Skip to content
Snippets Groups Projects
qtversionmanager.cpp 52.2 KiB
Newer Older
con's avatar
con committed
                            parts.append(line.mid(lastpos, i-lastpos));
                            if (debugScan)
                                qDebug()<<"part appended:"<<line.mid(lastpos, i-lastpos);
                            lastpos = i + 1; // Nex one starts after the space
                        }
                    }
                    parts.append(line.mid(lastpos));
                    if (debugScan)
                        qDebug()<<"part appended:"<<line.mid(lastpos);

                    foreach(const QString &part, parts) {
hjk's avatar
hjk committed
                        if (debugScan)
con's avatar
con committed
                            qDebug()<<"now interpreting part"<<part;
                        bool setFlags;
                        // Now try to understand each part for that we do a rather stupid approach, optimize it if you care
                        if (part.startsWith("CONFIG")) {
                            // Yep something interesting
                            if (part.indexOf("+=") != -1) {
                                setFlags = true;
                            } else if (part.indexOf("-=") != -1) {
                                setFlags = false;
                            } else {
                                setFlags = true;
                                if (debugScan)
                                    qDebug()<<"This can never happen, except if we can't parse Makefiles...";
                            }
                            if (debugScan)
                                qDebug()<<"part has setFlags:"<<setFlags;
                            // now loop forward, looking for something that looks like debug, release or debug_and_release

hjk's avatar
hjk committed
                            for (int i = 0; i < part.size(); ++i) {
con's avatar
con committed
                                int left = part.size() - i;
                                if (left >= 17  && QStringRef(&part, i, 17) == "debug_and_release") {
                                        if (setFlags)
                                            result = QtVersion::QmakeBuildConfig(result | QtVersion::BuildAll);
                                        else
                                            result = QtVersion::QmakeBuildConfig(result & ~QtVersion::BuildAll);
                                        if (debugScan)
                                            qDebug()<<"found debug_and_release new value"<<result;
                                        i += 17;
                                } else if (left >=7 && QStringRef(&part, i, 7) ==  "release") {
                                        if (setFlags)
                                            result = QtVersion::QmakeBuildConfig(result & ~QtVersion::DebugBuild);
                                        else
                                            result = QtVersion::QmakeBuildConfig(result | QtVersion::DebugBuild);
                                        if (debugScan)
                                            qDebug()<<"found release new value"<<result;
hjk's avatar
hjk committed
                                        i += 7;
con's avatar
con committed
                                } else if (left >= 5 && QStringRef(&part, i, 5) == "debug") {
                                        if (setFlags)
                                            result = QtVersion::QmakeBuildConfig(result  | QtVersion::DebugBuild);
                                        else
                                            result = QtVersion::QmakeBuildConfig(result  & ~QtVersion::DebugBuild);
                                        if (debugScan)
                                            qDebug()<<"found debug new value"<<result;
hjk's avatar
hjk committed
                                        i += 5;
con's avatar
con committed
                                }
                            }
                        }
                    }
                }
                if (debugScan)
                    qDebug()<<"returning: "<<result;
                if (debugScan)
                    qDebug()<<"buildall = "<<bool(result & QtVersion::BuildAll);
                if (debugScan)
                    qDebug()<<"debug ="<<bool(result & QtVersion::DebugBuild);
            }
        }
        makefile.close();
    }
    return result;
}

