From 90b094e832125b4ad441f982473c2ff26ac5cac8 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 19 Feb 2009 17:47:44 +0100 Subject: [PATCH] Make the git plugin look for the binary in the path specified in the settings. Previously, the environment was passed to the process, but that did not affect the location of the binary. --- src/libs/utils/synchronousprocess.cpp | 105 ++++++++++++++++++++++++++ src/libs/utils/synchronousprocess.h | 6 ++ src/plugins/git/gitclient.cpp | 13 ++-- src/plugins/git/gitclient.h | 1 + src/plugins/git/gitcommand.cpp | 6 +- src/plugins/git/gitcommand.h | 7 +- src/plugins/git/gitsettings.cpp | 29 +++++++ src/plugins/git/gitsettings.h | 2 + src/plugins/git/settingspage.cpp | 17 ++++- 9 files changed, 173 insertions(+), 13 deletions(-) diff --git a/src/libs/utils/synchronousprocess.cpp b/src/libs/utils/synchronousprocess.cpp index 667416a3406..597be2932be 100644 --- a/src/libs/utils/synchronousprocess.cpp +++ b/src/libs/utils/synchronousprocess.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include @@ -366,5 +368,108 @@ void SynchronousProcess::processStdErr(bool emitSignals) } } +// Path utilities + +enum OS_Type { OS_Mac, OS_Windows, OS_Unix }; + +#ifdef Q_OS_WIN +static const OS_Type pathOS = OS_Windows; +#else +# ifdef Q_OS_MAC +static const OS_Type pathOS = OS_Mac; +# else +static const OS_Type pathOS = OS_Unix; +# endif +#endif + +// Locate a binary in a directory, applying all kinds of +// extensions the operating system supports. +static QString checkBinary(const QDir &dir, const QString &binary) +{ + // naive UNIX approach + const QFileInfo info(dir.filePath(binary)); + if (info.isFile() && info.isExecutable()) + return info.absoluteFilePath(); + + // Does the OS have some weird extension concept or does the + // binary have a 3 letter extension? + if (pathOS == OS_Unix) + return QString(); + const int dotIndex = binary.lastIndexOf(QLatin1Char('.')); + if (dotIndex != -1 && dotIndex == binary.size() - 4) + return QString(); + + switch (pathOS) { + case OS_Unix: + break; + case OS_Windows: { + static const char *windowsExtensions[] = {".cmd", ".bat", ".exe", ".com" }; + // Check the Windows extensions using the order + const int windowsExtensionCount = sizeof(windowsExtensions)/sizeof(const char*); + for (int e = 0; e < windowsExtensionCount; e ++) { + const QFileInfo windowsBinary(dir.filePath(binary + QLatin1String(windowsExtensions[e]))); + if (windowsBinary.isFile() && windowsBinary.isExecutable()) + return windowsBinary.absoluteFilePath(); + } + } + break; + case OS_Mac: { + // Check for Mac app folders + const QFileInfo appFolder(dir.filePath(binary + QLatin1String(".app"))); + if (appFolder.isDir()) { + QString macBinaryPath = appFolder.absoluteFilePath(); + macBinaryPath += QLatin1String("/Contents/MacOS/"); + macBinaryPath += binary; + const QFileInfo macBinary(macBinaryPath); + if (macBinary.isFile() && macBinary.isExecutable()) + return macBinary.absoluteFilePath(); + } + } + break; + } + return QString(); +} + +QString SynchronousProcess::locateBinary(const QString &path, const QString &binary) +{ + // Absolute file? + const QFileInfo absInfo(binary); + if (absInfo.isAbsolute()) + return checkBinary(absInfo.dir(), absInfo.fileName()); + + // Windows finds binaries in the current directory + if (pathOS == OS_Windows) { + const QString currentDirBinary = checkBinary(QDir::current(), binary); + if (!currentDirBinary.isEmpty()) + return currentDirBinary; + } + + const QStringList paths = path.split(pathSeparator()); + if (paths.empty()) + return QString(); + const QStringList::const_iterator cend = paths.constEnd(); + for (QStringList::const_iterator it = paths.constBegin(); it != cend; ++it) { + const QDir dir(*it); + const QString rc = checkBinary(dir, binary); + if (!rc.isEmpty()) + return rc; + } + return QString(); +} + +QString SynchronousProcess::locateBinary(const QString &binary) +{ + const QByteArray path = qgetenv("PATH"); + return locateBinary(QString::fromLocal8Bit(path), binary); +} + +QChar SynchronousProcess::pathSeparator() +{ + if (pathOS == OS_Windows) + return QLatin1Char(';'); + return QLatin1Char(':'); +} + + } // namespace Utils } // namespace Core diff --git a/src/libs/utils/synchronousprocess.h b/src/libs/utils/synchronousprocess.h index e9218f43d80..80a04a1303e 100644 --- a/src/libs/utils/synchronousprocess.h +++ b/src/libs/utils/synchronousprocess.h @@ -114,6 +114,12 @@ public: SynchronousProcessResponse run(const QString &binary, const QStringList &args); + // Helpers to find binaries. Do not use it for other path variables + // and file types. + static QString locateBinary(const QString &binary); + static QString locateBinary(const QString &path, const QString &binary); + static QChar pathSeparator(); + signals: void stdOut(const QByteArray &data, bool firstTime); void stdErr(const QByteArray &data, bool firstTime); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 87f06931200..a3ad3ba84ff 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -102,8 +102,10 @@ GitClient::GitClient(GitPlugin* plugin) m_plugin(plugin), m_core(Core::ICore::instance()) { - if (QSettings *s = m_core->settings()) + if (QSettings *s = m_core->settings()) { m_settings.fromSettings(s); + m_binaryPath = m_settings.gitBinaryPath(); + } } GitClient::~GitClient() @@ -478,7 +480,7 @@ GitCommand *GitClient::createCommand(const QString &workingDirectory, if (m_settings.adoptPath) environment.set(QLatin1String("PATH"), m_settings.path); - GitCommand* command = new GitCommand(workingDirectory, environment); + GitCommand* command = new GitCommand(m_binaryPath, workingDirectory, environment); if (outputToWindow) { if (!editor) { // assume that the commands output is the important thing connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(appendDataAndPopup(QByteArray))); @@ -527,10 +529,9 @@ bool GitClient::synchronousGit(const QString &workingDirectory, { if (Git::Constants::debug) qDebug() << "synchronousGit" << workingDirectory << arguments; - const QString binary = QLatin1String(Constants::GIT_BINARY); if (logCommandToWindow) - m_plugin->outputWindow()->append(formatCommand(binary, arguments)); + m_plugin->outputWindow()->append(formatCommand(m_binaryPath, arguments)); QProcess process; process.setWorkingDirectory(workingDirectory); @@ -540,7 +541,7 @@ bool GitClient::synchronousGit(const QString &workingDirectory, environment.set(QLatin1String("PATH"), m_settings.path); process.setEnvironment(environment.toStringList()); - process.start(binary, arguments); + process.start(m_binaryPath, arguments); if (!process.waitForFinished()) { if (errorText) *errorText = "Error: Git timed out"; @@ -1000,6 +1001,6 @@ void GitClient::setSettings(const GitSettings &s) m_settings = s; if (QSettings *s = m_core->settings()) m_settings.toSettings(s); + m_binaryPath = m_settings.gitBinaryPath(); } } - diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index c920ffc1b32..edd7e05c9e3 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -176,6 +176,7 @@ private: GitPlugin *m_plugin; Core::ICore *m_core; GitSettings m_settings; + QString m_binaryPath; }; diff --git a/src/plugins/git/gitcommand.cpp b/src/plugins/git/gitcommand.cpp index 8362926cecc..54bfcea2ab4 100644 --- a/src/plugins/git/gitcommand.cpp +++ b/src/plugins/git/gitcommand.cpp @@ -61,8 +61,10 @@ GitCommand::Job::Job(const QStringList &a, int t) : { } -GitCommand::GitCommand(const QString &workingDirectory, +GitCommand::GitCommand(const QString &binaryPath, + const QString &workingDirectory, ProjectExplorer::Environment &environment) : + m_binaryPath(binaryPath), m_workingDirectory(workingDirectory), m_environment(environmentToList(environment)) { @@ -109,7 +111,7 @@ void GitCommand::run() if (Git::Constants::debug) qDebug() << "GitCommand::run" << j << '/' << count << m_jobs.at(j).arguments; - process.start(QLatin1String(Constants::GIT_BINARY), m_jobs.at(j).arguments); + process.start(m_binaryPath, m_jobs.at(j).arguments); if (!process.waitForFinished(m_jobs.at(j).timeout * 1000)) { ok = false; error += QLatin1String("Error: Git timed out"); diff --git a/src/plugins/git/gitcommand.h b/src/plugins/git/gitcommand.h index 32b76bf3485..1d661706151 100644 --- a/src/plugins/git/gitcommand.h +++ b/src/plugins/git/gitcommand.h @@ -43,9 +43,11 @@ namespace Internal { class GitCommand : public QObject { + Q_DISABLE_COPY(GitCommand) Q_OBJECT public: - explicit GitCommand(const QString &workingDirectory, + explicit GitCommand(const QString &binaryPath, + const QString &workingDirectory, ProjectExplorer::Environment &environment); @@ -67,8 +69,7 @@ private: int timeout; }; - QStringList environment() const; - + const QString m_binaryPath; const QString m_workingDirectory; const QStringList m_environment; diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp index 02a1acc1d9e..c379bb3fba5 100644 --- a/src/plugins/git/gitsettings.cpp +++ b/src/plugins/git/gitsettings.cpp @@ -32,9 +32,13 @@ ***************************************************************************/ #include "gitsettings.h" +#include "gitconstants.h" + +#include #include #include +#include static const char *groupC = "Git"; static const char *sysEnvKeyC = "SysEnv"; @@ -79,5 +83,30 @@ bool GitSettings::equals(const GitSettings &s) const return adoptPath == s.adoptPath && path == s.path && logCount == s.logCount && timeout == s.timeout; } +QString GitSettings::gitBinaryPath(bool *ok, QString *errorMessage) const +{ + // Locate binary in path if one is specified, otherwise default + // to pathless binary + if (ok) + *ok = true; + if (errorMessage) + errorMessage->clear(); + const QString binary = QLatin1String(Constants::GIT_BINARY); + // Easy, git is assumed to be elsewhere accessible + if (!adoptPath) + return binary; + // Search in path? + const QString pathBinary = Core::Utils::SynchronousProcess::locateBinary(path, binary); + if (pathBinary.isEmpty()) { + if (ok) + *ok = false; + if (errorMessage) + *errorMessage = QCoreApplication::translate("GitSettings", + "The binary '%1' could not be located in the path '%2'").arg(binary, path); + return binary; + } + return pathBinary; +} + } } diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h index c2463eb326d..05dc250c578 100644 --- a/src/plugins/git/gitsettings.h +++ b/src/plugins/git/gitsettings.h @@ -51,6 +51,8 @@ struct GitSettings void fromSettings(QSettings *); void toSettings(QSettings *) const; + QString gitBinaryPath(bool *ok = 0, QString *errorMessage = 0) const; + bool equals(const GitSettings &s) const; bool adoptPath; diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp index 121c7cd889b..4fdd672eac4 100644 --- a/src/plugins/git/settingspage.cpp +++ b/src/plugins/git/settingspage.cpp @@ -36,8 +36,10 @@ #include "gitplugin.h" #include +#include -using namespace Git::Internal; +namespace Git { +namespace Internal { SettingsPageWidget::SettingsPageWidget(QWidget *parent) : QWidget(parent) @@ -101,6 +103,17 @@ void SettingsPage::apply() { if (!m_widget) return; + const GitSettings newSettings = m_widget->settings(); + // Warn if git cannot be found in path if the widget is on top + if (m_widget->isVisible()) { + bool gitFoundOk; + QString errorMessage; + newSettings.gitBinaryPath(&gitFoundOk, &errorMessage); + if (!gitFoundOk) + QMessageBox::warning(m_widget, tr("Git Settings"), errorMessage); + } - GitPlugin::instance()->setSettings(m_widget->settings()); + GitPlugin::instance()->setSettings(newSettings); +} +} }