src: switch from Qt to boost implementation of executeProcess (#1145)

* changed implementation of executeProcess
* unified all different implementations of executeProcess
* separated arg list from command for all calls of executeProcess (this also required to remove quotes that were added to path args because otherwise the Python indexer would mistake absolute paths for relative paths.)
* update expected output for custom command tests
This commit is contained in:
Malte Langkabel
2021-02-22 16:00:32 +01:00
committed by GitHub
parent 9a13c194df
commit 3c1638da25
28 changed files with 430 additions and 417 deletions
@@ -1,6 +1,12 @@
SourceFilePath: "src/a.txt"
IndexerCommandCustom
SourceFilePath: "src/a.txt"
Custom Command: "echo "Hello World""
SourceFilePath: "src/b.txt"
Arguments:
IndexerCommandCustom
SourceFilePath: "src/b.txt"
Custom Command: "echo "Hello World""
SourceFilePath: "src/included/a.txt"
Arguments:
IndexerCommandCustom
SourceFilePath: "src/included/a.txt"
Custom Command: "echo "Hello World""
Arguments:
@@ -1,6 +1,12 @@
SourceFilePath: "src/a.txt"
IndexerCommandCustom
SourceFilePath: "src/a.txt"
Custom Command: "echo "Hello World""
SourceFilePath: "src/b.txt"
Arguments:
IndexerCommandCustom
SourceFilePath: "src/b.txt"
Custom Command: "echo "Hello World""
SourceFilePath: "src/included/a.txt"
Arguments:
IndexerCommandCustom
SourceFilePath: "src/included/a.txt"
Custom Command: "echo "Hello World""
Arguments:
+1
View File
@@ -94,6 +94,7 @@ fi
echo -e $INFO "clearing $TARGET_PATH"
rm -rf $TARGET_PATH
mkdir -p $TARGET_PATH
echo -e $INFO "copying downloaded data to $TARGET_PATH"
cp -r $TEMP_PATH/$PACKAGE_NAME/* $TARGET_PATH
+5
View File
@@ -93,6 +93,11 @@ void addLanguagePackages()
int main(int argc, char* argv[])
{
// auto p = utility::executeProcessBoost(utility::searchPath(L"mvn") + L" --version", FilePath("/Users/ebsi/Documents/boost_1_67_0"), 3000);
// std::wcout << p.first << " " << p.second << std::endl;
// return 0;
QCoreApplication::addLibraryPath(QStringLiteral("."));
#pragma warning(push)
+11 -1
View File
@@ -1,6 +1,7 @@
#include "ResourcePaths.h"
#include "AppPath.h"
#include "utilityApp.h"
FilePath ResourcePaths::getColorSchemesPath()
{
@@ -37,7 +38,7 @@ FilePath ResourcePaths::getJavaPath()
return AppPath::getSharedDataPath().concatenate(L"data/java/");
}
FilePath ResourcePaths::getPythonPath()
FilePath ResourcePaths::getPythonDirectoryPath()
{
return AppPath::getSharedDataPath().concatenate(L"data/python/");
}
@@ -46,3 +47,12 @@ FilePath ResourcePaths::getCxxCompilerHeaderPath()
{
return AppPath::getSharedDataPath().concatenate(L"data/cxx/include/").getCanonical();
}
FilePath ResourcePaths::getPythonIndexerFilePath()
{
if (utility::getOsType() == OS_WINDOWS)
{
return getPythonDirectoryPath().concatenate(L"SourcetrailPythonIndexer.exe");
}
return getPythonDirectoryPath().concatenate(L"SourcetrailPythonIndexer");
}
+2 -1
View File
@@ -15,8 +15,9 @@ public:
static FilePath getGuiPath();
static FilePath getLicensePath();
static FilePath getJavaPath();
static FilePath getPythonPath();
static FilePath getPythonDirectoryPath();
static FilePath getCxxCompilerHeaderPath();
static FilePath getPythonIndexerFilePath();
};
#endif // RESOURCE_PATHS_H
+37 -16
View File
@@ -11,7 +11,8 @@ IndexerCommandType IndexerCommandCustom::getStaticIndexerCommandType()
}
IndexerCommandCustom::IndexerCommandCustom(
const std::wstring& customCommand,
const std::wstring& command,
const std::vector<std::wstring>& arguments,
const FilePath& projectFilePath,
const FilePath& databaseFilePath,
const std::wstring& databaseVersion,
@@ -19,7 +20,8 @@ IndexerCommandCustom::IndexerCommandCustom(
bool runInParallel)
: IndexerCommand(sourceFilePath)
, m_type(getStaticIndexerCommandType())
, m_customCommand(customCommand)
, m_command(command)
, m_arguments(arguments)
, m_projectFilePath(projectFilePath)
, m_databaseFilePath(databaseFilePath)
, m_databaseVersion(databaseVersion)
@@ -29,7 +31,8 @@ IndexerCommandCustom::IndexerCommandCustom(
IndexerCommandCustom::IndexerCommandCustom(
IndexerCommandType type,
const std::wstring& customCommand,
const std::wstring& command,
const std::vector<std::wstring>& arguments,
const FilePath& projectFilePath,
const FilePath& databaseFilePath,
const std::wstring& databaseVersion,
@@ -37,7 +40,8 @@ IndexerCommandCustom::IndexerCommandCustom(
bool runInParallel)
: IndexerCommand(sourceFilePath)
, m_type(type)
, m_customCommand(customCommand)
, m_command(command)
, m_arguments(arguments)
, m_projectFilePath(projectFilePath)
, m_databaseFilePath(databaseFilePath)
, m_databaseVersion(databaseVersion)
@@ -67,19 +71,19 @@ void IndexerCommandCustom::setDatabaseFilePath(const FilePath& databaseFilePath)
m_databaseFilePath = databaseFilePath;
}
std::wstring IndexerCommandCustom::getCustomCommand() const
std::wstring IndexerCommandCustom::getCommand() const
{
std::wstring command = m_customCommand;
return replaceVariables(m_command);
}
command = utility::replace(
command, L"%{PROJECT_FILE_PATH}", L'\"' + m_projectFilePath.wstr() + L'\"');
command = utility::replace(
command, L"%{DATABASE_FILE_PATH}", L'\"' + m_databaseFilePath.wstr() + L'\"');
command = utility::replace(command, L"%{DATABASE_VERSION}", L'\"' + m_databaseVersion + L'\"');
command = utility::replace(
command, L"%{SOURCE_FILE_PATH}", L'\"' + getSourceFilePath().wstr() + L'\"');
return command;
std::vector<std::wstring> IndexerCommandCustom::getArguments() const
{
std::vector<std::wstring> args;
for (const std::wstring& argument: m_arguments)
{
args.push_back(replaceVariables(argument));
}
return args;
}
bool IndexerCommandCustom::getRunInParallel() const
@@ -92,7 +96,15 @@ QJsonObject IndexerCommandCustom::doSerialize() const
QJsonObject jsonObject = IndexerCommand::doSerialize();
{
jsonObject["custom_command"] = QString::fromStdWString(m_customCommand);
jsonObject["command"] = QString::fromStdWString(m_command);
}
{
QJsonArray argumentsArray;
for (const std::wstring& argument: m_arguments)
{
argumentsArray.append(QString::fromStdWString(argument));
}
jsonObject["arguments"] = argumentsArray;
}
{
jsonObject["run_in_parallel"] = m_runInParallel;
@@ -100,3 +112,12 @@ QJsonObject IndexerCommandCustom::doSerialize() const
return jsonObject;
}
std::wstring IndexerCommandCustom::replaceVariables(std::wstring s) const
{
s = utility::replace(s, L"%{PROJECT_FILE_PATH}", m_projectFilePath.wstr());
s = utility::replace(s, L"%{DATABASE_FILE_PATH}", m_databaseFilePath.wstr());
s = utility::replace(s, L"%{DATABASE_VERSION}", m_databaseVersion);
s = utility::replace(s, L"%{SOURCE_FILE_PATH}", getSourceFilePath().wstr());
return s;
}
+10 -4
View File
@@ -12,7 +12,8 @@ public:
static IndexerCommandType getStaticIndexerCommandType();
IndexerCommandCustom(
const std::wstring& customCommand,
const std::wstring& command,
const std::vector<std::wstring>& arguments,
const FilePath& projectFilePath,
const FilePath& databaseFilePath,
const std::wstring& databaseVersion,
@@ -21,7 +22,8 @@ public:
IndexerCommandCustom(
IndexerCommandType type,
const std::wstring& customCommand,
const std::wstring& command,
const std::vector<std::wstring>& arguments,
const FilePath& projectFilePath,
const FilePath& databaseFilePath,
const std::wstring& databaseVersion,
@@ -34,15 +36,19 @@ public:
FilePath getDatabaseFilePath() const;
void setDatabaseFilePath(const FilePath& databaseFilePath);
std::wstring getCustomCommand() const;
std::wstring getCommand() const;
std::vector<std::wstring> getArguments() const;
bool getRunInParallel() const;
protected:
QJsonObject doSerialize() const override;
private:
std::wstring replaceVariables(std::wstring s) const;
IndexerCommandType m_type;
std::wstring m_customCommand;
std::wstring m_command;
std::vector<std::wstring> m_arguments;
FilePath m_projectFilePath;
FilePath m_databaseFilePath;
std::wstring m_databaseVersion;
+6 -5
View File
@@ -188,22 +188,23 @@ void TaskBuildIndex::runIndexerProcess(int processId, const std::wstring& logFil
return;
}
const std::wstring commandPath = L"\"" + indexerProcessPath.wstr() + L"\"";
std::vector<std::wstring> commandArguments;
commandArguments.push_back(std::to_wstring(processId));
commandArguments.push_back(utility::decodeFromUtf8(m_appUUID));
commandArguments.push_back(L"\"" + AppPath::getSharedDataPath().getAbsolute().wstr() + L"\"");
commandArguments.push_back(L"\"" + UserPaths::getUserDataPath().getAbsolute().wstr() + L"\"");
commandArguments.push_back(AppPath::getSharedDataPath().getAbsolute().wstr());
commandArguments.push_back(UserPaths::getUserDataPath().getAbsolute().wstr());
if (!logFilePath.empty())
{
commandArguments.push_back(L"\"" + logFilePath + L"\"");
commandArguments.push_back(logFilePath);
}
int result = 1;
while ((!m_indexerCommandQueueStopped || result != 0) && !m_interrupted)
{
result = utility::executeProcessAndGetExitCode(commandPath, commandArguments, FilePath(), -1);
result = utility::executeProcess(
indexerProcessPath.wstr(), commandArguments, FilePath(), false, -1)
.exitCode;
LOG_INFO_STREAM(<< "Indexer process " << processId << " returned with " + std::to_string(result));
}
@@ -465,17 +465,19 @@ void TaskExecuteCustomCommands::runIndexerCommand(
indexedSourceFileCount + 1, indexedSourceFileCount, m_indexerCommandCount, {sourcePath});
MessageIndexingStatus(true, indexedSourceFileCount * 100 / m_indexerCommandCount).dispatch();
const std::wstring command = indexerCommand->getCustomCommand();
const std::wstring command = indexerCommand->getCommand();
const std::vector<std::wstring> arguments = indexerCommand->getArguments();
LOG_INFO("Start processing command \"" + utility::encodeToUtf8(command) + "\"");
LOG_INFO(
"Start processing command \"" +
utility::encodeToUtf8(command + L" " + utility::join(arguments, L" ")) + "\"");
const ErrorCountInfo previousErrorCount = storage ? storage->getErrorCount()
: ErrorCountInfo();
LOG_INFO("Starting to index");
std::wstring errorMessage;
const int result = utility::executeProcessAndGetExitCode(
command, {}, m_projectDirectory, -1, true, &errorMessage);
const utility::ProcessOutput out = utility::executeProcess(
command, arguments, m_projectDirectory, false, -1, true);
LOG_INFO("Finished indexing");
if (storage)
@@ -501,22 +503,22 @@ void TaskExecuteCustomCommands::runIndexerCommand(
}
}
if (result == 0 && errorMessage.empty())
if (out.exitCode == 0 && out.error.empty())
{
std::wstring message = L"Process returned successfully.\n";
LOG_INFO(message);
}
else
{
std::wstring statusText = L"command \"" + indexerCommand->getCustomCommand() +
L"\" returned";
if (result != 0)
std::wstring statusText = L"command \"" + indexerCommand->getCommand() + L" " +
utility::join(arguments, L" ") + L"\" returned";
if (out.exitCode != 0)
{
statusText += L" code \"" + std::to_wstring(result) + L"\"";
statusText += L" code \"" + std::to_wstring(out.exitCode) + L"\"";
}
if (!errorMessage.empty())
if (!out.error.empty())
{
statusText += L" with message \"" + errorMessage + L"\"";
statusText += L" with message \"" + out.error + L"\"";
}
statusText += L".";
+2 -2
View File
@@ -42,7 +42,6 @@ std::set<FilePath> SourceGroupCustomCommand::getAllSourceFilePaths() const
std::vector<std::shared_ptr<IndexerCommand>> SourceGroupCustomCommand::getIndexerCommands(
const RefreshInfo& info) const
{
const std::wstring customCommand = m_settings->getCustomCommand();
const bool runInParallel = m_settings->getRunInParallel();
std::vector<std::shared_ptr<IndexerCommand>> indexerCommands;
@@ -51,7 +50,8 @@ std::vector<std::shared_ptr<IndexerCommand>> SourceGroupCustomCommand::getIndexe
if (info.filesToIndex.find(sourcePath) != info.filesToIndex.end())
{
indexerCommands.push_back(std::make_shared<IndexerCommandCustom>(
customCommand,
m_settings->getCustomCommand(),
std::vector<std::wstring> {},
m_settings->getProjectSettings()->getProjectFilePath(),
m_settings->getProjectSettings()->getTempDBFilePath(),
std::to_wstring(SqliteIndexStorage::getStorageVersion()),
@@ -24,20 +24,19 @@ void QtProjectWizardContentSelect::populate(QGridLayout* layout, int& row)
{
std::string pythonIndexerVersion = " ";
{
std::string str =
utility::executeProcess(
ResourcePaths::getPythonPath().wstr().append(L"SourcetrailPythonIndexer"),
std::vector<std::wstring> {L"--version"},
FilePath(),
5000)
.second;
std::regex regex(
"v\\d*\\.db\\d*\\.p\\d*"); // "\\d" matches any digit; "\\." matches the "." character
std::smatch matches;
std::regex_search(str, matches, regex);
if (!matches.empty())
utility::ProcessOutput output = utility::executeProcess(
ResourcePaths::getPythonIndexerFilePath().wstr(), {L"--version"}, FilePath(), false, 5000);
if (output.exitCode == 0)
{
pythonIndexerVersion = matches.str(0) + " ";
std::string str = utility::encodeToUtf8(output.output);
std::regex regex("v\\d*\\.db\\d*\\.p\\d*"); // "\\d" matches any digit; "\\." matches
// the "." character
std::smatch matches;
std::regex_search(str, matches, regex);
if (!matches.empty())
{
pythonIndexerVersion = matches.str(0) + " ";
}
}
}
@@ -62,20 +62,20 @@ void QtProjectWizardContentPathPythonEnvironment::onTextChanged(const QString& t
{
m_resultLabel->setText("Checking validity of Python environment...");
std::thread([=]() {
std::pair<int, std::string> out = utility::executeProcess(
ResourcePaths::getPythonPath().wstr().append(L"SourcetrailPythonIndexer"),
std::vector<std::wstring>{
L"check-environment",
L"--environment-path " + utility::getExpandedAndAbsolutePath(
FilePath(text.toStdWString()), m_settings->getProjectDirectoryPath())
.wstr()
},
const utility::ProcessOutput out = utility::executeProcess(
ResourcePaths::getPythonIndexerFilePath().wstr(),
{L"check-environment",
L"--environment-path",
utility::getExpandedAndAbsolutePath(
FilePath(text.toStdWString()), m_settings->getProjectDirectoryPath())
.wstr()},
FilePath(),
false,
5000);
m_onQtThread([=]() {
if (out.first == 0)
if (out.exitCode == 0)
{
m_resultLabel->setText(QString::fromStdString(out.second));
m_resultLabel->setText(QString::fromStdWString(out.output));
}
else
{
@@ -11,14 +11,14 @@ CxxFrameworkPathDetector::CxxFrameworkPathDetector(const std::string& compilerNa
std::vector<FilePath> CxxFrameworkPathDetector::doGetPaths() const
{
std::vector<std::string> paths = utility::getCxxHeaderPaths(m_compilerName);
std::vector<std::wstring> paths = utility::getCxxHeaderPaths(m_compilerName);
std::vector<FilePath> frameworkPaths;
for (const std::string& path: paths)
for (const std::wstring& path: paths)
{
if (utility::isPostfix<std::string>(" (framework directory)", path))
if (utility::isPostfix<std::wstring>(L" (framework directory)", path))
{
FilePath p =
FilePath(utility::replace(path, " (framework directory)", "")).makeCanonical();
FilePath(utility::replace(path, L" (framework directory)", L"")).makeCanonical();
if (p.exists())
{
frameworkPaths.push_back(p);
@@ -11,12 +11,12 @@ CxxHeaderPathDetector::CxxHeaderPathDetector(const std::string& compilerName)
std::vector<FilePath> CxxHeaderPathDetector::doGetPaths() const
{
std::vector<std::string> paths = utility::getCxxHeaderPaths(m_compilerName);
std::vector<std::wstring> paths = utility::getCxxHeaderPaths(m_compilerName);
std::vector<FilePath> headerSearchPaths;
for (const std::string& path: paths)
for (const std::wstring& path: paths)
{
if (!utility::isPostfix<std::string>(" (framework directory)", path) &&
if (!utility::isPostfix<std::wstring>(L" (framework directory)", path) &&
FilePath(path).getCanonical().exists() &&
!FilePath(path).getCanonical().getConcatenated(L"/stdarg.h").exists())
{
@@ -20,27 +20,32 @@ std::vector<FilePath> CxxVs15HeaderPathDetector::doGetPaths() const
.expandEnvironmentVariables();
if (!expandedPaths.empty())
{
const std::string output =
utility::executeProcess(
expandedPaths[0].wstr(), std::vector<std::wstring> {L"-latest", L"-property installationPath"}, FilePath(), 10000)
.second;
const FilePath vsInstallPath(output);
if (vsInstallPath.exists())
const utility::ProcessOutput out = utility::executeProcess(
expandedPaths.front().wstr(),
{L"-latest", L"-property", L"installationPath"},
FilePath(),
false,
10000);
if (out.exitCode == 0)
{
for (const FilePath& versionPath: FileSystem::getDirectSubDirectories(
vsInstallPath.getConcatenated(L"VC/Tools/MSVC")))
const FilePath vsInstallPath(out.output);
if (vsInstallPath.exists())
{
if (versionPath.exists())
for (const FilePath& versionPath: FileSystem::getDirectSubDirectories(
vsInstallPath.getConcatenated(L"VC/Tools/MSVC")))
{
headerSearchPaths.push_back(versionPath.getConcatenated(L"include"));
headerSearchPaths.push_back(versionPath.getConcatenated(L"atlmfc/include"));
if (versionPath.exists())
{
headerSearchPaths.push_back(versionPath.getConcatenated(L"include"));
headerSearchPaths.push_back(
versionPath.getConcatenated(L"atlmfc/include"));
}
}
headerSearchPaths.push_back(
vsInstallPath.getConcatenated(L"VC/Auxiliary/VS/include"));
headerSearchPaths.push_back(
vsInstallPath.getConcatenated(L"VC/Auxiliary/VS/UnitTest/include"));
}
headerSearchPaths.push_back(
vsInstallPath.getConcatenated(L"VC/Auxiliary/VS/include"));
headerSearchPaths.push_back(
vsInstallPath.getConcatenated(L"VC/Auxiliary/VS/UnitTest/include"));
}
}
}
@@ -9,22 +9,23 @@
namespace utility
{
std::vector<std::string> getCxxHeaderPaths(const std::string& compilerName)
std::vector<std::wstring> getCxxHeaderPaths(const std::string& compilerName)
{
std::string command = compilerName + " -x c++ -v -E /dev/null";
std::string clangOutput = utility::executeProcess(
utility::decodeFromUtf8(compilerName),
std::vector<std::wstring> {L"-x c++", L"-v", L"-E /dev/null"})
.second;
std::string standardHeaders = utility::substrBetween<std::string>(
clangOutput, "#include <...> search starts here:\n", "\nEnd of search list");
std::vector<std::string> paths;
std::vector<std::wstring> paths;
if (!standardHeaders.empty())
const utility::ProcessOutput out = utility::executeProcess(
utility::decodeFromUtf8(compilerName), {L"-x", L"c++", L"-v", L"-E", L"/dev/null"});
if (out.exitCode == 0)
{
for (const std::string& s: utility::splitToVector(standardHeaders, '\n'))
std::wstring standardHeaders = utility::substrBetween<std::wstring>(
out.output, L"#include <...> search starts here:\n", L"\nEnd of search list");
if (!standardHeaders.empty())
{
paths.push_back(utility::trim(s));
for (const std::wstring& s: utility::splitToVector(standardHeaders, L'\n'))
{
paths.push_back(utility::trim(s));
}
}
}
@@ -9,7 +9,7 @@
namespace utility
{
std::vector<std::string> getCxxHeaderPaths(const std::string& compilerName);
std::vector<std::wstring> getCxxHeaderPaths(const std::string& compilerName);
std::vector<FilePath> getWindowsSdkHeaderSearchPaths(ApplicationArchitectureType architectureType);
FilePath getWindowsSdkRootPathUsingRegistry(
@@ -54,28 +54,28 @@ std::vector<FilePath> JavaPathDetectorLinux::doGetPaths() const
FilePath JavaPathDetectorLinux::getJavaInPath() const
{
std::string output = utility::executeProcess(L"which", std::vector<std::wstring>{L"java"}).second;
if (!output.empty())
bool ok;
FilePath javaPath(utility::searchPath(L"java", ok));
if (ok && !javaPath.empty() && javaPath.exists())
{
output = utility::trim(output);
FilePath javaPath(output);
if (!javaPath.empty() && javaPath.exists())
{
return javaPath;
}
return javaPath;
}
return FilePath();
}
FilePath JavaPathDetectorLinux::readLink(const FilePath& path) const
{
FilePath javaPath(utility::executeProcess(L"readlink", std::vector<std::wstring>{L"-f", path.wstr()}).second);
if (!javaPath.empty())
const utility::ProcessOutput out = utility::executeProcess(
L"readlink", std::vector<std::wstring> {L"-f", path.wstr()});
if (out.exitCode == 0 && !out.output.empty())
{
return javaPath;
FilePath javaPath(utility::trim(out.output));
if (!javaPath.empty())
{
return javaPath;
}
}
return FilePath();
}
@@ -113,7 +113,9 @@ FilePath JavaPathDetectorLinux::getJavaInJavaHome() const
bool JavaPathDetectorLinux::checkVersion(const FilePath& path) const
{
std::string output = utility::executeProcess(path.wstr(), std::vector<std::wstring>{L"-version"}).second;
const utility::ProcessOutput out = utility::executeProcess(path.wstr(), {L"-version"});
return output.find(m_javaVersion) != std::string::npos;
return (
(out.exitCode == 0) &&
(out.output.find(utility::decodeFromUtf8(m_javaVersion)) != std::string::npos));
}
@@ -14,21 +14,22 @@ std::vector<FilePath> JavaPathDetectorMac::doGetPaths() const
std::vector<FilePath> paths;
FilePath javaPath;
std::string output = utility::executeProcess(L"/usr/libexec/java_home", std::vector<std::wstring>{}).second;
const utility::ProcessOutput out = utility::executeProcess(L"/usr/libexec/java_home", {});
const std::wstring output = out.exitCode == 0 ? utility::trim(out.output) : L"";
if (!output.empty())
{
javaPath = FilePath(utility::trim(output) + "/../MacOS/libjli.dylib").makeCanonical();
javaPath = FilePath(output + L"/../MacOS/libjli.dylib").makeCanonical();
}
if (!javaPath.exists() && output.size())
if (!javaPath.exists() && !output.empty())
{
javaPath = FilePath(utility::trim(output) + "/lib/libjli.dylib");
javaPath = FilePath(output + L"/lib/libjli.dylib");
}
if (!javaPath.exists() && output.size())
if (!javaPath.exists() && !output.empty())
{
javaPath = FilePath(utility::trim(output) + "/jre/lib/jli/libjli.dylib");
javaPath = FilePath(output + L"/jre/lib/jli/libjli.dylib");
}
if (!javaPath.exists())
@@ -36,9 +37,9 @@ std::vector<FilePath> JavaPathDetectorMac::doGetPaths() const
javaPath = FilePath(L"/usr/lib/libjli.dylib");
}
if (!javaPath.exists() && output.size())
if (!javaPath.exists() && !output.empty())
{
javaPath = FilePath(utility::trim(output) + "/jre/lib/server/libjvm.dylib");
javaPath = FilePath(output + L"/jre/lib/server/libjvm.dylib");
}
if (!javaPath.exists())
@@ -7,12 +7,17 @@ MavenPathDetectorUnix::MavenPathDetectorUnix(): PathDetector("Maven for Unix") {
std::vector<FilePath> MavenPathDetectorUnix::doGetPaths() const
{
FilePath mavenPath(utility::executeProcess(L"which", std::vector<std::wstring>{L"mvn"}).second);
std::vector<FilePath> paths;
if (mavenPath.exists())
const utility::ProcessOutput out = utility::executeProcess(L"which", {L"mvn"});
if (out.exitCode == 0)
{
paths.push_back(mavenPath);
FilePath mavenPath(out.output);
if (mavenPath.exists())
{
paths.push_back(mavenPath);
}
}
return paths;
}
@@ -7,10 +7,11 @@ MavenPathDetectorWindows::MavenPathDetectorWindows(): PathDetector("Maven for Wi
std::vector<FilePath> MavenPathDetectorWindows::doGetPaths() const
{
FilePath mavenPath(utility::executeProcess(L"cmd", std::vector<std::wstring>{L"/c where mvn.cmd && exit"}).second);
std::vector<FilePath> paths;
if (mavenPath.exists())
bool ok;
FilePath mavenPath(utility::searchPath(L"mvn.cmd", ok));
if (ok && !mavenPath.empty() && mavenPath.exists())
{
paths.push_back(mavenPath);
}
+155 -224
View File
@@ -1,284 +1,215 @@
#include "utilityApp.h"
#include <chrono>
#include <mutex>
#include <set>
#include <QProcess>
#include <QRegularExpression>
#include <QSysInfo>
#include <QThread>
#include <qprocessordetection.h>
#include <boost/asio/buffer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/read.hpp>
#include <boost/process.hpp>
#include <boost/process/async_pipe.hpp>
#include <boost/process/child.hpp>
#include <boost/process/io.hpp>
#include <boost/process/search_path.hpp>
#include <boost/process/start_dir.hpp>
#include "AppPath.h"
#include "ApplicationSettings.h"
#include "UserPaths.h"
#include <QThread>
#include "ScopedFunctor.h"
#include "logging.h"
#include "utilityString.h"
namespace
{
void logProcessStreams(QProcess& process, std::wstring& outputBuffer, std::wstring& errorBuffer)
{
{
outputBuffer += QString(process.readAllStandardOutput()).toStdWString();
std::vector<std::wstring> outputLines = utility::split<std::vector<std::wstring>>(
outputBuffer, L"\n");
for (size_t i = 0; i < outputLines.size() - 1; i++)
{
if (outputLines[i].back() == L'\r')
{
outputLines[i].pop_back();
}
LOG_INFO_BARE(L"Process output: " + outputLines[i]);
}
outputBuffer = outputLines.back();
}
{
errorBuffer += QString(process.readAllStandardError()).toStdWString();
std::vector<std::wstring> errorLines = utility::split<std::vector<std::wstring>>(
errorBuffer, L"\n");
for (size_t i = 0; i < errorLines.size() - 1; i++)
{
if (errorLines[i].back() == L'\r')
{
errorLines[i].pop_back();
}
LOG_ERROR_BARE(L"Process error: " + errorLines[i]);
}
errorBuffer = errorLines.back();
}
}
} // namespace
namespace utility
{
std::mutex s_runningProcessesMutex;
std::set<QProcess*> s_runningProcesses;
std::set<std::shared_ptr<boost::process::child>> s_runningProcesses;
} // namespace utility
std::pair<int, std::string> utility::executeProcess(
const std::wstring& commandPath,
const std::vector<std::wstring>& commandArguments,
const FilePath& workingDirectory,
const int timeout)
std::wstring utility::searchPath(const std::wstring& bin, bool& ok)
{
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
if (!workingDirectory.empty())
ok = false;
std::wstring r = boost::process::search_path(bin).generic_wstring();
if (!r.empty())
{
process.setWorkingDirectory(QString::fromStdWString(workingDirectory.wstr()));
ok = true;
return r;
}
QString command = QString::fromStdWString(commandPath);
for (const std::wstring& commandArgument: commandArguments)
{
command += QString::fromStdWString(L" " + commandArgument);
}
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QStringList envlist = env.toStringList();
envlist.replaceInStrings(
QRegularExpression(QStringLiteral("^(?i)PATH=(.*)")),
QStringLiteral("PATH=/opt/local/bin:/usr/local/bin:$HOME/bin:\\1"));
process.setEnvironment(envlist);
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
process.start(command);
s_runningProcesses.insert(&process);
}
process.waitForFinished(timeout);
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
s_runningProcesses.erase(&process);
}
// QProcess::ProcessError error = process.error();
const std::string processoutput = process.readAll().toStdString();
const int exitCode = process.exitCode();
process.close();
return std::make_pair(exitCode, utility::trim(processoutput));
return bin;
}
std::string utility::executeProcessUntilNoOutput(
const std::wstring& commandPath,
const std::vector<std::wstring>& commandArguments,
const FilePath& workingDirectory,
const int waitTime)
std::wstring utility::searchPath(const std::wstring& bin)
{
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
bool ok;
return searchPath(bin, ok);
}
if (!workingDirectory.empty())
utility::ProcessOutput utility::executeProcess(
const std::wstring& command,
const std::vector<std::wstring>& arguments,
const FilePath& workingDirectory,
const bool waitUntilNoOutput,
const int timeout,
bool logProcessOutput)
{
std::string output = "";
int exitCode = 255;
try
{
process.setWorkingDirectory(QString::fromStdWString(workingDirectory.wstr()));
}
boost::asio::io_service ios;
boost::process::async_pipe ap(ios);
QString command = QString::fromStdWString(commandPath);
for (const std::wstring& commandArgument: commandArguments)
{
command += QString::fromStdWString(L" " + commandArgument);
}
std::shared_ptr<boost::process::child> process;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QStringList envlist = env.toStringList();
envlist.replaceInStrings(
QRegularExpression(QStringLiteral("^(?i)PATH=(.*)")),
QStringLiteral("PATH=/opt/local/bin:/usr/local/bin:$HOME/bin:\\1"));
process.setEnvironment(envlist);
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
process.start(command);
s_runningProcesses.insert(&process);
}
std::string processoutput = "";
while (!process.waitForFinished(waitTime))
{
const std::string currentOutput = process.readAll().toStdString();
if (currentOutput.empty())
boost::process::environment env = boost::this_process::environment();
std::vector<std::string> previousPath = env["PATH"].to_vector();
env["PATH"] = {"/opt/local/bin", "/usr/local/bin", "$HOME/bin"};
for (const std::string& entry: previousPath)
{
LOG_WARNING(
"Canceling process because it did not generate any output during the last " +
std::to_string(waitTime / 1000) + " seconds.");
break;
env["PATH"].append(entry);
}
if (workingDirectory.empty())
{
process = std::make_shared<boost::process::child>(
searchPath(command),
boost::process::args(arguments),
env,
boost::process::std_in.close(),
(boost::process::std_out & boost::process::std_err) > ap);
}
else
{
processoutput += currentOutput;
process = std::make_shared<boost::process::child>(
searchPath(command),
boost::process::args(arguments),
boost::process::start_dir(workingDirectory.wstr()),
env,
boost::process::std_in.close(),
(boost::process::std_out & boost::process::std_err) > ap);
}
}
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
s_runningProcesses.erase(&process);
}
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
s_runningProcesses.insert(process);
}
processoutput += process.readAll().toStdString();
process.close();
processoutput = utility::trim(processoutput);
return processoutput;
}
int utility::executeProcessAndGetExitCode(
const std::wstring& commandPath,
const std::vector<std::wstring>& commandArguments,
const FilePath& workingDirectory,
const int timeout,
bool logProcessOutput,
std::wstring* errorMessage)
{
bool finished = false;
QProcess process;
QObject::connect(
&process, &QProcess::errorOccurred, [&finished, errorMessage](QProcess::ProcessError error) {
finished = true;
if (errorMessage != nullptr)
{
switch (error)
{
case QProcess::FailedToStart:
*errorMessage = L"File not found or resource error occurred.";
break;
case QProcess::Crashed:
*errorMessage = L"Process crashed.";
break;
case QProcess::Timedout:
*errorMessage = L"Process timed out.";
break;
case QProcess::ReadError:
*errorMessage = L"A read error occurred while executing process.";
break;
case QProcess::WriteError:
*errorMessage = L"A write error occurred while executing process.";
break;
case QProcess::UnknownError:
*errorMessage = L"An unknown error occurred while executing process.";
break;
}
};
ScopedFunctor remover([process]() {
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
s_runningProcesses.erase(process);
});
QObject::connect(
&process,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[&finished](int exitCode, QProcess::ExitStatus exitStatus) { finished = true; });
bool outputReceived = false;
std::vector<char> buf(128);
auto stdOutBuffer = boost::asio::buffer(buf);
std::string logBuffer;
std::function<void(const boost::system::error_code& ec, std::size_t n)> onStdOut =
[&output, &buf, &stdOutBuffer, &ap, &onStdOut, &outputReceived, &logBuffer, logProcessOutput](
const boost::system::error_code& ec, std::size_t size) {
std::string text;
text.reserve(size);
text.insert(text.end(), buf.begin(), buf.begin() + size);
if (!workingDirectory.empty())
{
process.setWorkingDirectory(QString::fromStdWString(workingDirectory.wstr()));
}
if (!text.empty())
{
outputReceived = true;
}
QString command = QString::fromStdWString(commandPath);
for (const std::wstring& commandArgument: commandArguments)
{
command += QString::fromStdWString(L" " + commandArgument);
}
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
QStringList envlist = env.toStringList();
envlist.replaceInStrings(
QRegularExpression(QStringLiteral("^(?i)PATH=(.*)")),
QStringLiteral("PATH=/opt/local/bin:/usr/local/bin:$HOME/bin:\\1"));
process.setEnvironment(envlist);
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
process.start(command);
s_runningProcesses.insert(&process);
}
{
std::wstring outputBuffer;
std::wstring errorBuffer;
if (timeout == -1)
{
while (!finished && !process.waitForFinished(1000))
{
output += text;
if (logProcessOutput)
{
logProcessStreams(process, outputBuffer, errorBuffer);
logBuffer += text;
const bool isEndOfLine = (logBuffer.back() == '\n');
const std::vector<std::string> lines = utility::splitToVector(logBuffer, "\n");
for (size_t i = 0; i < lines.size() - (isEndOfLine ? 0 : 1); i++)
{
LOG_INFO_BARE("Process output: " + lines[i]);
}
if (isEndOfLine)
{
logBuffer.clear();
}
else
{
logBuffer = lines.back();
}
}
if (!ec)
{
boost::asio::async_read(ap, stdOutBuffer, onStdOut);
}
};
boost::asio::async_read(ap, stdOutBuffer, onStdOut);
ios.run();
if (timeout > 0)
{
if (waitUntilNoOutput)
{
while (!process->wait_for(std::chrono::milliseconds(timeout)))
{
if (!outputReceived)
{
LOG_WARNING(
"Canceling process because it did not generate any output during the "
"last " +
std::to_string(timeout / 1000) + " seconds.");
process->terminate();
break;
}
outputReceived = false;
}
}
else
{
if (!process->wait_for(std::chrono::milliseconds(timeout)))
{
LOG_WARNING(
"Canceling process because it timed out after " +
std::to_string(timeout / 1000) + " seconds.");
process->terminate();
}
}
}
else
{
if (!finished)
{
process.waitForFinished(timeout);
}
process->wait();
}
if (logProcessOutput)
{
logProcessStreams(process, outputBuffer, errorBuffer);
for (const std::string& line: utility::splitToVector(logBuffer, "\n"))
{
LOG_INFO_BARE("Process output: " + line);
}
}
exitCode = process->exit_code();
}
catch (const boost::process::process_error& e)
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
s_runningProcesses.erase(&process);
ProcessOutput ret;
ret.error = utility::decodeFromUtf8(e.code().message());
ret.exitCode = e.code().value();
LOG_ERROR_BARE(L"Process error: " + ret.error);
return ret;
}
const int exitCode = process.exitCode();
process.close();
return exitCode;
ProcessOutput ret;
ret.output = utility::trim(utility::decodeFromUtf8(output));
ret.exitCode = exitCode;
return ret;
}
void utility::killRunningProcesses()
{
std::lock_guard<std::mutex> lock(s_runningProcessesMutex);
for (QProcess* process: s_runningProcesses)
for (std::shared_ptr<boost::process::child> process: s_runningProcesses)
{
process->kill();
process->terminate();
}
}
@@ -307,4 +238,4 @@ std::string utility::getOsTypeString()
break;
}
return "unknown";
}
}
+17 -15
View File
@@ -9,25 +9,27 @@
namespace utility
{
std::pair<int, std::string> executeProcess(
const std::wstring& commandPath,
const std::vector<std::wstring>& commandArguments,
const FilePath& workingDirectory = FilePath(),
const int timeout = 30000);
std::string executeProcessUntilNoOutput(
const std::wstring& commandPath,
const std::vector<std::wstring>& commandArguments,
const FilePath& workingDirectory,
int waitTime = 10000);
int executeProcessAndGetExitCode(
const std::wstring& commandPath,
const std::vector<std::wstring>& commandArguments,
struct ProcessOutput
{
std::wstring output;
std::wstring error;
int exitCode;
};
std::wstring searchPath(const std::wstring& bin, bool& ok);
std::wstring searchPath(const std::wstring& bin);
ProcessOutput executeProcess(
const std::wstring& command,
const std::vector<std::wstring>& arguments,
const FilePath& workingDirectory = FilePath(),
const bool waitUntilNoOutput = false,
const int timeout = 30000,
bool logProcessOutput = false,
std::wstring* errorMessage = nullptr);
bool logProcessOutput = false);
void killRunningProcesses();
int getIdealThreadCount();
constexpr OsType getOsType()
+6 -18
View File
@@ -104,12 +104,8 @@ std::wstring mavenGenerateSources(
auto args = getMavenArgs(settingsFilePath);
args.push_back(L"generate-sources");
std::shared_ptr<TextAccess> outputAccess = TextAccess::createFromString(
utility::executeProcessUntilNoOutput(
mavenPath.wstr(),
args,
projectDirectoryPath,
60000));
std::shared_ptr<TextAccess> outputAccess = TextAccess::createFromString(utility::encodeToUtf8(
utility::executeProcess(mavenPath.wstr(), args, projectDirectoryPath, true, 60000).output));
if (outputAccess->isEmpty())
{
@@ -132,12 +128,8 @@ bool mavenCopyDependencies(
args.push_back(L"dependency:copy-dependencies");
args.push_back(L"-DoutputDirectory=" + outputDirectoryPath.wstr());
std::shared_ptr<TextAccess> outputAccess = TextAccess::createFromString(
utility::executeProcessUntilNoOutput(
mavenPath.wstr(),
args,
projectDirectoryPath,
60000));
std::shared_ptr<TextAccess> outputAccess = TextAccess::createFromString(utility::encodeToUtf8(
utility::executeProcess(mavenPath.wstr(), args, projectDirectoryPath, true, 60000).output));
const std::wstring errorMessage = getErrorMessageFromMavenOutput(outputAccess);
if (!errorMessage.empty())
@@ -165,12 +157,8 @@ std::vector<FilePath> mavenGetAllDirectoriesFromEffectivePom(
args.push_back(L"help:effective-pom");
args.push_back(L"-Doutput=" + outputPath.wstr());
std::shared_ptr<TextAccess> outputAccess = TextAccess::createFromString(
utility::executeProcessUntilNoOutput(
mavenPath.wstr(),
args,
projectDirectoryPath,
60000));
std::shared_ptr<TextAccess> outputAccess = TextAccess::createFromString(utility::encodeToUtf8(
utility::executeProcess(mavenPath.wstr(), args, projectDirectoryPath, true, 60000).output));
const std::wstring errorMessage = getErrorMessageFromMavenOutput(outputAccess);
if (!errorMessage.empty())
@@ -49,25 +49,28 @@ std::set<FilePath> SourceGroupPythonEmpty::getAllSourceFilePaths() const
std::vector<std::shared_ptr<IndexerCommand>> SourceGroupPythonEmpty::getIndexerCommands(
const RefreshInfo& info) const
{
std::wstring args = L"";
std::vector<std::wstring> args;
args.push_back(L"index");
args += L" --source-file-path=%{SOURCE_FILE_PATH}";
args += L" --database-file-path=%{DATABASE_FILE_PATH}";
args.push_back(L"--source-file-path");
args.push_back(L"%{SOURCE_FILE_PATH}");
args.push_back(L"--database-file-path");
args.push_back(L"%{DATABASE_FILE_PATH}");
if (!m_settings->getEnvironmentPath().empty())
{
args += L" --environment-path=\"" +
m_settings->getEnvironmentPathExpandedAndAbsolute().wstr() + L"\"";
args.push_back(L"--environment-path");
args.push_back(m_settings->getEnvironmentPathExpandedAndAbsolute().wstr());
}
if (ApplicationSettings::getInstance()->getVerboseIndexerLoggingEnabled())
{
args += L" --verbose";
args.push_back(L"--verbose");
}
if (info.shallow)
{
args += L" --shallow";
args.push_back(L"--shallow");
}
std::vector<std::shared_ptr<IndexerCommand>> indexerCommands;
@@ -77,8 +80,8 @@ std::vector<std::shared_ptr<IndexerCommand>> SourceGroupPythonEmpty::getIndexerC
{
indexerCommands.push_back(std::make_shared<IndexerCommandCustom>(
INDEXER_COMMAND_PYTHON,
L"\"" + ResourcePaths::getPythonPath().wstr() +
L"SourcetrailPythonIndexer\" index" + args,
ResourcePaths::getPythonIndexerFilePath().wstr(),
args,
m_settings->getProjectSettings()->getProjectFilePath(),
m_settings->getProjectSettings()->getTempDBFilePath(),
std::to_wstring(SqliteIndexStorage::getStorageVersion()),
+22 -12
View File
@@ -56,28 +56,38 @@ std::shared_ptr<TestStorage> parseCode(std::string code)
const std::set<FilePathFilter> includeFilters;
const FilePath workingDirectory(L".");
std::wstring args = L"";
args += L" --source-file-path=%{SOURCE_FILE_PATH}";
args += L" --database-file-path=%{DATABASE_FILE_PATH}";
args += L" --shallow";
std::vector<std::wstring> args;
args.push_back(L"index");
args.push_back(L"--source-file-path");
args.push_back(L"%{SOURCE_FILE_PATH}");
args.push_back(L"--database-file-path");
args.push_back(L"%{DATABASE_FILE_PATH}");
args.push_back(L"--shallow");
std::shared_ptr<IndexerCommandCustom> indexerCommand = std::make_shared<IndexerCommandCustom>(
INDEXER_COMMAND_PYTHON,
L"\"" +
FilePath("../app").getConcatenated(ResourcePaths::getPythonPath()).makeAbsolute().wstr() +
L"SourcetrailPythonIndexer\" index" + args,
FilePath("../app")
.getConcatenated(ResourcePaths::getPythonIndexerFilePath())
.makeAbsolute()
.makeCanonical()
.wstr(),
args,
rootPath,
tempDbPath,
std::to_wstring(SqliteIndexStorage::getStorageVersion()),
sourceFilePath,
true);
std::wstring errorMessage;
const int result = utility::executeProcessAndGetExitCode(
indexerCommand->getCustomCommand(), {}, rootPath, -1, true, &errorMessage);
const utility::ProcessOutput out = utility::executeProcess(
indexerCommand->getCommand(), indexerCommand->getArguments(), rootPath, false, -1, true);
REQUIRE(result == 0);
REQUIRE(errorMessage.empty());
if (!out.error.empty())
{
FAIL(
"Error occurred while running the indexer: \"" + utility::encodeToUtf8(out.error) +
"\". Process output was: \"" + utility::encodeToUtf8(out.output) + "\"");
}
REQUIRE(out.exitCode == 0);
}
std::shared_ptr<TestStorage> testStorage;
+8 -2
View File
@@ -148,9 +148,15 @@ std::wstring indexerCommandCustomToString(
std::shared_ptr<const IndexerCommandCustom> indexerCommand, const FilePath& baseDirectory)
{
std::wstring result;
result += L"SourceFilePath: \"" +
result += L"IndexerCommandCustom\n";
result += L"\tSourceFilePath: \"" +
indexerCommand->getSourceFilePath().getRelativeTo(baseDirectory).wstr() + L"\"\n";
result += L"\tCustom Command: \"" + indexerCommand->getCustomCommand() + L"\"\n";
result += L"\tCustom Command: \"" + indexerCommand->getCommand() + L"\"\n";
result += L"\tArguments:\n";
for (const std::wstring& argument: indexerCommand->getArguments())
{
result += L"\t\t\"" + argument + L"\"\n";
}
return result;
}