void QtVersion::updateVersionInfo() const
{
    if (m_versionInfoUpToDate)
        return;
    // extract data from qmake executable
    m_versionInfo.clear();
    m_notInstalled = false;
    QFileInfo qmake(qmakeCommand());
    if (qmake.exists()) {
        static const char * const variables[] = {
             "QT_INSTALL_DATA",
             "QT_INSTALL_LIBS",
             "QT_INSTALL_HEADERS",
             "QT_INSTALL_DEMOS",
             "QT_INSTALL_EXAMPLES",
             "QT_INSTALL_CONFIGURATION",
             "QT_INSTALL_TRANSLATIONS",
             "QT_INSTALL_PLUGINS",
             "QT_INSTALL_BINS",
             "QT_INSTALL_DOCS",
             "QT_INSTALL_PREFIX"
        };
        QStringList args;
        for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
            args << "-query" << variables[i];
con's avatar
con committed
        QProcess process;
        process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
        if (process.waitForFinished(2000)) {
            QByteArray output = process.readAllStandardOutput();
            QTextStream stream(&output);
            while (!stream.atEnd()) {
                QString line = stream.readLine();
                int index = line.indexOf(":");
                if (index != -1)
Joerg Bornemann's avatar
Joerg Bornemann committed
                    m_versionInfo.insert(line.left(index), QDir::fromNativeSeparators(line.mid(index+1)));
con's avatar
con committed
            }
        }

        if (m_versionInfo.contains("QT_INSTALL_DATA"))
            m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(m_versionInfo.value("QT_INSTALL_DATA")+"/mkspecs"));

        // Now check for a qt that is configured with a prefix but not installed
        if (m_versionInfo.contains("QT_INSTALL_BINS")) {
            QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS"));
            if (!fi.exists())
                m_notInstalled = true;
        }
        if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
            QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
            if (!fi.exists())
                m_notInstalled = true;
        }

        // Parse qconfigpri
        QString baseDir = m_versionInfo.contains("QT_INSTALL_DATA") ?
                           m_versionInfo.value("QT_INSTALL_DATA") :
                           m_path;
        QFile qconfigpri(baseDir + QLatin1String("/mkspecs/qconfig.pri"));
        if (qconfigpri.exists()) {
            qconfigpri.open(QIODevice::ReadOnly | QIODevice::Text);
            QTextStream stream(&qconfigpri);
            while (!stream.atEnd()) {
                QString line = stream.readLine().trimmed();
                if (line.startsWith(QLatin1String("CONFIG"))) {
                    m_defaultConfigIsDebugAndRelease = false;
                    QStringList values = line.split(QLatin1Char('=')).at(1).trimmed().split(" ");
                    foreach(const QString &value, values) {
                        if (value == "debug")
                            m_defaultConfigIsDebug = true;
hjk's avatar
hjk committed
                        else if (value == "release")
con's avatar
con committed
                            m_defaultConfigIsDebug = false;
hjk's avatar
hjk committed
                        else if (value == "build_all")
con's avatar
con committed
                            m_defaultConfigIsDebugAndRelease = true;
                    }
                }
            }
        }
    }
    m_versionInfoUpToDate = true;
}

bool QtVersion::isInstalled() const
{
    updateVersionInfo();
    return !m_notInstalled;
}

void QtVersion::updateMkSpec() const
{
    if (m_mkspecUpToDate)
        return;
    //qDebug()<<"Finding mkspec for"<<path();

    QString mkspec;
dt's avatar
dt committed
//    QFile f(path() + "/.qmake.cache");
//    if (f.exists() && f.open(QIODevice::ReadOnly)) {
hjk's avatar
hjk committed
//        while (!f.atEnd()) {
dt's avatar
dt committed
//            QByteArray line = f.readLine();
hjk's avatar
hjk committed
//            if (line.startsWith("QMAKESPEC")) {
dt's avatar
dt committed
//                const QList<QByteArray> &temp = line.split('=');
hjk's avatar
hjk committed
//                if (temp.size() == 2) {
dt's avatar
dt committed
//                    mkspec = temp.at(1).trimmed();
//                    if (mkspec.startsWith("$$QT_BUILD_TREE/mkspecs/"))
//                        mkspec = mkspec.mid(QString("$$QT_BUILD_TREE/mkspecs/").length());
//                    else if (mkspec.startsWith("$$QT_BUILD_TREE\\mkspecs\\"))
//                        mkspec = mkspec.mid(QString("$$QT_BUILD_TREE\\mkspecs\\").length());
//                    mkspec = QDir::fromNativeSeparators(mkspec);
dt's avatar
dt committed
//                }
//                break;
//            }
//        }
//        f.close();
//    } else {
con's avatar
con committed
        // no .qmake.cache so look at the default mkspec
        QString mkspecPath = versionInfo().value("QMAKE_MKSPECS");
        if (mkspecPath.isEmpty())
            mkspecPath = path() + "/mkspecs/default";
        else
            mkspecPath = mkspecPath + "/default";
//        qDebug() << "default mkspec is located at" << mkspecPath;
#ifdef Q_OS_WIN
        QFile f2(mkspecPath + "/qmake.conf");
        if (f2.exists() && f2.open(QIODevice::ReadOnly)) {
hjk's avatar
hjk committed
            while (!f2.atEnd()) {
con's avatar
con committed
                QByteArray line = f2.readLine();
hjk's avatar
hjk committed
                if (line.startsWith("QMAKESPEC_ORIGINAL")) {
con's avatar
con committed
                    const QList<QByteArray> &temp = line.split('=');
                    if (temp.size() == 2) {
lowinu's avatar
lowinu committed
                        mkspec = temp.at(1).trimmed();
con's avatar
con committed
                    }
                    break;
                }
            }
            f2.close();
        }
#elif defined(Q_OS_MAC)
        QFile f2(mkspecPath + "/qmake.conf");
        if (f2.exists() && f2.open(QIODevice::ReadOnly)) {
hjk's avatar
hjk committed
            while (!f2.atEnd()) {
con's avatar
con committed
                QByteArray line = f2.readLine();
hjk's avatar
hjk committed
                if (line.startsWith("MAKEFILE_GENERATOR")) {
con's avatar
con committed
                    const QList<QByteArray> &temp = line.split('=');
                    if (temp.size() == 2) {
                        const QByteArray &value = temp.at(1);
                        if (value.contains("XCODE")) {
                            // we don't want to generate xcode projects...
//                            qDebug() << "default mkspec is xcode, falling back to g++";
                            mkspec = "macx-g++";
                        } else {
                            //resolve mkspec link
                            QFileInfo f3(mkspecPath);
                            if (f3.isSymLink()) {
                                mkspec = f3.symLinkTarget();
                            }
                        }
                    }
                    break;
                }
            }
            f2.close();
        }
#else
        QFileInfo f2(mkspecPath);
        if (f2.isSymLink()) {
            mkspec = f2.symLinkTarget();
        }
#endif
dt's avatar
dt committed
//    }
con's avatar
con committed

dt's avatar
dt committed
    m_mkspecFullPath = mkspec;
    int index = mkspec.lastIndexOf('/');
hjk's avatar
hjk committed
    if (index == -1)
con's avatar
con committed
        index = mkspec.lastIndexOf('\\');
Joerg Bornemann's avatar
Joerg Bornemann committed
    QString mkspecDir = QDir(m_path + "/mkspecs/").canonicalPath();
    if (index >= 0 && QDir(mkspec.left(index)).canonicalPath() == mkspecDir)
con's avatar
con committed
        mkspec = mkspec.mid(index+1).trimmed();

    m_mkspec = mkspec;
    m_mkspecUpToDate = true;
//    qDebug()<<"mkspec for "<<m_path<<" is "<<mkspec;
}

QString QtVersion::qmakeCommand() const
{
    // We can't use versionInfo QT_INSTALL_BINS here
    // because that functions calls us to find out the values for versionInfo
    if (!m_qmakeCommand.isNull())
        return m_qmakeCommand;

    QDir qtDir = path() + "/bin/";
hjk's avatar
hjk committed
    foreach (const QString &possibleCommand, QtVersionManager::possibleQMakeCommands()) {
con's avatar
con committed
        QString s = qtDir.absoluteFilePath(possibleCommand);
        QFileInfo qmake(s);
        if (qmake.exists() && qmake.isExecutable()) {
            QString qtVersion = QtVersionManager::qtVersionForQMake(qmake.absoluteFilePath());
            if (!qtVersion.isNull()) {
                m_qtVersionString = qtVersion;
con's avatar
con committed
                m_qmakeCommand = qmake.absoluteFilePath();
                return qmake.absoluteFilePath();
            }
        }
    }
    return QString::null;
}

dt's avatar
dt committed
ProjectExplorer::ToolChain::ToolChainType QtVersion::toolchainType() const
con's avatar
con committed
{
    if (!isValid())
dt's avatar
dt committed
        return ProjectExplorer::ToolChain::INVALID;
con's avatar
con committed
    const QString &spec = mkspec();
//    qDebug()<<"spec="<<spec;
hjk's avatar
hjk committed
    if (spec.contains("win32-msvc") || spec.contains(QLatin1String("win32-icc")))
dt's avatar
dt committed
        return ProjectExplorer::ToolChain::MSVC;
    else if (spec.contains("win32-g++"))
dt's avatar
dt committed
        return ProjectExplorer::ToolChain::MinGW;
hjk's avatar
hjk committed
    else if (spec == QString::null)
dt's avatar
dt committed
        return ProjectExplorer::ToolChain::INVALID;
    else if (spec.contains("wince"))
dt's avatar
dt committed
        return ProjectExplorer::ToolChain::WINCE;
    else if (spec.contains("linux-icc"))
        return ProjectExplorer::ToolChain::LinuxICC;
con's avatar
con committed
    else
dt's avatar
dt committed
        return ProjectExplorer::ToolChain::GCC;
con's avatar
con committed
}

QString QtVersion::mingwDirectory() const
{
    return m_mingwDirectory;
}

void QtVersion::setMingwDirectory(const QString &directory)
{
    m_mingwDirectory = directory;
}

QString QtVersion::prependPath() const
{
    return m_prependPath;
}

void QtVersion::setPrependPath(const QString &directory)
{
    m_prependPath = directory;
}

QString QtVersion::msvcVersion() const
{
    return m_msvcVersion;
}

dt's avatar
dt committed
QString QtVersion::wincePlatform() const
{
//    qDebug()<<"QtVersion::wincePlatform returning"<<ProjectExplorer::CeSdkHandler::platformName(mkspecPath() + "/qmake.conf");
dt's avatar
dt committed
    return ProjectExplorer::CeSdkHandler::platformName(mkspecPath() + "/qmake.conf");
}

con's avatar
con committed
void QtVersion::setMsvcVersion(const QString &version)
{
    m_msvcVersion = version;
}

dt's avatar
dt committed
void QtVersion::addToEnvironment(Environment &env)
con's avatar
con committed
{
dt's avatar
dt committed
    env.set("QTDIR", m_path);
con's avatar
con committed
    QString qtdirbin = versionInfo().value("QT_INSTALL_BINS");
dt's avatar
dt committed
    env.prependOrSetPath(qtdirbin);
con's avatar
con committed
    // add libdir, includedir and bindir
    // or add Mingw dirs
    // or do nothing on other
}

int QtVersion::uniqueId() const
{
    return m_id;
}

int QtVersion::getUniqueId()
{
    QtVersionManager *vm = ExtensionSystem::PluginManager::instance()->getObject<QtVersionManager>();
    return vm->getUniqueId();
}

bool QtVersion::isValid() const
{
    return (!(m_id == -1 || m_path == QString::null || m_name == QString::null || mkspec() == QString::null) && !m_notInstalled);
}

QtVersion::QmakeBuildConfig QtVersion::defaultBuildConfig() const
{
    updateVersionInfo();
    QtVersion::QmakeBuildConfig result = QtVersion::QmakeBuildConfig(0);
    if (m_defaultConfigIsDebugAndRelease)
        result = QtVersion::BuildAll;
    if (m_defaultConfigIsDebug)
        result = QtVersion::QmakeBuildConfig(result | QtVersion::DebugBuild);
    return result;
}

bool QtVersion::hasDebuggingHelper() const
{
    return m_hasDebuggingHelper;
}


// TODO buildDebuggingHelperLibrary needs to be accessible outside of the
// qt4versionmanager
// That probably means moving qt4version management into either the projectexplorer
// (The Projectexplorer plugin probably needs some splitting up, most of the stuff
// could be in a plugin shared by qt4projectmanager, cmakemanager and debugger.)
QString QtVersion::buildDebuggingHelperLibrary()
{
// Locations to try:
//    $QTDIR/qtc-debugging-helper
//    $APPLICATION-DIR/qtc-debugging-helper/$hash
//    $USERDIR/qtc-debugging-helper/$hash

    QString output;
    uint hash = qHash(path());
    QStringList directories;
    directories
            << path() + "/qtc-debugging-helper/"
            << QApplication::applicationDirPath() + "/../qtc-debugging-helper/" + QString::number(hash) +"/"
            << QDesktopServices::storageLocation (QDesktopServices::DataLocation) + "/qtc-debugging-helper/" + QString::number(hash) +"/";

    QStringList files;
    files << "gdbmacros.cpp" << "gdbmacros.pro"
          << "LICENSE.LGPL" << "LGPL_EXCEPTION.TXT";

    foreach(const QString &directory, directories) {
        QString dumperPath = Core::ICore::instance()->resourcePath() + "/gdbmacros/";
        bool success = true;
        QDir().mkpath(directory);
        foreach (const QString &file, files) {
            QString source = dumperPath + file;
            QString dest = directory + file;
            QFileInfo destInfo(dest);
            if (destInfo.exists()) {
                if (destInfo.lastModified() >= QFileInfo(source).lastModified())
                    continue;
                success &= QFile::remove(dest);
            }
            success &= QFile::copy(source, dest);
        }
        if (!success)
            continue;

        output += QString("Building debugging helper library in %1\n").arg(directory);
        output += "\n";
        output += QString("Runinng %1 ...\n").arg(qmakeCommand());

        QProcess qmake;
        ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
        addToEnvironment(env);

        // TODO this is a hack to get, to be removed and rewritten for 1.2
        //
        // For MSVC and MINGW, we need a toolchain to get the right environment        
        ProjectExplorer::ToolChain *toolChain = 0;
        ProjectExplorer::ToolChain::ToolChainType t = toolchainType();
        if (t == ProjectExplorer::ToolChain::MinGW)
            toolChain = ProjectExplorer::ToolChain::createMinGWToolChain("g++", mingwDirectory());
        else if(t == ProjectExplorer::ToolChain::MSVC)
            toolChain = ProjectExplorer::ToolChain::createMSVCToolChain(msvcVersion());
        if (toolChain) {
            toolChain->addToEnvironment(env);
            delete toolChain;
            toolChain = 0;
        }

        qmake.setEnvironment(env.toStringList());
        qmake.setWorkingDirectory(directory);
        qmake.setProcessChannelMode(QProcess::MergedChannels);

con's avatar
con committed
        qmake.start(qmakeCommand(), QStringList()<<"-spec"<< mkspec() <<"gdbmacros.pro");
        qmake.waitForFinished();

        output += qmake.readAll();

        // TODO this is butt ugly
        // only qt4projects have a toolchain() method. (Reason mostly, that in order to create
        // the toolchain, we need to have the path to gcc
        // which might depend on environment settings of the project
        // so we hardcode the toolchainType to make conversation here
        // and think about how to fix that later

        QString make;
        if (t == ProjectExplorer::ToolChain::MinGW)
            make = "mingw32-make.exe";
        else if(t == ProjectExplorer::ToolChain::MSVC || t == ProjectExplorer::ToolChain::WINCE)
            make = "nmake.exe";
        else if (t == ProjectExplorer::ToolChain::GCC || t == ProjectExplorer::ToolChain::LinuxICC)
            make = "make";

        QString makeFullPath = env.searchInPath(make);
        output += "\n";
        if (!makeFullPath.isEmpty()) {
            output += QString("Running %1 ...\n").arg(makeFullPath);
            qmake.start(makeFullPath, QStringList());
            qmake.waitForFinished();
            output += qmake.readAll();
        } else {
            output += QString("%1 not found in PATH\n").arg(make);
        }
        break;
    }
    m_hasDebuggingHelper = !dumperLibrary().isEmpty();
    return output;
}