Merge remote-tracking branch 'origin/8.0'

Change-Id: I9f41e115adb25c08acc01110b6027020eff1a1e7
This commit is contained in:
Eike Ziller
2022-06-17 11:29:14 +02:00
216 changed files with 1616 additions and 1419 deletions

7
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,7 @@
Thank you for contributing to Qt Creator! Unfortunately the GitHub Qt Creator
presence is only a git mirror.
Please submit your patch via gerrit:
https://wiki.qt.io/Qt_Creator#Setting_up_Gerrit_to_contribute_back_to_Qt_Creator
We are sorry for the inconvenience.

View File

@@ -7,7 +7,7 @@ on:
- 'doc/**'
env:
QT_VERSION: 6.2.3
QT_VERSION: 6.3.0
CLANG_VERSION: 14.0.3
ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1

22
dist/changes-8.0.0.md vendored
View File

@@ -10,17 +10,26 @@ the public Git repository. For example:
git clone git://code.qt.io/qt-creator/qt-creator.git
git log --cherry-pick --pretty=oneline origin/7.0..v8.0.0
Help
----
* Added support for mouse forward and backward buttons (QTCREATORBUG-25168)
Editing
-------
* Added shortcut for adding next search match to multi-selection
* Added warning when editing generated file (QTCREATORBUG-27173)
* Added option for hiding line ending information
* Fixed updating of annotations (QTCREATORBUG-26812)
* Fixed that whitespace was not selected on double-click (QTCREATORBUG-24607)
* Fixed `Rewrap Paragraph` when indenting with tabs (QTCREATORBUG-27602)
### C++
* Removed `libclang` based code model
* Fixed that `Generate Setter and Getter` generated non-static methods for
static pointer types (QTCREATORBUG-27547)
* Clangd
* Increased minimum `Clangd` version to 14
* Improved performance of `compile_commands.json` creation
@@ -64,6 +73,8 @@ Editing
* Fixed semantic highlighting after server reset
* Fixed that semantic update was delayed by `Document update threshold` even
after saving
* Fixed that tooltips could appear while Qt Creator is not in the foreground
* Fixed synchronization of outline view (QTCREATORBUG-27595)
### Image Viewer
@@ -73,11 +84,14 @@ Projects
--------
* Added locator filter for starting run configurations
* Added `BuildSystem:Name` variable for default build directory
(QTCREATORBUG-26147)
### CMake
* Added `Profile` build configuration type that is `RelWithDebInfo` with `QML
debugging and profiling`
* Added `install` command to wizard generated projects
* Turned `QML debugging and profiling` option on by default for `Debug`
configurations
* Removed hardcoded `QT_QML_DEBUG` from wizard created project files
@@ -96,6 +110,7 @@ Analyzer
### Coco
* Added experimental `Coco` integration
* Added visualization of code coverage in code editor
### CppCheck
@@ -129,6 +144,11 @@ Platforms
* Added auto-detection for MSVC ARM toolchain and debugger
* Fixed ABI detection on ARM Windows
### macOS
* Fixed import of existing builds of CMake projects that were done on the
command line (QTCREATORBUG-27591)
### Android
* Added option to connect physical device over WiFi
@@ -160,6 +180,7 @@ Aaron Barany
Adam Treat
Alesandro Portale
Alessandro Portale
Alexander Akulich
Alexander Drozdov
Alexandru Croitor
Andre Hartmann
@@ -176,6 +197,7 @@ David Schulz
Dmitry Shachnev
Eike Ziller
Erik Verbruggen
Evgeny Shtanov
Fawzi Mohamed
Henning Gruendl
Ihor Ivlev

View File

@@ -162,6 +162,8 @@
the application from running until the debug client connects to the
server. This enables debugging from the start.
\note Setting breakpoints is only possible if the application is started with block mode.
\li Select \uicontrol Debug > \uicontrol {Start Debugging} >
\uicontrol {Attach to QML Port}.

View File

@@ -1740,6 +1740,11 @@ def qdump__QString(d, value):
d.putArrayData(data, size, d.createType('@QChar'))
def qdump__QSettingsKey(d, value):
qdump__QString(d, value)
d.putBetterType(value.type)
def qdump__QStaticStringData(d, value):
size = value.type[0]
(ref, size, alloc, pad, offset, data) = value.split('iii@p%ss' % (2 * size))

View File

@@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.15
import QtTest 1.0
TestCase {

View File

@@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick %{QtQuickVersion}
Item {

View File

@@ -3,13 +3,16 @@
"supportedProjectTypes": [ ],
"id": "Q.Qml.2",
"category": "R.Qt",
"trDescription": "Creates a QML file with boilerplate code, starting with \"import QtQuick 2.0\".",
"trDescription": "Creates a QML file with boilerplate code, starting with \"import QtQuick\".",
"trDisplayName": "QML File (Qt Quick 2)",
"trDisplayCategory": "Qt",
"iconText": "qml",
"enabled": "%{JS: value('Plugins').indexOf('QmlJSEditor') >= 0}",
"options": { "key": "DefaultSuffix", "value": "%{JS: Util.preferredSuffix('text/x-qml')}" },
"options": [
{"key": "DefaultSuffix", "value": "%{JS: Util.preferredSuffix('text/x-qml')}"},
{"key": "QtQuickVersion", "value": "%{JS: Util.qtQuickVersion(value('TargetPath'))}"}
],
"pages" :
[

View File

@@ -36,3 +36,5 @@ else()
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif()
@endif
install(TARGETS %{ProjectName})

View File

@@ -3,3 +3,5 @@ cmake_minimum_required(VERSION 3.5)
project(%{ProjectName} LANGUAGES C)
add_executable(%{ProjectName} %{CFileName})
install(TARGETS %{ProjectName})

View File

@@ -6,3 +6,5 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(%{ProjectName} %{CppFileName})
install(TARGETS %{ProjectName})

View File

@@ -27,3 +27,5 @@ set_target_properties(%{TargetName} PROPERTIES
target_link_libraries(%{TargetName}
PRIVATE Qt6::Quick)
install(TARGETS %{TargetName} BUNDLE DESTINATION .)

View File

@@ -71,6 +71,8 @@ set_target_properties(%{ProjectName} PROPERTIES
WIN32_EXECUTABLE TRUE
)
install(TARGETS %{ProjectName} BUNDLE DESTINATION .)
if(QT_VERSION_MAJOR EQUAL 6)
qt_import_qml_plugins(%{ProjectName})
qt_finalize_executable(%{ProjectName})

View File

@@ -74,6 +74,8 @@ set_target_properties(%{ProjectName} PROPERTIES
WIN32_EXECUTABLE TRUE
)
install(TARGETS %{ProjectName} BUNDLE DESTINATION .)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(%{ProjectName})
endif()

View File

@@ -96,86 +96,6 @@ protected:
}
};
class ContainingFunctionAt: protected SymbolVisitor
{
TranslationUnit *translationUnit;
Symbol *root;
int line;
int column;
Symbol *functionSymbol;
bool foundFunction;
bool foundBlock;
bool scopeContains(Scope* scope, int line, int column){
if (!scope)
return false;
int scopeStartLine{-1}, scopeStartColumn{-1}, scopeEndLine{-1}, scopeEndColumn{-1};
translationUnit->getPosition(scope->startOffset(), &scopeStartLine, &scopeStartColumn);
translationUnit->getPosition(scope->endOffset(), &scopeEndLine, &scopeEndColumn);
if (line < scopeStartLine || line > scopeEndLine)
return false;
if (line > scopeStartLine && line < scopeEndLine)
return true;
if (scopeStartLine == line && column >= scopeStartColumn)
return true;
if (scopeEndLine == line && column <= scopeEndColumn)
return true;
return false;
}
public:
ContainingFunctionAt(TranslationUnit *unit, Symbol *root)
: translationUnit(unit), root(root), line(0), column(0), functionSymbol(nullptr)
, foundFunction(false), foundBlock(false) {}
Symbol *operator()(int line, int column)
{
this->line = line;
this->column = column;
this->functionSymbol = nullptr;
accept(root);
return foundBlock ? functionSymbol : nullptr;
}
protected:
bool preVisit(Symbol *s) final
{
if (foundBlock)
return false;
if (foundFunction) {
auto block = s->asBlock();
if (!block)
return true;
if (scopeContains(block->asScope(), line, column)) {
foundBlock = true;
return false;
}
return true;
}
auto asFunction = s->asFunction();
if (asFunction) {
if (s->line() < line || (s->line() == line && s->column() <= column)) {
foundFunction = scopeContains(s->asScope(), line, column);
if (foundFunction)
functionSymbol = asFunction;
}
}
return true;
}
};
class FindScopeAt: protected SymbolVisitor
{
TranslationUnit *_unit;
@@ -592,11 +512,19 @@ QString Document::functionAt(int line, int column, int *lineOpeningDeclaratorPar
if (line < 1 || column < 1)
return QString();
Symbol *symbol = ContainingFunctionAt{translationUnit(), globalNamespace()}(line, column);
Symbol *symbol = lastVisibleSymbolAt(line, column);
if (!symbol)
return QString();
// Find the enclosing function scope (which might be several levels up, or we might be standing
// on it)
Scope *scope = symbol->asScope();
if (!scope)
scope = symbol->enclosingScope();
while (scope && !scope->isFunction() )
scope = scope->enclosingScope();
if (!scope)
return QString();

View File

@@ -142,11 +142,30 @@ void FindUsages::reportResult(unsigned tokenIndex, const QList<LookupItem> &cand
const int len = tk.utf16chars();
const Usage u(Utils::FilePath::fromString(_doc->fileName()), lineText,
getType(line, col, tokenIndex), line, col - 1, len);
getContainingFunction(line, col), getType(line, col, tokenIndex),
line, col - 1, len);
_usages.append(u);
_references.append(tokenIndex);
}
QString FindUsages::getContainingFunction(int line, int column)
{
const QList<AST *> astPath = ASTPath(_doc)(line, column);
bool hasBlock = false;
for (auto it = astPath.crbegin(); it != astPath.crend(); ++it) {
if (!hasBlock && (*it)->asCompoundStatement())
hasBlock = true;
if (const auto func = (*it)->asFunctionDefinition()) {
if (!hasBlock)
return {};
if (!func->symbol)
return {};
return Overview().prettyName(func->symbol->name());
}
}
return {};
}
class FindUsages::GetUsageType
{
public:

View File

@@ -42,11 +42,14 @@ public:
enum class Type { Declaration, Initialization, Read, Write, WritableRef, Other };
Usage() = default;
Usage(const Utils::FilePath &path, const QString &lineText, Type t, int line, int col, int len)
: path(path), lineText(lineText), type(t), line(line), col(col), len(len) {}
Usage(const Utils::FilePath &path, const QString &lineText, const QString &func, Type t,
int line, int col, int len)
: path(path), lineText(lineText), containingFunction(func), type(t),
line(line), col(col), len(len) {}
Utils::FilePath path;
QString lineText;
QString containingFunction;
Type type = Type::Other;
int line = 0;
int col = 0;
@@ -75,6 +78,7 @@ protected:
void reportResult(unsigned tokenIndex, const Name *name, Scope *scope = nullptr);
void reportResult(unsigned tokenIndex, const QList<LookupItem> &candidates);
Usage::Type getType(int line, int column, int tokenIndex);
QString getContainingFunction(int line, int column);
bool checkCandidates(const QList<LookupItem> &candidates) const;
void checkExpression(unsigned startToken, unsigned endToken, Scope *scope = nullptr);

View File

@@ -557,9 +557,11 @@ void DiagramController::onBeginResetModel()
void DiagramController::onEndResetModel()
{
updateAllDiagramsList();
foreach (MDiagram *diagram, m_allDiagrams) {
for (MDiagram *diagram : qAsConst(m_allDiagrams)) {
const QList<DElement *> elements = diagram->diagramElements();
// remove all elements which are not longer part of the model
foreach (DElement *element, diagram->diagramElements()) {
for (int i = elements.size() - 1; i >= 0; --i) {
DElement *element = elements.at(i);
if (element->modelUid().isValid()) {
MElement *modelElement = m_modelController->findElement(element->modelUid());
if (!modelElement)
@@ -567,7 +569,7 @@ void DiagramController::onEndResetModel()
}
}
// update all remaining elements from model
foreach (DElement *element, diagram->diagramElements())
for (DElement *element : diagram->diagramElements())
updateElementFromModel(element, diagram, false);
}
emit endResetAllDiagrams();

View File

@@ -76,6 +76,8 @@ bool allOf(const T &container, F predicate);
/////////////////////////
template<typename T, typename F>
void erase(T &container, F predicate);
template<typename T, typename F>
bool eraseOne(T &container, F predicate);
/////////////////////////
// contains
@@ -442,7 +444,15 @@ void erase(T &container, F predicate)
container.erase(std::remove_if(std::begin(container), std::end(container), predicate),
std::end(container));
}
template<typename T, typename F>
bool eraseOne(T &container, F predicate)
{
const auto it = std::find_if(std::begin(container), std::end(container), predicate);
if (it == std::end(container))
return false;
container.erase(it);
return true;
}
//////////////////
// contains

View File

@@ -527,10 +527,11 @@ QString FilePath::fileNameWithPathComponents(int pathComponents) const
;
}
// If there are no more slashes before the found one, return the entire string
if (i > 0 && m_data.lastIndexOf(slash, i) != -1)
return m_data.mid(component);
return m_data;
// If there are no more slashes before the found one, return the entire string
return displayName();
}
/// \returns the base name of the file without the path.

View File

@@ -89,21 +89,6 @@ CallerHandle::~CallerHandle()
qDeleteAll(m_signals);
}
bool CallerHandle::waitForStarted(int msecs)
{
return waitForSignal(msecs, SignalType::Started);
}
bool CallerHandle::waitForReadyRead(int msces)
{
return waitForSignal(msces, SignalType::ReadyRead);
}
bool CallerHandle::waitForFinished(int msecs)
{
return waitForSignal(msecs, SignalType::Done);
}
void CallerHandle::flush()
{
flushFor(SignalType::NoSignal);
@@ -329,11 +314,11 @@ void CallerHandle::setProcessSetupData(ProcessSetupData *setup)
m_setup = setup;
}
bool CallerHandle::waitForSignal(int msecs, SignalType newSignal)
bool CallerHandle::waitForSignal(SignalType signalType, int msecs)
{
QTC_ASSERT(isCalledFromCallersThread(), return false);
QTC_ASSERT(m_launcherHandle, return false);
return m_launcherHandle->waitForSignal(msecs, newSignal);
return m_launcherHandle->waitForSignal(signalType, msecs);
}
// Called from caller's or launcher's thread.
@@ -351,7 +336,7 @@ bool CallerHandle::isCalledFromLaunchersThread() const
}
// Called from caller's thread exclusively.
bool LauncherHandle::waitForSignal(int msecs, CallerHandle::SignalType newSignal)
bool LauncherHandle::waitForSignal(CallerHandle::SignalType newSignal, int msecs)
{
QTC_ASSERT(!isCalledFromLaunchersThread(), return false);
QDeadlineTimer deadline(msecs);

View File

@@ -73,9 +73,7 @@ public:
LauncherHandle *launcherHandle() const { return m_launcherHandle; }
void setLauncherHandle(LauncherHandle *handle) { QMutexLocker locker(&m_mutex); m_launcherHandle = handle; }
bool waitForStarted(int msecs);
bool waitForReadyRead(int msces);
bool waitForFinished(int msecs);
bool waitForSignal(CallerHandle::SignalType signalType, int msecs);
// Returns the list of flushed signals.
void flush();
@@ -109,8 +107,6 @@ signals:
void done(const Utils::ProcessResultData &resultData);
private:
bool waitForSignal(int msecs, SignalType newSignal);
// Called from caller's thread exclusively.
void sendPacket(const Internal::LauncherPacket &packet);
// Called from caller's or launcher's thread.
@@ -158,7 +154,7 @@ public:
// Called from caller's thread, moved to launcher's thread afterwards.
LauncherHandle(quintptr token) : m_token(token) {}
// Called from caller's thread exclusively.
bool waitForSignal(int msecs, CallerHandle::SignalType newSignal);
bool waitForSignal(CallerHandle::SignalType newSignal, int msecs);
CallerHandle *callerHandle() const { return m_callerHandle; }
void setCallerHandle(CallerHandle *handle) { QMutexLocker locker(&m_mutex); m_callerHandle = handle; }

View File

@@ -60,12 +60,6 @@ MimeType mimeTypeForFile(const QString &fileName, MimeMatchMode mode)
return mdb.mimeTypeForFile(fileName, MimeDatabase::MatchMode(mode));
}
MimeType mimeTypeForFile(const QFileInfo &fileInfo, MimeMatchMode mode)
{
MimeDatabase mdb;
return mdb.mimeTypeForFile(fileInfo, MimeDatabase::MatchMode(mode));
}
MimeType mimeTypeForFile(const FilePath &filePath, MimeMatchMode mode)
{
MimeDatabase mdb;

View File

@@ -44,10 +44,6 @@
#include <mimemagicrule_p.h>
#include <mimetype.h>
QT_BEGIN_NAMESPACE
class QFileInfo;
QT_END_NAMESPACE
namespace Utils {
class FilePath;
@@ -59,8 +55,6 @@ enum class MimeMatchMode { MatchDefault = 0x0, MatchExtension = 0x1, MatchConten
QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const QString &fileName,
MimeMatchMode mode = MimeMatchMode::MatchDefault);
QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const QFileInfo &fileInfo,
MimeMatchMode mode = MimeMatchMode::MatchDefault);
QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const FilePath &filePath,
MimeMatchMode mode = MimeMatchMode::MatchDefault);
QTCREATOR_UTILS_EXPORT QList<MimeType> mimeTypesForFileName(const QString &fileName);

View File

@@ -85,13 +85,22 @@ enum class ProcessSignalType {
Done
};
class QTCREATOR_UTILS_EXPORT ProcessBlockingInterface : public QObject
{
private:
// Wait for:
// - Started is being called only in Starting state.
// - ReadyRead is being called in Starting or Running state.
// - Done is being called in Starting or Running state.
virtual bool waitForSignal(ProcessSignalType signalType, int msecs) = 0;
friend class Internal::QtcProcessPrivate;
};
class QTCREATOR_UTILS_EXPORT ProcessInterface : public QObject
{
Q_OBJECT
public:
ProcessInterface(QObject *parent = nullptr) : QObject(parent) {}
signals:
// This should be emitted when being in Starting state only.
// After emitting this signal the process enters Running state.
@@ -121,14 +130,7 @@ private:
// It's being called in Starting or Running state.
virtual void sendControlSignal(ControlSignal controlSignal) = 0;
// It's being called only in Starting state.
virtual bool waitForStarted(int msecs) = 0;
// It's being called in Starting or Running state.
virtual bool waitForReadyRead(int msecs) = 0;
// It's being called in Starting or Running state.
virtual bool waitForFinished(int msecs) = 0;
virtual ProcessBlockingInterface *processBlockingInterface() const { return nullptr; }
friend class QtcProcess;
friend class Internal::QtcProcessPrivate;

View File

@@ -322,10 +322,34 @@ bool DefaultImpl::ensureProgramExists(const QString &program)
return false;
}
class QProcessBlockingImpl : public ProcessBlockingInterface
{
public:
QProcessBlockingImpl(QProcess *process) : m_process(process) {}
private:
bool waitForSignal(ProcessSignalType signalType, int msecs) final
{
switch (signalType) {
case ProcessSignalType::Started:
return m_process->waitForStarted(msecs);
case ProcessSignalType::ReadyRead:
return m_process->waitForReadyRead(msecs);
case ProcessSignalType::Done:
return m_process->waitForFinished(msecs);
}
return false;
}
QProcess *m_process = nullptr;
};
class QProcessImpl final : public DefaultImpl
{
public:
QProcessImpl() : m_process(new ProcessHelper(this))
QProcessImpl()
: m_process(new ProcessHelper(this))
, m_blockingImpl(new QProcessBlockingImpl(m_process))
{
connect(m_process, &QProcess::started,
this, &QProcessImpl::handleStarted);
@@ -361,9 +385,7 @@ private:
}
}
bool waitForStarted(int msecs) final { return m_process->waitForStarted(msecs); }
bool waitForReadyRead(int msecs) final { return m_process->waitForReadyRead(msecs); }
bool waitForFinished(int msecs) final { return m_process->waitForFinished(msecs); }
virtual ProcessBlockingInterface *processBlockingInterface() const { return m_blockingImpl; }
void doDefaultStart(const QString &program, const QStringList &arguments) final
{
@@ -411,7 +433,8 @@ private:
emit done(result);
}
ProcessHelper *m_process;
ProcessHelper *m_process = nullptr;
QProcessBlockingImpl *m_blockingImpl = nullptr;
};
static uint uniqueToken()
@@ -420,6 +443,33 @@ static uint uniqueToken()
return ++globalUniqueToken;
}
class ProcessLauncherBlockingImpl : public ProcessBlockingInterface
{
public:
ProcessLauncherBlockingImpl(CallerHandle *caller) : m_caller(caller) {}
private:
bool waitForSignal(ProcessSignalType signalType, int msecs) final
{
// TODO: Remove CallerHandle::SignalType
const CallerHandle::SignalType type = [signalType] {
switch (signalType) {
case ProcessSignalType::Started:
return CallerHandle::SignalType::Started;
case ProcessSignalType::ReadyRead:
return CallerHandle::SignalType::ReadyRead;
case ProcessSignalType::Done:
return CallerHandle::SignalType::Done;
}
QTC_CHECK(false);
return CallerHandle::SignalType::NoSignal;
}();
return m_caller->waitForSignal(type, msecs);
}
CallerHandle *m_caller = nullptr;
};
class ProcessLauncherImpl final : public DefaultImpl
{
Q_OBJECT
@@ -434,6 +484,7 @@ public:
this, &ProcessInterface::readyRead);
connect(m_handle, &CallerHandle::done,
this, &ProcessInterface::done);
m_blockingImpl = new ProcessLauncherBlockingImpl(m_handle);
}
~ProcessLauncherImpl() final
{
@@ -462,9 +513,7 @@ private:
}
}
bool waitForStarted(int msecs) final { return m_handle->waitForStarted(msecs); }
bool waitForReadyRead(int msecs) final { return m_handle->waitForReadyRead(msecs); }
bool waitForFinished(int msecs) final { return m_handle->waitForFinished(msecs); }
virtual ProcessBlockingInterface *processBlockingInterface() const { return m_blockingImpl; }
void doDefaultStart(const QString &program, const QStringList &arguments) final
{
@@ -476,6 +525,7 @@ private:
const uint m_token = 0;
// Lives in caller's thread.
CallerHandle *m_handle = nullptr;
ProcessLauncherBlockingImpl *m_blockingImpl = nullptr;
};
static ProcessImpl defaultProcessImpl()
@@ -535,13 +585,15 @@ private:
const ProcessResultData m_resultData;
};
class GeneralProcessBlockingImpl;
class ProcessInterfaceHandler : public QObject
{
public:
ProcessInterfaceHandler(QtcProcessPrivate *caller, ProcessInterface *process);
ProcessInterfaceHandler(GeneralProcessBlockingImpl *caller, ProcessInterface *process);
// Called from caller's thread exclusively.
bool waitForSignal(int msecs, ProcessSignalType newSignal);
bool waitForSignal(ProcessSignalType newSignal, int msecs);
void moveToCallerThread();
private:
@@ -555,11 +607,44 @@ private:
void handleDone(const ProcessResultData &data);
void appendSignal(ProcessInterfaceSignal *newSignal);
QtcProcessPrivate *m_caller = nullptr;
GeneralProcessBlockingImpl *m_caller = nullptr;
QMutex m_mutex;
QWaitCondition m_waitCondition;
};
class GeneralProcessBlockingImpl : public ProcessBlockingInterface
{
public:
GeneralProcessBlockingImpl(QtcProcessPrivate *parent);
void flush() { flushSignals(takeAllSignals()); }
bool flushFor(ProcessSignalType signalType) {
return flushSignals(takeSignalsFor(signalType), &signalType);
}
bool shouldFlush() const { QMutexLocker locker(&m_mutex); return !m_signals.isEmpty(); }
// Called from ProcessInterfaceHandler thread exclusively.
void appendSignal(ProcessInterfaceSignal *launcherSignal);
private:
// Called from caller's thread exclusively
bool waitForSignal(ProcessSignalType newSignal, int msecs) final;
QList<ProcessInterfaceSignal *> takeAllSignals();
QList<ProcessInterfaceSignal *> takeSignalsFor(ProcessSignalType signalType);
bool flushSignals(const QList<ProcessInterfaceSignal *> &signalList,
ProcessSignalType *signalType = nullptr);
void handleStartedSignal(const StartedSignal *launcherSignal);
void handleReadyReadSignal(const ReadyReadSignal *launcherSignal);
void handleDoneSignal(const DoneSignal *launcherSignal);
QtcProcessPrivate *m_caller = nullptr;
std::unique_ptr<ProcessInterfaceHandler> m_processHandler;
mutable QMutex m_mutex;
QList<ProcessInterfaceSignal *> m_signals;
};
class QtcProcessPrivate : public QObject
{
public:
@@ -591,11 +676,18 @@ public:
void setProcessInterface(ProcessInterface *process)
{
m_process.reset(process);
m_processHandler.reset(new ProcessInterfaceHandler(this, process));
m_process->setParent(this);
connect(m_process.get(), &ProcessInterface::started,
this, &QtcProcessPrivate::handleStarted);
connect(m_process.get(), &ProcessInterface::readyRead,
this, &QtcProcessPrivate::handleReadyRead);
connect(m_process.get(), &ProcessInterface::done,
this, &QtcProcessPrivate::handleDone);
// In order to move the process into another thread together with handle
m_process->setParent(m_processHandler.get());
m_processHandler->setParent(this);
m_blockingInterface.reset(process->processBlockingInterface());
if (!m_blockingInterface)
m_blockingInterface.reset(new GeneralProcessBlockingImpl(this));
m_blockingInterface->setParent(this);
}
CommandLine fullCommandLine() const
@@ -624,14 +716,11 @@ public:
}
QtcProcess *q;
std::unique_ptr<ProcessInterfaceHandler> m_processHandler;
std::unique_ptr<ProcessBlockingInterface> m_blockingInterface;
std::unique_ptr<ProcessInterface> m_process;
ProcessSetupData m_setup;
void slotTimeout();
void handleStartedSignal(const StartedSignal *launcherSignal);
void handleReadyReadSignal(const ReadyReadSignal *launcherSignal);
void handleDoneSignal(const DoneSignal *launcherSignal);
void handleStarted(qint64 processId, qint64 applicationMainThreadId);
void handleReadyRead(const QByteArray &outputData, const QByteArray &errorData);
void handleDone(const ProcessResultData &data);
@@ -647,31 +736,11 @@ public:
ProcessResult interpretExitCode(int exitCode);
// === ProcessInterfaceHandler related ===
// Called from caller's thread exclusively
bool waitForSignal(int msecs, ProcessSignalType newSignal);
void flush() { flushSignals(takeAllSignals()); }
bool flushFor(ProcessSignalType signalType) {
return flushSignals(takeSignalsFor(signalType), &signalType);
}
QList<ProcessInterfaceSignal *> takeAllSignals();
QList<ProcessInterfaceSignal *> takeSignalsFor(ProcessSignalType signalType);
bool flushSignals(const QList<ProcessInterfaceSignal *> &signalList,
ProcessSignalType *signalType = nullptr);
bool shouldFlush() const { QMutexLocker locker(&m_mutex); return !m_signals.isEmpty(); }
bool waitForSignal(ProcessSignalType signalType, int msecs);
Qt::ConnectionType connectionType() const;
void sendControlSignal(ControlSignal controlSignal);
// Called from ProcessInterfaceHandler thread exclusively.
void appendSignal(ProcessInterfaceSignal *launcherSignal);
mutable QMutex m_mutex;
QList<ProcessInterfaceSignal *> m_signals;
QTimer m_killTimer;
// =======================================
QProcess::ProcessState m_state = QProcess::NotRunning;
qint64 m_processId = 0;
qint64 m_applicationMainThreadId = 0;
@@ -692,10 +761,11 @@ public:
Guard m_guard;
};
ProcessInterfaceHandler::ProcessInterfaceHandler(QtcProcessPrivate *caller,
ProcessInterfaceHandler::ProcessInterfaceHandler(GeneralProcessBlockingImpl *caller,
ProcessInterface *process)
: m_caller(caller)
{
process->disconnect();
connect(process, &ProcessInterface::started,
this, &ProcessInterfaceHandler::handleStarted);
connect(process, &ProcessInterface::readyRead,
@@ -705,7 +775,7 @@ ProcessInterfaceHandler::ProcessInterfaceHandler(QtcProcessPrivate *caller,
}
// Called from caller's thread exclusively.
bool ProcessInterfaceHandler::waitForSignal(int msecs, ProcessSignalType newSignal)
bool ProcessInterfaceHandler::waitForSignal(ProcessSignalType newSignal, int msecs)
{
QDeadlineTimer deadline(msecs);
while (true) {
@@ -769,17 +839,20 @@ void ProcessInterfaceHandler::appendSignal(ProcessInterfaceSignal *newSignal)
}
m_waitCondition.wakeOne();
// call in callers thread
QMetaObject::invokeMethod(m_caller, &QtcProcessPrivate::flush);
QMetaObject::invokeMethod(m_caller, &GeneralProcessBlockingImpl::flush);
}
// Called from caller's thread exclusively
bool QtcProcessPrivate::waitForSignal(int msecs, ProcessSignalType newSignal)
GeneralProcessBlockingImpl::GeneralProcessBlockingImpl(QtcProcessPrivate *parent)
: m_caller(parent)
, m_processHandler(new ProcessInterfaceHandler(this, parent->m_process.get()))
{
const QDeadlineTimer timeout(msecs);
const QDeadlineTimer currentKillTimeout(m_killTimer.remainingTime());
const bool needsSplit = m_killTimer.isActive() ? timeout > currentKillTimeout : false;
const QDeadlineTimer mainTimeout = needsSplit ? currentKillTimeout : timeout;
// In order to move the process interface into another thread together with handle
parent->m_process.get()->setParent(m_processHandler.get());
m_processHandler->setParent(this);
}
bool GeneralProcessBlockingImpl::waitForSignal(ProcessSignalType newSignal, int msecs)
{
m_processHandler->setParent(nullptr);
QThread thread;
@@ -789,12 +862,7 @@ bool QtcProcessPrivate::waitForSignal(int msecs, ProcessSignalType newSignal)
// the caller here is blocked, so all signals should be buffered and we are going
// to flush them from inside waitForSignal().
m_processHandler->moveToThread(&thread);
bool result = m_processHandler->waitForSignal(mainTimeout.remainingTime(), newSignal);
if (!result && needsSplit) {
m_killTimer.stop();
sendControlSignal(ControlSignal::Kill);
result = m_processHandler->waitForSignal(timeout.remainingTime(), newSignal);
}
const bool result = m_processHandler->waitForSignal(newSignal, msecs);
m_processHandler->moveToCallerThread();
m_processHandler->setParent(this);
thread.quit();
@@ -803,14 +871,14 @@ bool QtcProcessPrivate::waitForSignal(int msecs, ProcessSignalType newSignal)
}
// Called from caller's thread exclusively
QList<ProcessInterfaceSignal *> QtcProcessPrivate::takeAllSignals()
QList<ProcessInterfaceSignal *> GeneralProcessBlockingImpl::takeAllSignals()
{
QMutexLocker locker(&m_mutex);
return std::exchange(m_signals, {});
}
// Called from caller's thread exclusively
QList<ProcessInterfaceSignal *> QtcProcessPrivate::takeSignalsFor(ProcessSignalType signalType)
QList<ProcessInterfaceSignal *> GeneralProcessBlockingImpl::takeSignalsFor(ProcessSignalType signalType)
{
// If we are flushing for ReadyRead or Done - flush all.
if (signalType != ProcessSignalType::Started)
@@ -840,7 +908,7 @@ QList<ProcessInterfaceSignal *> QtcProcessPrivate::takeSignalsFor(ProcessSignalT
}
// Called from caller's thread exclusively
bool QtcProcessPrivate::flushSignals(const QList<ProcessInterfaceSignal *> &signalList,
bool GeneralProcessBlockingImpl::flushSignals(const QList<ProcessInterfaceSignal *> &signalList,
ProcessSignalType *signalType)
{
bool signalMatched = false;
@@ -866,14 +934,50 @@ bool QtcProcessPrivate::flushSignals(const QList<ProcessInterfaceSignal *> &sign
return signalMatched;
}
// Called from caller's thread exclusively
void GeneralProcessBlockingImpl::handleStartedSignal(const StartedSignal *aSignal)
{
m_caller->handleStarted(aSignal->processId(), aSignal->applicationMainThreadId());
}
void GeneralProcessBlockingImpl::handleReadyReadSignal(const ReadyReadSignal *aSignal)
{
m_caller->handleReadyRead(aSignal->stdOut(), aSignal->stdErr());
}
void GeneralProcessBlockingImpl::handleDoneSignal(const DoneSignal *aSignal)
{
m_caller->handleDone(aSignal->resultData());
}
// Called from ProcessInterfaceHandler thread exclusively.
void GeneralProcessBlockingImpl::appendSignal(ProcessInterfaceSignal *newSignal)
{
QMutexLocker locker(&m_mutex);
m_signals.append(newSignal);
}
bool QtcProcessPrivate::waitForSignal(ProcessSignalType newSignal, int msecs)
{
const QDeadlineTimer timeout(msecs);
const QDeadlineTimer currentKillTimeout(m_killTimer.remainingTime());
const bool needsSplit = m_killTimer.isActive() ? timeout > currentKillTimeout : false;
const QDeadlineTimer mainTimeout = needsSplit ? currentKillTimeout : timeout;
bool result = m_blockingInterface->waitForSignal(newSignal, mainTimeout.remainingTime());
if (!result && needsSplit) {
m_killTimer.stop();
sendControlSignal(ControlSignal::Kill);
result = m_blockingInterface->waitForSignal(newSignal, timeout.remainingTime());
}
return result;
}
Qt::ConnectionType QtcProcessPrivate::connectionType() const
{
return (m_process->thread() == thread()) ? Qt::DirectConnection
: Qt::BlockingQueuedConnection;
}
// Called from caller's thread exclusively
void QtcProcessPrivate::sendControlSignal(ControlSignal controlSignal)
{
QTC_ASSERT(QThread::currentThread() == thread(), return);
@@ -885,13 +989,6 @@ void QtcProcessPrivate::sendControlSignal(ControlSignal controlSignal)
}, connectionType());
}
// Called from ProcessInterfaceHandler thread exclusively.
void QtcProcessPrivate::appendSignal(ProcessInterfaceSignal *newSignal)
{
QMutexLocker locker(&m_mutex);
m_signals.append(newSignal);
}
void QtcProcessPrivate::clearForRun()
{
m_hangTimerCount = 0;
@@ -1188,17 +1285,6 @@ void QtcProcess::setRemoteProcessHooks(const DeviceProcessHooks &hooks)
s_deviceHooks = hooks;
}
void QtcProcess::stopProcess()
{
if (state() == QProcess::NotRunning)
return;
terminate();
if (waitForFinished(300))
return;
kill();
waitForFinished(300);
}
static bool askToKill(const QString &command)
{
#ifdef QT_GUI_LIB
@@ -1275,7 +1361,7 @@ bool QtcProcess::readDataFromProcess(int timeoutS,
QString QtcProcess::normalizeNewlines(const QString &text)
{
QString res = text;
const auto newEnd = std::unique(res.begin(), res.end(), [](const QChar &c1, const QChar &c2) {
const auto newEnd = std::unique(res.begin(), res.end(), [](const QChar c1, const QChar c2) {
return c1 == '\r' && c2 == '\r'; // QTCREATORBUG-24556
});
res.chop(std::distance(newEnd, res.end()));
@@ -1444,8 +1530,8 @@ bool QtcProcess::waitForStarted(int msecs)
return true;
if (d->m_state == QProcess::NotRunning)
return false;
return s_waitForStarted.measureAndRun(&QtcProcessPrivate::waitForSignal, d, msecs,
ProcessSignalType::Started);
return s_waitForStarted.measureAndRun(&QtcProcessPrivate::waitForSignal, d,
ProcessSignalType::Started, msecs);
}
bool QtcProcess::waitForReadyRead(int msecs)
@@ -1453,7 +1539,7 @@ bool QtcProcess::waitForReadyRead(int msecs)
QTC_ASSERT(d->m_process, return false);
if (d->m_state == QProcess::NotRunning)
return false;
return d->waitForSignal(msecs, ProcessSignalType::ReadyRead);
return d->waitForSignal(ProcessSignalType::ReadyRead, msecs);
}
bool QtcProcess::waitForFinished(int msecs)
@@ -1461,7 +1547,7 @@ bool QtcProcess::waitForFinished(int msecs)
QTC_ASSERT(d->m_process, return false);
if (d->m_state == QProcess::NotRunning)
return false;
return d->waitForSignal(msecs, ProcessSignalType::Done);
return d->waitForSignal(ProcessSignalType::Done, msecs);
}
QByteArray QtcProcess::readAllStandardOutput()
@@ -1511,9 +1597,9 @@ void QtcProcess::close()
d->m_process->disconnect();
d->m_process.release()->deleteLater();
}
if (d->m_processHandler) {
d->m_processHandler->disconnect();
d->m_processHandler.release()->deleteLater();
if (d->m_blockingInterface) {
d->m_blockingInterface->disconnect();
d->m_blockingInterface.release()->deleteLater();
}
d->clearForRun();
}
@@ -1618,10 +1704,16 @@ QString QtcProcess::allOutput() const
return !out.isEmpty() ? out : err;
}
QByteArray QtcProcess::rawStdOut() const
{
QTC_CHECK(d->m_stdOut.keepRawData);
return d->m_stdOut.rawData;
}
QString QtcProcess::stdOut() const
{
QTC_CHECK(d->m_stdOut.keepRawData);
return normalizeNewlines(d->m_codec->toUnicode(d->m_stdOut.rawData));
return d->m_codec->toUnicode(d->m_stdOut.rawData);
}
QString QtcProcess::stdErr() const
@@ -1631,13 +1723,37 @@ QString QtcProcess::stdErr() const
// is not trivial. So weaken it a bit for now.
//QTC_CHECK(d->m_stdErr.keepRawData);
QTC_CHECK(d->m_stdErr.keepRawData || d->m_stdErr.rawData.isEmpty());
return normalizeNewlines(d->m_codec->toUnicode(d->m_stdErr.rawData));
return d->m_codec->toUnicode(d->m_stdErr.rawData);
}
QByteArray QtcProcess::rawStdOut() const
QString QtcProcess::cleanedStdOut() const
{
QTC_CHECK(d->m_stdOut.keepRawData);
return d->m_stdOut.rawData;
return normalizeNewlines(stdOut());
}
QString QtcProcess::cleanedStdErr() const
{
return normalizeNewlines(stdErr());
}
static QStringList splitLines(const QString &text)
{
QStringList result = text.split('\n');
for (QString &line : result) {
if (line.endsWith('\r'))
line.chop(1);
}
return result;
}
const QStringList QtcProcess::stdOutLines() const
{
return splitLines(stdOut());
}
const QStringList QtcProcess::stdErrLines() const
{
return splitLines(stdErr());
}
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const QtcProcess &r)
@@ -1843,7 +1959,8 @@ void QtcProcessPrivate::slotTimeout()
|| askToKill(m_setup.m_commandLine.executable().toString());
m_waitingForUser = false;
if (terminate) {
q->stopProcess();
q->stop();
q->waitForFinished();
m_result = ProcessResult::Hang;
} else {
m_hangTimerCount = 0;
@@ -1854,22 +1971,6 @@ void QtcProcessPrivate::slotTimeout()
}
}
void QtcProcessPrivate::handleStartedSignal(const StartedSignal *aSignal)
{
handleStarted(aSignal->processId(), aSignal->applicationMainThreadId());
}
void QtcProcessPrivate::handleReadyReadSignal(const ReadyReadSignal *aSignal)
{
handleReadyRead(aSignal->stdOut(), aSignal->stdErr());
}
void QtcProcessPrivate::handleDoneSignal(const DoneSignal *aSignal)
{
m_killTimer.stop();
handleDone(aSignal->resultData());
}
void QtcProcessPrivate::handleStarted(qint64 processId, qint64 applicationMainThreadId)
{
QTC_CHECK(m_state == QProcess::Starting);
@@ -1908,6 +2009,7 @@ void QtcProcessPrivate::handleReadyRead(const QByteArray &outputData, const QByt
void QtcProcessPrivate::handleDone(const ProcessResultData &data)
{
m_killTimer.stop();
m_resultData = data;
switch (m_state) {

View File

@@ -175,7 +175,6 @@ public:
void setStdErrCallback(const std::function<void(const QString &)> &callback);
void setStdErrLineCallback(const std::function<void(const QString &)> &callback);
void stopProcess();
bool readDataFromProcess(int timeoutS, QByteArray *stdOut, QByteArray *stdErr,
bool showTimeOutMessageBox);
@@ -185,11 +184,17 @@ public:
QByteArray allRawOutput() const;
QString allOutput() const;
QString stdOut() const;
QString stdErr() const;
QByteArray rawStdOut() const;
QString stdOut() const; // possibly with CR
QString stdErr() const; // possibly with CR
QString cleanedStdOut() const; // with CR removed
QString cleanedStdErr() const; // with CR removed
const QStringList stdOutLines() const; // split, CR removed
const QStringList stdErrLines() const; // split, CR removed
QString exitMessage() const;
QString toStandaloneCommandLine() const;

View File

@@ -368,7 +368,10 @@ void ShellCommand::runFullySynchronous(QtcProcess &process, const FilePath &work
void ShellCommand::runSynchronous(QtcProcess &process, const FilePath &workingDirectory)
{
connect(this, &ShellCommand::terminate, &process, &QtcProcess::stopProcess);
connect(this, &ShellCommand::terminate, &process, [&process] {
process.stop();
process.waitForFinished();
});
process.setEnvironment(processEnvironment());
if (d->m_codec)
process.setCodec(d->m_codec);

View File

@@ -50,12 +50,6 @@ private:
qint64 write(const QByteArray &) final { QTC_CHECK(false); return -1; }
void sendControlSignal(ControlSignal controlSignal) final;
// intentionally no-op without an assert
bool waitForStarted(int) final { return false; }
bool waitForReadyRead(int) final { QTC_CHECK(false); return false; }
// intentionally no-op without an assert
bool waitForFinished(int) final { return false; }
// OK, however, impl looks a bit different (!= NotRunning vs == Running).
// Most probably changing it into (== Running) should be OK.
bool isRunning() const;

View File

@@ -127,7 +127,7 @@ bool AndroidQmlPreviewWorker::isPreviewRunning(int lastKnownPid) const
void AndroidQmlPreviewWorker::startPidWatcher()
{
m_pidFutureWatcher.setFuture(Utils::runAsync([this]() {
m_pidFutureWatcher.setFuture(runAsync([this]() {
// wait for started
const int sleepTimeMs = 2000;
QDeadlineTimer deadline(20000);
@@ -157,7 +157,7 @@ void AndroidQmlPreviewWorker::startLogcat()
QString args = QString("logcat --pid=%1").arg(m_viewerPid);
if (!m_logcatStartTimeStamp.isEmpty())
args += QString(" -T '%1'").arg(m_logcatStartTimeStamp);
Utils::CommandLine cmd(AndroidConfigurations::currentConfig().adbToolPath());
CommandLine cmd(AndroidConfigurations::currentConfig().adbToolPath());
cmd.setArguments(args);
m_logcatProcess.setCommand(cmd);
m_logcatProcess.setUseCtrlCStub(true);
@@ -190,7 +190,7 @@ AndroidQmlPreviewWorker::AndroidQmlPreviewWorker(ProjectExplorer::RunControl *ru
connect(this, &AndroidQmlPreviewWorker::previewPidChanged,
this, &AndroidQmlPreviewWorker::startLogcat);
connect(this, &RunWorker::stopped, &m_logcatProcess, &Utils::QtcProcess::stopProcess);
connect(this, &RunWorker::stopped, &m_logcatProcess, &QtcProcess::stop);
m_logcatProcess.setStdOutCallback([this](const QString &stdOut) {
filterLogcatAndAppendMessage(stdOut);
});
@@ -314,7 +314,7 @@ bool AndroidQmlPreviewWorker::preparePreviewArtefacts()
}
} else {
const FilePaths allFiles = m_rc->project()->files(m_rc->project()->SourceFiles);
const FilePaths filesToExport = Utils::filtered(allFiles,[](const FilePath &path) {
const FilePaths filesToExport = filtered(allFiles, [](const FilePath &path) {
return path.suffix() == "qmlproject";
});
@@ -363,7 +363,8 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder,
QByteArray stdOut;
QByteArray stdErr;
if (!rccProcess.readDataFromProcess(30, &stdOut, &stdErr, true)) {
rccProcess.stopProcess();
rccProcess.stop();
rccProcess.waitForFinished();
appendMessage(tr("A timeout occurred running \"%1\"").
arg(rccProcess.commandLine().toUserOutput()), StdErrFormat);
qrcPath.removeFile();

View File

@@ -166,8 +166,10 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
proc.setTimeoutS(timeout);
proc.setStdOutCallback([offset, progressQuota, &proc, &assertionFound, &fi](const QString &out) {
int progressPercent = parseProgress(out, assertionFound);
if (assertionFound)
proc.stopProcess();
if (assertionFound) {
proc.stop();
proc.waitForFinished();
}
if (progressPercent != -1)
fi.setProgressValue(offset + qRound((progressPercent / 100.0) * progressQuota));
});
@@ -175,8 +177,10 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar
output.stdError = err;
});
if (interruptible) {
QObject::connect(&sdkManager, &AndroidSdkManager::cancelActiveOperations,
&proc, &QtcProcess::stopProcess);
QObject::connect(&sdkManager, &AndroidSdkManager::cancelActiveOperations, &proc, [&proc] {
proc.stop();
proc.waitForFinished();
});
}
proc.setCommand({config.sdkManagerToolPath(), newArgs});
proc.runBlocking(EventLoopMode::On);

View File

@@ -39,7 +39,7 @@ namespace Autotest {
namespace Internal {
TestOutputReader *BoostTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
auto settings = static_cast<BoostTestSettings *>(framework()->testSettings());
return new BoostTestOutputReader(fi, app, buildDirectory(), projectFile(),

View File

@@ -36,7 +36,7 @@ public:
explicit BoostTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -29,6 +29,7 @@
#include "boosttesttreeitem.h"
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
@@ -41,7 +42,7 @@ namespace Internal {
static Q_LOGGING_CATEGORY(orLog, "qtc.autotest.boost.outputreader", QtWarningMsg)
BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile,
LogLevel log, ReportLevel report)
@@ -51,7 +52,7 @@ BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface<TestResultPt
, m_reportLevel(report)
{
if (m_testApplication) {
connect(m_testApplication, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
connect(m_testApplication, &Utils::QtcProcess::finished,
this, &BoostTestOutputReader::onFinished);
}
}
@@ -405,7 +406,8 @@ TestResultPtr BoostTestOutputReader::createDefaultResult() const
return TestResultPtr(result);
}
void BoostTestOutputReader::onFinished(int exitCode, QProcess::ExitStatus /*exitState*/) {
void BoostTestOutputReader::onFinished() {
int exitCode = m_testApplication->exitCode();
if (m_reportLevel == ReportLevel::No && m_testCaseCount != -1) {
int reportedFailsAndSkips = m_summary[ResultType::Fail] + m_summary[ResultType::Skip];
m_summary.insert(ResultType::Pass, m_testCaseCount - reportedFailsAndSkips);

View File

@@ -39,7 +39,7 @@ class BoostTestOutputReader : public TestOutputReader
Q_OBJECT
public:
BoostTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, LogLevel log, ReportLevel report);
protected:
void processOutputLine(const QByteArray &outputLine) override;
@@ -47,7 +47,7 @@ protected:
TestResultPtr createDefaultResult() const override;
private:
void onFinished(int exitCode, QProcess::ExitStatus /*exitState*/);
void onFinished();
void sendCompleteInformation();
void handleMessageMatch(const QRegularExpressionMatch &match);
void reportNoOutputFinish(const QString &description, ResultType type);

View File

@@ -90,22 +90,24 @@ void CatchCodeParser::handleIdentifier()
QTC_ASSERT(m_currentIndex < m_tokens.size(), return);
const Token &token = m_tokens.at(m_currentIndex);
const QByteArray &identifier = m_source.mid(int(token.bytesBegin()), int(token.bytes()));
if (identifier == "TEST_CASE") {
const QByteArray unprefixed = identifier.startsWith("CATCH_") ? identifier.mid(6) : identifier;
if (unprefixed == "TEST_CASE") {
handleTestCase(false);
} else if (identifier == "SCENARIO") {
} else if (unprefixed == "SCENARIO") {
handleTestCase(true);
} else if (identifier == "TEMPLATE_TEST_CASE" || identifier == "TEMPLATE_PRODUCT_TEST_CASE"
|| identifier == "TEMPLATE_LIST_TEST_CASE" || identifier == "TEMPLATE_TEST_CASE_SIG"
|| identifier == "TEMPLATE_PRODUCT_TEST_CASE_SIG") {
} else if (unprefixed == "TEMPLATE_TEST_CASE" || unprefixed == "TEMPLATE_PRODUCT_TEST_CASE"
|| unprefixed == "TEMPLATE_LIST_TEST_CASE" || unprefixed == "TEMPLATE_TEST_CASE_SIG"
|| unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_SIG") {
handleParameterizedTestCase(false);
} else if (identifier == "TEST_CASE_METHOD") {
} else if (unprefixed == "TEST_CASE_METHOD") {
handleFixtureOrRegisteredTestCase(true);
} else if (identifier == "TEMPLATE_TEST_CASE_METHOD_SIG"
|| identifier == "TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"
|| identifier == "TEMPLATE_TEST_CASE_METHOD"
|| identifier == "TEMPLATE_LIST_TEST_CASE_METHOD") {
} else if (unprefixed == "TEMPLATE_TEST_CASE_METHOD_SIG"
|| unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"
|| unprefixed == "TEMPLATE_TEST_CASE_METHOD"
|| unprefixed == "TEMPLATE_LIST_TEST_CASE_METHOD") {
handleParameterizedTestCase(true);
} else if (identifier == "METHOD_AS_TEST_CASE" || identifier == "REGISTER_TEST_CASE") {
} else if (unprefixed == "METHOD_AS_TEST_CASE" || unprefixed == "REGISTER_TEST_CASE") {
handleFixtureOrRegisteredTestCase(false);
}
}

View File

@@ -35,7 +35,8 @@
namespace Autotest {
namespace Internal {
TestOutputReader *CatchConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi, QProcess *app) const
TestOutputReader *CatchConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
Utils::QtcProcess *app) const
{
return new CatchOutputReader(fi, app, buildDirectory(), projectFile());
}
@@ -97,7 +98,7 @@ QStringList CatchConfiguration::argumentsForTestRunner(QStringList *omitted) con
{
QStringList arguments;
if (testCaseCount())
arguments << "\"" + testCases().join("\",\"") + "\"";
arguments << "\"" + testCases().join("\", \"") + "\"";
arguments << "--reporter" << "xml";
if (AutotestPlugin::settings()->processArgs) {

View File

@@ -34,7 +34,7 @@ class CatchConfiguration : public DebuggableTestConfiguration
public:
CatchConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -49,7 +49,8 @@ namespace CatchXml {
}
CatchOutputReader::CatchOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile)
: TestOutputReader (futureInterface, testApplication, buildDirectory)
, m_projectFile(projectFile)

View File

@@ -39,7 +39,7 @@ class CatchOutputReader : public TestOutputReader
public:
CatchOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:

View File

@@ -57,10 +57,11 @@ static bool isCatchTestCaseMacro(const QString &macroName)
static bool isCatchMacro(const QString &macroName)
{
QString unprefixed = macroName.startsWith("CATCH_") ? macroName.mid(6) : macroName;
const QStringList validSectionMacros = {
QStringLiteral("SECTION"), QStringLiteral("WHEN")
};
return isCatchTestCaseMacro(macroName) || validSectionMacros.contains(macroName);
return isCatchTestCaseMacro(unprefixed) || validSectionMacros.contains(unprefixed);
}
static bool includesCatchHeader(const CPlusPlus::Document::Ptr &doc,
@@ -123,7 +124,8 @@ bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> &futu
const QByteArray &fileContent = getFileContent(fileName);
if (!hasCatchNames(doc)) {
const QRegularExpression regex("\\b(SCENARIO|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|"
const QRegularExpression regex("\\b(CATCH_)?"
"(SCENARIO|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|"
"TEMPLATE_TEST_CASE(_METHOD)?_SIG|"
"TEMPLATE_PRODUCT_TEST_CASE(_METHOD)?_SIG|"
"TEMPLATE_LIST_TEST_CASE_METHOD|METHOD_AS_TEST_CASE|"

View File

@@ -37,7 +37,9 @@ namespace Internal {
QString CatchTreeItem::testCasesString() const
{
return m_state & CatchTreeItem::Parameterized ? QString(name() + " -*") : name();
QString testcase = m_state & CatchTreeItem::Parameterized ? QString(name() + " -*") : name();
// mask comma if it is part of the test case name
return testcase.replace(',', "\\,");
}
static QString nonRootDisplayName(const CatchTreeItem *it)

View File

@@ -36,7 +36,7 @@ CTestConfiguration::CTestConfiguration(ITestBase *testBase)
}
TestOutputReader *CTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
return new CTestOutputReader(fi, app, workingDirectory());
}

View File

@@ -36,7 +36,7 @@ public:
explicit CTestConfiguration(ITestBase *testBase);
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const final;
Utils::QtcProcess *app) const final;
};
} // namespace Internal

View File

@@ -71,7 +71,7 @@ private:
};
CTestOutputReader::CTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory)
: TestOutputReader(futureInterface, testApplication, buildDirectory)
{

View File

@@ -28,6 +28,8 @@
#include <QCoreApplication>
namespace Utils { class QtcProcess; }
namespace Autotest {
namespace Internal {
@@ -36,7 +38,7 @@ class CTestOutputReader final : public Autotest::TestOutputReader
Q_DECLARE_TR_FUNCTIONS(Autotest::Internal::CTestOutputReader)
public:
CTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory);
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
protected:
void processOutputLine(const QByteArray &outputLineWithNewLine) final;

View File

@@ -38,7 +38,7 @@ namespace Autotest {
namespace Internal {
TestOutputReader *GTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
return new GTestOutputReader(fi, app, buildDirectory(), projectFile());
}

View File

@@ -37,7 +37,7 @@ public:
: DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -28,6 +28,7 @@
#include "../testtreemodel.h"
#include "../testtreeitem.h"
#include <utils/hostosinfo.h>
#include <utils/qtcprocess.h>
#include <QDir>
#include <QFileInfo>
@@ -37,18 +38,19 @@ namespace Autotest {
namespace Internal {
GTestOutputReader::GTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile)
: TestOutputReader(futureInterface, testApplication, buildDirectory)
, m_projectFile(projectFile)
{
if (m_testApplication) {
connect(m_testApplication, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, [this] (int exitCode, QProcess::ExitStatus /*exitStatus*/) {
connect(m_testApplication, &Utils::QtcProcess::finished,
this, [this]() {
int exitCode = m_testApplication->exitCode();
if (exitCode == 1 && !m_description.isEmpty()) {
createAndReportResult(tr("Running tests failed.\n %1\nExecutable: %2")
.arg(m_description).arg(id()), ResultType::MessageFatal);
.arg(m_description).arg(id()), ResultType::MessageFatal);
}
// on Windows abort() will result in normal termination, but exit code will be set to 3
if (Utils::HostOsInfo::isWindowsHost() && exitCode == 3)

View File

@@ -38,7 +38,7 @@ class GTestOutputReader : public TestOutputReader
public:
GTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile);
protected:
void processOutputLine(const QByteArray &outputLine) override;

View File

@@ -38,7 +38,7 @@ namespace Autotest {
namespace Internal {
TestOutputReader *QtTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()

View File

@@ -36,7 +36,7 @@ public:
explicit QtTestConfiguration(ITestFramework *framework)
: DebuggableTestConfiguration(framework) {}
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -126,7 +126,7 @@ static QString constructBenchmarkInformation(const QString &metric, double value
}
QtTestOutputReader::QtTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication,
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile,
OutputMode mode, TestType type)

View File

@@ -48,7 +48,7 @@ public:
};
QtTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory,
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory,
const Utils::FilePath &projectFile, OutputMode mode, TestType type);
protected:
void processOutputLine(const QByteArray &outputLine) override;

View File

@@ -52,7 +52,9 @@ const QString QtTestResult::outputString(bool selected) const
case ResultType::UnexpectedPass:
case ResultType::BlacklistedFail:
case ResultType::BlacklistedPass:
output = className + "::" + m_function;
output = className;
if (!m_function.isEmpty())
output.append("::" + m_function);
if (!m_dataTag.isEmpty())
output.append(QString(" (%1)").arg(m_dataTag));
if (selected && !desc.isEmpty()) {
@@ -60,7 +62,9 @@ const QString QtTestResult::outputString(bool selected) const
}
break;
case ResultType::Benchmark:
output = className + "::" + m_function;
output = className;
if (!m_function.isEmpty())
output.append("::" + m_function);
if (!m_dataTag.isEmpty())
output.append(QString(" (%1)").arg(m_dataTag));
if (!desc.isEmpty()) {

View File

@@ -44,7 +44,7 @@ QuickTestConfiguration::QuickTestConfiguration(ITestFramework *framework)
}
TestOutputReader *QuickTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const
Utils::QtcProcess *app) const
{
auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings());
const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value()

View File

@@ -35,7 +35,7 @@ class QuickTestConfiguration : public DebuggableTestConfiguration
public:
explicit QuickTestConfiguration(ITestFramework *framework);
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const override;
Utils::QtcProcess *app) const override;
QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override;
Utils::Environment filteredEnvironment(const Utils::Environment &original) const override;
};

View File

@@ -35,9 +35,7 @@
#include <QPointer>
#include <QStringList>
QT_BEGIN_NAMESPACE
class QProcess;
QT_END_NAMESPACE
namespace Utils { class QtcProcess; }
namespace Autotest {
namespace Internal {
@@ -66,7 +64,7 @@ public:
Utils::FilePath executableFilePath() const;
virtual TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const = 0;
Utils::QtcProcess *app) const = 0;
virtual Utils::Environment filteredEnvironment(const Utils::Environment &original) const;
ITestBase *testBase() const { return m_testBase; }

View File

@@ -30,6 +30,7 @@
#include "testtreeitem.h"
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QDebug>
#include <QDir>
@@ -46,11 +47,12 @@ Utils::FilePath TestOutputReader::constructSourceFilePath(const Utils::FilePath
}
TestOutputReader::TestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory)
Utils::QtcProcess *testApplication,
const Utils::FilePath &buildDirectory)
: m_futureInterface(futureInterface)
, m_testApplication(testApplication)
, m_buildDir(buildDirectory)
, m_id(testApplication ? testApplication->program() : QString())
, m_id(testApplication ? testApplication->commandLine().executable().toUserOutput() : QString())
{
auto chopLineBreak = [](QByteArray line) {
if (line.endsWith('\n'))
@@ -61,17 +63,11 @@ TestOutputReader::TestOutputReader(const QFutureInterface<TestResultPtr> &future
};
if (m_testApplication) {
connect(m_testApplication, &QProcess::readyReadStandardOutput,
this, [chopLineBreak, this] () {
m_testApplication->setReadChannel(QProcess::StandardOutput);
while (m_testApplication->canReadLine())
processStdOutput(chopLineBreak(m_testApplication->readLine()));
m_testApplication->setStdOutLineCallback([this, &chopLineBreak](const QString &line) {
processStdOutput(chopLineBreak(line.toUtf8()));
});
connect(m_testApplication, &QProcess::readyReadStandardError,
this, [chopLineBreak, this] () {
m_testApplication->setReadChannel(QProcess::StandardError);
while (m_testApplication->canReadLine())
processStdError(chopLineBreak(m_testApplication->readLine()));
m_testApplication->setStdErrLineCallback([this, &chopLineBreak](const QString &line) {
processStdError(chopLineBreak(line.toUtf8()));
});
}
}

View File

@@ -29,9 +29,10 @@
#include <QFutureInterface>
#include <QObject>
#include <QProcess>
#include <QString>
namespace Utils { class QtcProcess; }
namespace Autotest {
class TestOutputReader : public QObject
@@ -39,7 +40,7 @@ class TestOutputReader : public QObject
Q_OBJECT
public:
TestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
QProcess *testApplication, const Utils::FilePath &buildDirectory);
Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory);
virtual ~TestOutputReader();
void processStdOutput(const QByteArray &outputLine);
virtual void processStdError(const QByteArray &outputLine);
@@ -67,7 +68,7 @@ protected:
void reportResult(const TestResultPtr &result);
QFutureInterface<TestResultPtr> m_futureInterface;
QProcess *m_testApplication; // not owned
Utils::QtcProcess *m_testApplication; // not owned
Utils::FilePath m_buildDir;
QString m_id;
QHash<ResultType, int> m_summary;

View File

@@ -66,7 +66,7 @@ public:
ProjectExplorer::Runnable r;
QTC_ASSERT(m_testConfig, return r);
r.command.setExecutable(m_testConfig->executableFilePath());
r.command.setArguments(m_testConfig->argumentsForTestRunner().join(' '));
r.command.addArgs(m_testConfig->argumentsForTestRunner().join(' '), Utils::CommandLine::Raw);
r.workingDirectory = m_testConfig->workingDirectory();
r.environment = m_testConfig->environment();
return r;

View File

@@ -90,6 +90,8 @@ TestRunner::TestRunner()
{
s_instance = this;
m_cancelTimer.setSingleShot(true);
connect(&m_cancelTimer, &QTimer::timeout, this, [this]() { cancelCurrent(Timeout); });
connect(&m_futureWatcher, &QFutureWatcher<TestResultPtr>::resultReadyAt,
this, [this](int index) { emit testResultReady(m_futureWatcher.resultAt(index)); });
connect(&m_futureWatcher, &QFutureWatcher<TestResultPtr>::finished,
@@ -131,16 +133,17 @@ void TestRunner::runTest(TestRunMode mode, const ITestTreeItem *item)
}
}
static QString processInformation(const QProcess *proc)
static QString processInformation(const QtcProcess *proc)
{
QTC_ASSERT(proc, return QString());
QString information("\nCommand line: " + proc->program() + ' ' + proc->arguments().join(' '));
const Utils::CommandLine command = proc->commandLine();
QString information("\nCommand line: " + command.executable().toUserOutput() + ' ' + command.arguments());
QStringList important = { "PATH" };
if (Utils::HostOsInfo::isLinuxHost())
important.append("LD_LIBRARY_PATH");
else if (Utils::HostOsInfo::isMacHost())
important.append({ "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH" });
const QProcessEnvironment &environment = proc->processEnvironment();
const Utils::Environment &environment = proc->environment();
for (const QString &var : important)
information.append('\n' + var + ": " + environment.value(var));
return information;
@@ -204,34 +207,35 @@ bool TestRunner::currentConfigValid()
void TestRunner::setUpProcess()
{
QTC_ASSERT(m_currentConfig, return);
m_currentProcess = new QProcess;
m_currentProcess->setReadChannel(QProcess::StandardOutput);
m_currentProcess = new QtcProcess;
if (m_currentConfig->testBase()->type() == ITestBase::Framework) {
TestConfiguration *current = static_cast<TestConfiguration *>(m_currentConfig);
m_currentProcess->setProgram(current->executableFilePath().toString());
m_currentProcess->setCommand({current->executableFilePath(), {}});
} else {
TestToolConfiguration *current = static_cast<TestToolConfiguration *>(m_currentConfig);
m_currentProcess->setProgram(current->commandLine().executable().toString());
m_currentProcess->setCommand({current->commandLine().executable(), {}});
}
}
void TestRunner::setUpProcessEnv()
{
Utils::CommandLine command = m_currentProcess->commandLine();
if (m_currentConfig->testBase()->type() == ITestBase::Framework) {
TestConfiguration *current = static_cast<TestConfiguration *>(m_currentConfig);
QStringList omitted;
m_currentProcess->setArguments(current->argumentsForTestRunner(&omitted));
command.addArgs(current->argumentsForTestRunner(&omitted).join(' '), Utils::CommandLine::Raw);
if (!omitted.isEmpty()) {
const QString &details = constructOmittedDetailsString(omitted);
reportResult(ResultType::MessageWarn, details.arg(current->displayName()));
}
} else {
TestToolConfiguration *current = static_cast<TestToolConfiguration *>(m_currentConfig);
m_currentProcess->setArguments(current->commandLine().splitArguments());
command.setArguments(current->commandLine().arguments());
}
m_currentProcess->setCommand(command);
m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory().toString());
m_currentProcess->setWorkingDirectory(m_currentConfig->workingDirectory());
const Utils::Environment &original = m_currentConfig->environment();
Utils::Environment environment = m_currentConfig->filteredEnvironment(original);
const Utils::EnvironmentItems removedVariables = Utils::filtered(
@@ -243,7 +247,7 @@ void TestRunner::setUpProcessEnv()
.arg(m_currentConfig->displayName());
reportResult(ResultType::MessageWarn, details);
}
m_currentProcess->setProcessEnvironment(environment.toProcessEnvironment());
m_currentProcess->setEnvironment(environment);
}
void TestRunner::scheduleNext()
@@ -272,15 +276,16 @@ void TestRunner::scheduleNext()
setUpProcessEnv();
connect(m_currentProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
connect(m_currentProcess, &Utils::QtcProcess::finished,
this, &TestRunner::onProcessFinished);
const int timeout = AutotestPlugin::settings()->timeout;
QTimer::singleShot(timeout, m_currentProcess, [this]() { cancelCurrent(Timeout); });
m_cancelTimer.setInterval(timeout);
m_cancelTimer.start();
qCInfo(runnerLog) << "Command:" << m_currentProcess->program();
qCInfo(runnerLog) << "Arguments:" << m_currentProcess->arguments();
qCInfo(runnerLog) << "Command:" << m_currentProcess->commandLine().executable();
qCInfo(runnerLog) << "Arguments:" << m_currentProcess->commandLine().arguments();
qCInfo(runnerLog) << "Working directory:" << m_currentProcess->workingDirectory();
qCDebug(runnerLog) << "Environment:" << m_currentProcess->environment();
qCDebug(runnerLog) << "Environment:" << m_currentProcess->environment().toStringList();
m_currentProcess->start();
if (!m_currentProcess->waitForStarted()) {
@@ -355,7 +360,8 @@ void TestRunner::onProcessFinished()
void TestRunner::resetInternalPointers()
{
delete m_currentOutputReader;
delete m_currentProcess;
if (m_currentProcess)
m_currentProcess->deleteLater();
delete m_currentConfig;
m_currentOutputReader = nullptr;
m_currentProcess = nullptr;
@@ -812,6 +818,7 @@ void TestRunner::onBuildQueueFinished(bool success)
void TestRunner::onFinished()
{
m_cancelTimer.stop();
// if we've been canceled and we still have test configurations queued just throw them away
qDeleteAll(m_selectedTests);
m_selectedTests.clear();

View File

@@ -32,16 +32,17 @@
#include <QFutureWatcher>
#include <QObject>
#include <QQueue>
#include <QTimer>
QT_BEGIN_NAMESPACE
class QCheckBox;
class QComboBox;
class QDialogButtonBox;
class QLabel;
class QProcess;
QT_END_NAMESPACE
namespace ProjectExplorer { class Project; }
namespace Utils { class QtcProcess; }
namespace Autotest {
@@ -105,7 +106,7 @@ private:
bool m_executingTests = false;
bool m_canceled = false;
ITestConfiguration *m_currentConfig = nullptr;
QProcess *m_currentProcess = nullptr;
Utils::QtcProcess *m_currentProcess = nullptr;
TestOutputReader *m_currentOutputReader = nullptr;
TestRunMode m_runMode = TestRunMode::None;
@@ -116,6 +117,7 @@ private:
QMetaObject::Connection m_finishDebugConnect;
// temporarily used for handling of switching the current target
QMetaObject::Connection m_targetConnect;
QTimer m_cancelTimer;
bool m_skipTargetsCheck = false;
};

View File

@@ -52,7 +52,6 @@ QdbDeployConfigurationFactory::QdbDeployConfigurationFactory()
return prj->deploymentKnowledge() == DeploymentKnowledge::Bad
&& prj->hasMakeInstallEquivalent();
});
addInitialStep(RemoteLinux::Constants::CheckForFreeDiskSpaceId);
addInitialStep(Qdb::Constants::QdbStopApplicationStepId);
addInitialStep(RemoteLinux::Constants::DirectUploadStepId);
}

View File

@@ -45,7 +45,6 @@
#include <qtsupport/qtversionfactory.h>
#include <remotelinux/checkforfreediskspacestep.h>
#include <remotelinux/genericdirectuploadstep.h>
#include <remotelinux/makeinstallstep.h>
#include <remotelinux/remotelinux_constants.h>
@@ -177,8 +176,6 @@ public:
QdbStopApplicationStepFactory m_stopApplicationStepFactory;
QdbMakeDefaultAppStepFactory m_makeDefaultAppStepFactory;
QdbDeployStepFactory<RemoteLinux::CheckForFreeDiskSpaceStep>
m_checkForFreeDiskSpaceStepFactory{RemoteLinux::Constants::CheckForFreeDiskSpaceId};
QdbDeployStepFactory<RemoteLinux::GenericDirectUploadStep>
m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId};
QdbDeployStepFactory<RemoteLinux::MakeInstallStep>

View File

@@ -34,8 +34,6 @@
# include "test/clangfixittest.h"
#endif
#include <utils/runextensions.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/messagemanager.h>
@@ -45,6 +43,7 @@
#include <cppeditor/cppmodelmanager.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectpanelfactory.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
@@ -53,6 +52,9 @@
#include <texteditor/textmark.h>
#include <utils/temporarydirectory.h>
#include <utils/runextensions.h>
using namespace Utils;
namespace ClangCodeModel {
@@ -69,10 +71,13 @@ void ClangCodeModelPlugin::generateCompilationDB()
const auto projectInfo = CppModelManager::instance()->projectInfo(target->project());
if (!projectInfo)
return;
FilePath baseDir = projectInfo->buildRoot();
if (baseDir == target->project()->projectDirectory())
baseDir = TemporaryDirectory::masterDirectoryFilePath();
QFuture<GenerateCompilationDbResult> task
= Utils::runAsync(&Internal::generateCompilationDB, projectInfo,
projectInfo->buildRoot(), CompilationDbPurpose::Project,
baseDir, CompilationDbPurpose::Project,
warningsConfigForProject(target->project()),
globalClangOptions(),
FilePath());

View File

@@ -185,11 +185,6 @@ CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::completionAssi
return nullptr;
}
CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::functionHintAssistProvider()
{
return nullptr;
}
void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data,
const Utils::LinkHandler &processLinkCallback, bool resolveTarget,
bool inNextSplit)

View File

@@ -63,7 +63,6 @@ public:
~ClangModelManagerSupport() override;
CppEditor::CppCompletionAssistProvider *completionAssistProvider() override;
CppEditor::CppCompletionAssistProvider *functionHintAssistProvider() override;
TextEditor::BaseHoverHandler *createHoverHandler() override { return nullptr; }
CppEditor::BaseEditorDocumentProcessor *createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) override;

View File

@@ -25,8 +25,6 @@
#include "clangtoolrunner.h"
#include "clangtoolsconstants.h"
#include <utils/environment.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
@@ -60,32 +58,17 @@ static QString finishedWithBadExitCode(const QString &name, int exitCode)
}
ClangToolRunner::ClangToolRunner(QObject *parent)
: QObject(parent), m_process(new QtcProcess)
: QObject(parent)
{}
ClangToolRunner::~ClangToolRunner()
{
if (m_process->state() != QProcess::NotRunning) {
// asking politly to terminate costs ~300 ms on windows so skip the courtasy and direct kill the process
if (HostOsInfo::isWindowsHost()) {
m_process->kill();
m_process->waitForFinished(100);
} else {
m_process->stopProcess();
}
}
m_process->deleteLater();
}
void ClangToolRunner::init(const FilePath &outputDirPath, const Environment &environment)
{
m_outputDirPath = outputDirPath;
QTC_CHECK(!m_outputDirPath.isEmpty());
m_process->setEnvironment(environment);
m_process->setWorkingDirectory(m_outputDirPath); // Current clang-cl puts log file into working dir.
connect(m_process, &QtcProcess::done, this, &ClangToolRunner::onProcessDone);
m_process.setEnvironment(environment);
m_process.setWorkingDirectory(m_outputDirPath); // Current clang-cl puts log file into working dir.
connect(&m_process, &QtcProcess::done, this, &ClangToolRunner::onProcessDone);
}
QStringList ClangToolRunner::mainToolArguments() const
@@ -139,20 +122,20 @@ bool ClangToolRunner::run(const QString &fileToAnalyze, const QStringList &compi
m_commandLine = {m_executable, m_argsCreator(compilerOptions)};
qCDebug(LOG).noquote() << "Starting" << m_commandLine.toUserOutput();
m_process->setCommand(m_commandLine);
m_process->start();
m_process.setCommand(m_commandLine);
m_process.start();
return true;
}
void ClangToolRunner::onProcessDone()
{
if (m_process->result() == ProcessResult::StartFailed) {
if (m_process.result() == ProcessResult::StartFailed) {
emit finishedWithFailure(generalProcessError(m_name), commandlineAndOutput());
} else if (m_process->result() == ProcessResult::FinishedWithSuccess) {
qCDebug(LOG).noquote() << "Output:\n" << m_process->stdOut();
} else if (m_process.result() == ProcessResult::FinishedWithSuccess) {
qCDebug(LOG).noquote() << "Output:\n" << m_process.stdOut();
emit finishedWithSuccess(m_fileToAnalyze);
} else if (m_process->result() == ProcessResult::FinishedWithError) {
emit finishedWithFailure(finishedWithBadExitCode(m_name, m_process->exitCode()),
} else if (m_process.result() == ProcessResult::FinishedWithError) {
emit finishedWithFailure(finishedWithBadExitCode(m_name, m_process.exitCode()),
commandlineAndOutput());
} else { // == QProcess::CrashExit
emit finishedWithFailure(finishedDueToCrash(m_name), commandlineAndOutput());
@@ -165,8 +148,8 @@ QString ClangToolRunner::commandlineAndOutput() const
"Process Error: %2\n"
"Output:\n%3")
.arg(m_commandLine.toUserOutput())
.arg(m_process->error())
.arg(m_process->stdOut());
.arg(m_process.error())
.arg(m_process.stdOut());
}
} // namespace Internal

View File

@@ -28,13 +28,10 @@
#include "clangtoolslogfilereader.h"
#include <utils/commandline.h>
#include <QProcess>
#include <utils/qtcprocess.h>
#include <memory>
namespace Utils { class QtcProcess; }
namespace ClangTools {
namespace Internal {
@@ -46,7 +43,6 @@ class ClangToolRunner : public QObject
public:
ClangToolRunner(QObject *parent = nullptr);
~ClangToolRunner() override;
void init(const Utils::FilePath &outputDirPath, const Utils::Environment &environment);
void setName(const QString &name) { m_name = name; }
@@ -83,7 +79,7 @@ private:
private:
Utils::FilePath m_outputDirPath;
Utils::QtcProcess *m_process = nullptr;
Utils::QtcProcess m_process;
QString m_name;
Utils::FilePath m_executable;

View File

@@ -28,7 +28,6 @@
#include "cmakebuildconfiguration.h"
#include "cmakebuildsystem.h"
#include "cmakekitinformation.h"
#include "cmakeprojectconstants.h"
#include "cmakeprojectplugin.h"
#include "cmakespecificsettings.h"
#include "cmaketoolmanager.h"
@@ -90,12 +89,6 @@ BuildDirParameters::BuildDirParameters(CMakeBuildSystem *buildSystem)
if (Utils::HostOsInfo::isAnyUnixHost())
environment.set("ICECC", "no");
CMakeSpecificSettings *settings = CMakeProjectPlugin::projectTypeSpecificSettings();
if (!settings->ninjaPath.filePath().isEmpty()) {
const Utils::FilePath ninja = settings->ninjaPath.filePath();
environment.appendOrSetPath(ninja.isFile() ? ninja.parentDir() : ninja);
}
cmakeToolId = CMakeKitAspect::cmakeToolId(k);
}
@@ -109,8 +102,5 @@ CMakeTool *BuildDirParameters::cmakeTool() const
return CMakeToolManager::findById(cmakeToolId);
}
BuildDirParameters::BuildDirParameters(const BuildDirParameters &) = default;
BuildDirParameters &BuildDirParameters::operator=(const BuildDirParameters &) = default;
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -25,14 +25,10 @@
#pragma once
#include "cmakeconfigitem.h"
#include "cmaketool.h"
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
#include <QString>
#include <utils/filepath.h>
namespace CMakeProjectManager {
namespace Internal {
@@ -44,8 +40,6 @@ class BuildDirParameters
public:
BuildDirParameters();
explicit BuildDirParameters(CMakeBuildSystem *buildSystem);
BuildDirParameters(const BuildDirParameters &other);
BuildDirParameters &operator=(const BuildDirParameters &other);
bool isValid() const;
CMakeTool *cmakeTool() const;

View File

@@ -379,7 +379,16 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
}
connect(m_buildSystem, &BuildSystem::parsingFinished, this, [this] {
m_configModel->setConfiguration(m_buildSystem->configurationFromCMake());
const CMakeConfig config = m_buildSystem->configurationFromCMake();
auto qmlDebugAspect = m_buildSystem->buildConfiguration()
->aspect<QtSupport::QmlDebuggingAspect>();
const TriState qmlDebugSetting = qmlDebugAspect->value();
bool qmlDebugConfig = CMakeBuildConfiguration::hasQmlDebugging(config);
if ((qmlDebugSetting == TriState::Enabled && !qmlDebugConfig)
|| (qmlDebugSetting == TriState::Disabled && qmlDebugConfig)) {
qmlDebugAspect->setValue(TriState::Default);
}
m_configModel->setConfiguration(config);
m_configModel->setInitialParametersConfiguration(
m_buildSystem->initialCMakeConfiguration());
m_buildSystem->filterConfigArgumentsFromAdditionalCMakeArguments();
@@ -738,7 +747,8 @@ void CMakeBuildSettingsWidget::updateButtonState()
} else {
m_reconfigureButton->setText(tr("Run CMake"));
}
reconfigureButtonFont.setBold(m_configModel->hasChanges(isInitial));
reconfigureButtonFont.setBold(isInitial ? m_configModel->hasChanges(isInitial)
: !configChanges.isEmpty());
}
m_reconfigureButton->setFont(reconfigureButtonFont);
@@ -1388,6 +1398,16 @@ bool CMakeBuildConfiguration::isIos(const Kit *k)
|| deviceType == Ios::Constants::IOS_SIMULATOR_TYPE;
}
bool CMakeBuildConfiguration::hasQmlDebugging(const CMakeConfig &config)
{
// Determine QML debugging flags. This must match what we do in
// CMakeBuildSettingsWidget::getQmlDebugCxxFlags()
// such that in doubt we leave the QML Debugging setting at "Leave at default"
const QString cxxFlagsInit = config.stringValueOf("CMAKE_CXX_FLAGS_INIT");
const QString cxxFlags = config.stringValueOf("CMAKE_CXX_FLAGS");
return cxxFlagsInit.contains("-DQT_QML_DEBUG") && cxxFlags.contains("-DQT_QML_DEBUG");
}
void CMakeBuildConfiguration::buildTarget(const QString &buildTarget)
{
auto cmBs = qobject_cast<CMakeBuildStep *>(findOrDefault(
@@ -1694,6 +1714,15 @@ FilePath CMakeBuildConfiguration::sourceDirectory() const
return aspect<SourceDirectoryAspect>()->filePath();
}
void CMakeBuildConfiguration::addToEnvironment(Utils::Environment &env) const
{
CMakeSpecificSettings *settings = CMakeProjectPlugin::projectTypeSpecificSettings();
if (!settings->ninjaPath.filePath().isEmpty()) {
const Utils::FilePath ninja = settings->ninjaPath.filePath();
env.appendOrSetPath(ninja.isFile() ? ninja.parentDir() : ninja);
}
}
QString CMakeBuildSystem::cmakeBuildType() const
{
auto setBuildTypeFromConfig = [this](const CMakeConfig &config) {

View File

@@ -55,6 +55,7 @@ public:
shadowBuildDirectory(const Utils::FilePath &projectFilePath, const ProjectExplorer::Kit *k,
const QString &bcName, BuildConfiguration::BuildType buildType);
static bool isIos(const ProjectExplorer::Kit *k);
static bool hasQmlDebugging(const CMakeConfig &config);
// Context menu action:
void buildTarget(const QString &buildTarget);
@@ -63,6 +64,8 @@ public:
void setSourceDirectory(const Utils::FilePath& path);
Utils::FilePath sourceDirectory() const;
void addToEnvironment(Utils::Environment &env) const override;
signals:
void signingFlagsChanged();

View File

@@ -36,26 +36,31 @@
#include "cmakeprojectplugin.h"
#include "cmakespecificsettings.h"
#include "projecttreehelper.h"
#include "utils/algorithm.h"
#include <android/androidconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <cppeditor/cppeditorconstants.h>
#include <cppeditor/cppprojectupdater.h>
#include <cppeditor/generatedcodemodelsupport.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qtsupport/qtcppkitinfo.h>
#include <qtsupport/qtkitinformation.h>
#include <app/app_version.h>
#include <utils/algorithm.h>
#include <utils/checkablemessagebox.h>
#include <utils/fileutils.h>
#include <utils/macroexpander.h>
@@ -1314,5 +1319,31 @@ void CMakeBuildSystem::updateInitialCMakeExpandableVars()
emit configurationChanged(config);
}
MakeInstallCommand CMakeBuildSystem::makeInstallCommand(const FilePath &installRoot) const
{
MakeInstallCommand cmd;
if (CMakeTool *tool = CMakeKitAspect::cmakeTool(target()->kit()))
cmd.command.setExecutable(tool->cmakeExecutable());
QString installTarget = "install";
if (usesAllCapsTargets())
installTarget = "INSTALL";
FilePath buildDirectory = ".";
if (auto bc = buildConfiguration())
buildDirectory = bc->buildDirectory();
cmd.command.addArg("--build");
cmd.command.addArg(buildDirectory.onDevice(cmd.command.executable()).path());
cmd.command.addArg("--target");
cmd.command.addArg(installTarget);
if (isMultiConfigReader())
cmd.command.addArgs({"--config", cmakeBuildType()});
cmd.environment.set("DESTDIR", installRoot.nativePath());
return cmd;
}
} // namespace Internal
} // namespace CMakeProjectManager

View File

@@ -98,6 +98,9 @@ public:
Utils::CommandLine commandLineForTests(const QList<QString> &tests,
const QStringList &options) const final;
ProjectExplorer::MakeInstallCommand makeInstallCommand(
const Utils::FilePath &installRoot) const final;
static bool filteredOutTarget(const CMakeBuildTarget &target);
bool isMultiConfig() const;

View File

@@ -64,6 +64,13 @@ void CMakeParser::setSourceDirectory(const QString &sourceDir)
OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputFormat type)
{
if (line.startsWith("ninja: build stopped")) {
m_lastTask = BuildSystemTask(Task::Error, line);
m_lines = 1;
flush();
return Status::Done;
}
if (type != StdErrFormat)
return Status::NotHandled;

View File

@@ -116,40 +116,4 @@ ProjectExplorer::DeploymentKnowledge CMakeProject::deploymentKnowledge() const
: DeploymentKnowledge::Bad;
}
MakeInstallCommand CMakeProject::makeInstallCommand(const Target *target,
const FilePath &installRoot)
{
MakeInstallCommand cmd;
if (const BuildConfiguration * const bc = target->activeBuildConfiguration()) {
if (const auto cmakeStep = bc->buildSteps()->firstOfType<CMakeBuildStep>()) {
if (CMakeTool *tool = CMakeKitAspect::cmakeTool(target->kit()))
cmd.command.setExecutable(tool->cmakeExecutable());
}
}
QString installTarget = "install";
QStringList config;
auto bs = qobject_cast<CMakeBuildSystem*>(target->buildSystem());
QTC_ASSERT(bs, return {});
if (bs->usesAllCapsTargets())
installTarget = "INSTALL";
if (bs->isMultiConfigReader())
config << "--config" << bs->cmakeBuildType();
FilePath buildDirectory = ".";
if (auto bc = bs->buildConfiguration())
buildDirectory = bc->buildDirectory();
cmd.command.addArg("--build");
cmd.command.addArg(buildDirectory.onDevice(cmd.command.executable()).path());
cmd.command.addArg("--target");
cmd.command.addArg(installTarget);
cmd.command.addArgs(config);
cmd.environment.set("DESTDIR", installRoot.nativePath());
return cmd;
}
} // namespace CMakeProjectManager

View File

@@ -54,8 +54,6 @@ protected:
private:
ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override;
ProjectExplorer::MakeInstallCommand makeInstallCommand(const ProjectExplorer::Target *target,
const Utils::FilePath &installRoot) final;
mutable Internal::CMakeProjectImporter *m_projectImporter = nullptr;

View File

@@ -337,13 +337,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
canonicalProjectDirectory.toUserOutput());
}
// Determine QML debugging flags. This must match what we do in
// CMakeBuildSettingsWidget::getQmlDebugCxxFlags()
// such that in doubt we leave the QML Debugging setting at "Leave at default"
const QString cxxFlagsInit = config.stringValueOf("CMAKE_CXX_FLAGS_INIT");
const QString cxxFlags = config.stringValueOf("CMAKE_CXX_FLAGS");
data->hasQmlDebugging = cxxFlagsInit.contains("-DQT_QML_DEBUG")
&& cxxFlags.contains("-DQT_QML_DEBUG");
data->hasQmlDebugging = CMakeBuildConfiguration::hasQmlDebugging(config);
data->buildDirectory = importPath;
data->cmakeBuildType = buildType;
@@ -394,10 +388,7 @@ bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const
if (!Utils::contains(allLanguages, [&tcd](const Id& language) {return language == tcd.language;}))
continue;
ToolChain *tc = ToolChainKitAspect::toolChain(k, tcd.language);
if (!tc
|| !Utils::Environment::systemEnvironment()
.isSameExecutable(tc->compilerCommand().toString(),
tcd.compilerPath.toString())) {
if (!tc || !tc->matchesCompilerCommand(tcd.compilerPath)) {
return false;
}
}

View File

@@ -154,15 +154,11 @@ class CocoTextMark : public TextEditor::TextMark
public:
CocoTextMark(const FilePath &fileName, const CocoDiagnostic &diag, const Id &clientId)
: TextEditor::TextMark(fileName, diag.range().start().line() + 1, clientId)
, m_severity(diag.cocoSeverity())
{
setLineAnnotation(diag.message());
setToolTip(diag.message());
if (optional<CocoDiagnosticSeverity> severity = diag.cocoSeverity()) {
const TextEditor::TextStyle style = styleForSeverity(*severity);
m_annotationColor =
TextEditor::TextEditorSettings::fontSettings().formatFor(style).foreground();
}
updateAnnotationColor();
}
QColor annotationColor() const override
@@ -171,6 +167,16 @@ public:
: TextEditor::TextMark::annotationColor();
}
void updateAnnotationColor()
{
if (m_severity) {
const TextEditor::TextStyle style = styleForSeverity(*m_severity);
m_annotationColor =
TextEditor::TextEditorSettings::fontSettings().formatFor(style).foreground();
}
}
optional<CocoDiagnosticSeverity> m_severity;
QColor m_annotationColor;
};
@@ -180,10 +186,22 @@ public:
CocoDiagnosticManager(Client *client)
: DiagnosticManager(client)
{
connect(TextEditor::TextEditorSettings::instance(),
&TextEditor::TextEditorSettings::fontSettingsChanged,
this,
&CocoDiagnosticManager::fontSettingsChanged);
setExtraSelectionsId("CocoExtraSelections");
}
private:
void fontSettingsChanged()
{
forAllMarks([](TextEditor::TextMark *mark){
static_cast<CocoTextMark *>(mark)->updateAnnotationColor();
mark->updateMarker();
});
}
TextEditor::TextMark *createTextMark(const FilePath &filePath,
const Diagnostic &diagnostic,
bool /*isProjectFile*/) const override

View File

@@ -188,7 +188,7 @@ const char G_VIEW_PANES[] = "QtCreator.Group.View.Panes";
// Tools menu groups
const char G_TOOLS_DEBUG[] = "QtCreator.Group.Tools.Debug";
const char G_TOOLS_OPTIONS[] = "QtCreator.Group.Tools.Options";
const char G_EDIT_PREFERENCES[] = "QtCreator.Group.Edit.Preferences";
// Window menu groups
const char G_WINDOW_SIZE[] = "QtCreator.Group.Window.Size";

View File

@@ -32,8 +32,10 @@
#include <utils/qtcassert.h>
#include <QDir>
#include <QDirIterator>
#include <QTemporaryFile>
#include <QVariant>
#include <QVersionNumber>
namespace Core {
namespace Internal {
@@ -165,5 +167,34 @@ QString UtilsJsExtension::asciify(const QString &input) const
return result;
}
QString UtilsJsExtension::qtQuickVersion(const QString &filePath) const
{
QDirIterator dirIt(Utils::FilePath::fromString(filePath).parentDir().path(), {"*.qml"},
QDir::Files, QDirIterator::Subdirectories);
while (dirIt.hasNext()) {
Utils::FileReader reader;
if (!reader.fetch(Utils::FilePath::fromString(dirIt.next())))
continue;
const QString data = QString::fromUtf8(reader.data());
static const QString importString("import QtQuick");
const int importIndex = data.indexOf(importString);
if (importIndex == -1)
continue;
const int versionIndex = importIndex + importString.length();
const int newLineIndex = data.indexOf('\n', versionIndex);
if (newLineIndex == -1)
continue;
const QString versionString = data.mid(versionIndex,
newLineIndex - versionIndex).simplified();
if (versionString.isEmpty())
return {};
const auto version = QVersionNumber::fromString(versionString);
if (version.isNull())
return {};
return version.toString();
}
return QLatin1String("2.15");
}
} // namespace Internal
} // namespace Core

View File

@@ -76,6 +76,9 @@ public:
// Generate a ascii-only string:
Q_INVOKABLE QString asciify(const QString &input) const;
// Heuristic to find out which QtQuick import version to use for the given file.
Q_INVOKABLE QString qtQuickVersion(const QString &filePath) const;
};
} // namespace Internal

View File

@@ -152,7 +152,7 @@ void FilePropertiesDialog::refresh()
m_ui->name->setText(fileInfo.fileName());
m_ui->path->setText(QDir::toNativeSeparators(fileInfo.canonicalPath()));
const Utils::MimeType mimeType = Utils::mimeTypeForFile(fileInfo);
const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_filePath);
m_ui->mimeType->setText(mimeType.name());
const EditorTypeList factories = IEditorFactory::preferredEditorTypes(m_filePath);

View File

@@ -2148,8 +2148,8 @@ void EditorManagerPrivate::updateWindowTitleForDocument(IDocument *document, QWi
if (!documentName.isEmpty())
windowTitle.append(documentName);
const QString filePath = document ? document->filePath().absoluteFilePath().path()
: QString();
const Utils::FilePath filePath = document ? document->filePath().absoluteFilePath()
: Utils::FilePath();
const QString windowTitleAddition = d->m_titleAdditionHandler
? d->m_titleAdditionHandler(filePath)
: QString();
@@ -2181,7 +2181,7 @@ void EditorManagerPrivate::updateWindowTitleForDocument(IDocument *document, QWi
windowTitle.append(dashSep);
windowTitle.append(Core::Constants::IDE_DISPLAY_NAME);
window->window()->setWindowTitle(windowTitle);
window->window()->setWindowFilePath(filePath);
window->window()->setWindowFilePath(filePath.path());
if (HostOsInfo::isMacHost()) {
if (document)

View File

@@ -71,7 +71,7 @@ class CORE_EXPORT EditorManager : public QObject
Q_OBJECT
public:
using WindowTitleHandler = std::function<QString (const QString &)>;
using WindowTitleHandler = std::function<QString (const Utils::FilePath &)>;
static EditorManager *instance();

View File

@@ -717,14 +717,14 @@ void MainWindow::registerDefaultActions()
connect(m_loggerAction, &QAction::triggered, this, [] { LoggingViewer::showLoggingView(); });
// Options Action
mtools->appendGroup(Constants::G_TOOLS_OPTIONS);
mtools->addSeparator(Constants::G_TOOLS_OPTIONS);
medit->appendGroup(Constants::G_EDIT_PREFERENCES);
medit->addSeparator(Constants::G_EDIT_PREFERENCES);
m_optionsAction = new QAction(tr("&Options..."), this);
m_optionsAction = new QAction(tr("&Preferences..."), this);
m_optionsAction->setMenuRole(QAction::PreferencesRole);
cmd = ActionManager::registerAction(m_optionsAction, Constants::OPTIONS);
cmd->setDefaultKeySequence(QKeySequence::Preferences);
mtools->addAction(cmd, Constants::G_TOOLS_OPTIONS);
medit->addAction(cmd, Constants::G_EDIT_PREFERENCES);
connect(m_optionsAction, &QAction::triggered, this, [] { ICore::showOptionsDialog(Id()); });
mwindow->addSeparator(Constants::G_WINDOW_LIST);

View File

@@ -112,7 +112,8 @@ static bool runPatchHelper(const QByteArray &input, const FilePath &workingDirec
QByteArray stdOut;
QByteArray stdErr;
if (!patchProcess.readDataFromProcess(30, &stdOut, &stdErr, true)) {
patchProcess.stopProcess();
patchProcess.stop();
patchProcess.waitForFinished();
MessageManager::writeFlashing(
QApplication::translate("Core::PatchTool", "A timeout occurred running \"%1\"")
.arg(patch.toUserOutput()));

View File

@@ -124,11 +124,6 @@ CppCompletionAssistProvider *BuiltinModelManagerSupport::completionAssistProvide
}
CppCompletionAssistProvider *BuiltinModelManagerSupport::functionHintAssistProvider()
{
return nullptr;
}
TextEditor::BaseHoverHandler *BuiltinModelManagerSupport::createHoverHandler()
{
return new CppHoverHandler;

View File

@@ -42,7 +42,6 @@ public:
~BuiltinModelManagerSupport() override;
CppCompletionAssistProvider *completionAssistProvider() final;
CppCompletionAssistProvider *functionHintAssistProvider() override;
TextEditor::BaseHoverHandler *createHoverHandler() final;
BaseEditorDocumentProcessor *createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) final;

View File

@@ -87,7 +87,7 @@ static FilePath fallbackClangdFilePath()
{
if (g_defaultClangdFilePath.exists())
return g_defaultClangdFilePath;
return "clangd";
return Environment::systemEnvironment().searchInPath("clangd");
}
void CppCodeModelSettings::fromSettings(QSettings *s)

View File

@@ -26,16 +26,13 @@
#include "cppeditordocument.h"
#include "baseeditordocumentparser.h"
#include "builtineditordocumentprocessor.h"
#include "cppcodeformatter.h"
#include "cppcodemodelsettings.h"
#include "cppeditorconstants.h"
#include "cppeditorplugin.h"
#include "cppmodelmanager.h"
#include "cppeditorconstants.h"
#include "cppeditorplugin.h"
#include "cpphighlighter.h"
#include "cppqtstyleindenter.h"
#include "cppquickfixassistant.h"
#include <coreplugin/editormanager/editormanager.h>
@@ -58,20 +55,17 @@
const char NO_PROJECT_CONFIGURATION[] = "NoProject";
namespace {
CppEditor::CppModelManager *mm()
{
return CppEditor::CppModelManager::instance();
}
} // anonymous namespace
using namespace TextEditor;
using namespace Utils;
namespace CppEditor {
namespace Internal {
static CppEditor::CppModelManager *mm()
{
return CppEditor::CppModelManager::instance();
}
enum { processDocumentIntervalInMs = 150 };
class CppEditorDocumentHandleImpl : public CppEditorDocumentHandle
@@ -150,24 +144,12 @@ void CppEditorDocument::setCompletionAssistProvider(TextEditor::CompletionAssist
m_completionAssistProvider = nullptr;
}
void CppEditorDocument::setFunctionHintAssistProvider(TextEditor::CompletionAssistProvider *provider)
{
TextDocument::setFunctionHintAssistProvider(provider);
m_functionHintAssistProvider = nullptr;
}
CompletionAssistProvider *CppEditorDocument::completionAssistProvider() const
{
return m_completionAssistProvider
? m_completionAssistProvider : TextDocument::completionAssistProvider();
}
CompletionAssistProvider *CppEditorDocument::functionHintAssistProvider() const
{
return m_functionHintAssistProvider
? m_functionHintAssistProvider : TextDocument::functionHintAssistProvider();
}
TextEditor::IAssistProvider *CppEditorDocument::quickFixAssistProvider() const
{
if (const auto baseProvider = TextDocument::quickFixAssistProvider())
@@ -223,7 +205,6 @@ void CppEditorDocument::onMimeTypeChanged()
m_isObjCEnabled = (mt == QLatin1String(Constants::OBJECTIVE_C_SOURCE_MIMETYPE)
|| mt == QLatin1String(Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE));
m_completionAssistProvider = mm()->completionAssistProvider();
m_functionHintAssistProvider = mm()->functionHintAssistProvider();
initializeTimer();
}
@@ -258,14 +239,13 @@ void CppEditorDocument::reparseWithPreferredParseContext(const QString &parseCon
scheduleProcessDocument();
}
void CppEditorDocument::onFilePathChanged(const Utils::FilePath &oldPath,
const Utils::FilePath &newPath)
void CppEditorDocument::onFilePathChanged(const FilePath &oldPath, const FilePath &newPath)
{
Q_UNUSED(oldPath)
if (!newPath.isEmpty()) {
indenter()->setFileName(newPath);
setMimeType(Utils::mimeTypeForFile(newPath.toFileInfo()).name());
setMimeType(mimeTypeForFile(newPath).name());
connect(this, &Core::IDocument::contentsChanged,
this, &CppEditorDocument::scheduleProcessDocument,
@@ -376,13 +356,13 @@ void CppEditorDocument::releaseResources()
void CppEditorDocument::showHideInfoBarAboutMultipleParseContexts(bool show)
{
const Utils::Id id = Constants::MULTIPLE_PARSE_CONTEXTS_AVAILABLE;
const Id id = Constants::MULTIPLE_PARSE_CONTEXTS_AVAILABLE;
if (show) {
Utils::InfoBarEntry info(id,
tr("Note: Multiple parse contexts are available for this file. "
"Choose the preferred one from the editor toolbar."),
Utils::InfoBarEntry::GlobalSuppression::Enabled);
InfoBarEntry info(id,
tr("Note: Multiple parse contexts are available for this file. "
"Choose the preferred one from the editor toolbar."),
InfoBarEntry::GlobalSuppression::Enabled);
info.removeCancelButton();
if (infoBar()->canInfoBeAdded(id))
infoBar()->addInfo(info);
@@ -457,9 +437,9 @@ TextEditor::TabSettings CppEditorDocument::tabSettings() const
return indenter()->tabSettings().value_or(TextEditor::TextDocument::tabSettings());
}
bool CppEditorDocument::save(QString *errorString, const Utils::FilePath &filePath, bool autoSave)
bool CppEditorDocument::save(QString *errorString, const FilePath &filePath, bool autoSave)
{
Utils::ExecuteOnDestruction resetSettingsOnScopeExit;
ExecuteOnDestruction resetSettingsOnScopeExit;
if (indenter()->formatOnSave() && !autoSave) {
auto *layout = qobject_cast<TextEditor::TextDocumentLayout *>(document()->documentLayout());

View File

@@ -51,9 +51,7 @@ public:
bool isObjCEnabled() const;
void setCompletionAssistProvider(TextEditor::CompletionAssistProvider *provider) override;
void setFunctionHintAssistProvider(TextEditor::CompletionAssistProvider *provider) override;
TextEditor::CompletionAssistProvider *completionAssistProvider() const override;
TextEditor::CompletionAssistProvider *functionHintAssistProvider() const override;
TextEditor::IAssistProvider *quickFixAssistProvider() const override;
void recalculateSemanticInfoDetached();
@@ -128,7 +126,6 @@ private:
QScopedPointer<BaseEditorDocumentProcessor> m_processor;
CppCompletionAssistProvider *m_completionAssistProvider = nullptr;
CppCompletionAssistProvider *m_functionHintAssistProvider = nullptr;
// (Un)Registration in CppModelManager
QScopedPointer<CppEditorDocumentHandle> m_editorDocumentHandle;

View File

@@ -632,25 +632,6 @@ CPlusPlus::Symbol *CppFindReferences::findSymbol(const CppFindReferencesParamete
return nullptr;
}
Utils::optional<QString> getContainingFunctionName(const Utils::FilePath &fileName,
int line,
int column)
{
const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot();
auto document = snapshot.document(fileName);
// context properties need lookup inside function scope, and thus require a full check
CPlusPlus::Document::Ptr localDoc = document;
if (document->checkMode() != CPlusPlus::Document::FullCheck) {
localDoc = snapshot.documentFromSource(document->utf8Source(), document->fileName());
localDoc->check();
}
auto funcName = localDoc->functionAt(line, column);
return funcName.size() ? Utils::make_optional(funcName) : Utils::nullopt;
}
static void displayResults(SearchResult *search,
QFutureWatcher<CPlusPlus::Usage> *watcher,
int first,
@@ -665,24 +646,11 @@ static void displayResults(SearchResult *search,
item.setMainRange(result.line, result.col, result.len);
item.setLineText(result.lineText);
item.setUserData(int(result.type));
item.setContainingFunctionName(result.containingFunction);
item.setStyle(colorStyleForUsageType(result.type));
item.setUseTextEditorFont(true);
if (search->supportsReplace())
item.setSelectForReplacement(SessionManager::projectForFile(result.path));
// In case we're looking for a function, we need to look at the symbol near the end. This
// is needed to avoid following corner-cases:
// 1) if we're looking at the beginning of the function declaration, we can get the
// declaration of the previous function
// 2) if we're looking somewhere at the middle of the function declaration, we can still
// get the declaration of the previous function if the cursor is located at the
// namespace declaration, i.e. CppReference>|<s::findUsages
const auto containingFunctionName = getContainingFunctionName(result.path,
result.line,
(result.col + result.len) - 1);
item.setContainingFunctionName(containingFunctionName);
search->addResult(item);
if (parameters.prettySymbolName.isEmpty())
@@ -777,7 +745,7 @@ restart_search:
if (macro.name() == useMacro.name()) {
unsigned column;
const QString &lineSource = matchingLine(use.bytesBegin(), source, &column);
usages.append(CPlusPlus::Usage(fileName, lineSource,
usages.append(CPlusPlus::Usage(fileName, lineSource, {},
CPlusPlus::Usage::Type::Other, use.beginLine(),
column, useMacro.nameToQString().size()));
}

View File

@@ -1620,11 +1620,6 @@ CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const
return d->m_builtinModelManagerSupport->completionAssistProvider();
}
CppCompletionAssistProvider *CppModelManager::functionHintAssistProvider() const
{
return d->m_builtinModelManagerSupport->functionHintAssistProvider();
}
TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const
{
return d->m_builtinModelManagerSupport->createHoverHandler();

View File

@@ -162,7 +162,6 @@ public:
void activateClangCodeModel(ModelManagerSupportProvider *modelManagerSupportProvider);
CppCompletionAssistProvider *completionAssistProvider() const;
CppCompletionAssistProvider *functionHintAssistProvider() const;
BaseEditorDocumentProcessor *createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) const;
TextEditor::BaseHoverHandler *createHoverHandler() const;

View File

@@ -57,7 +57,6 @@ public:
virtual ~ModelManagerSupport() = 0;
virtual CppCompletionAssistProvider *completionAssistProvider() = 0;
virtual CppCompletionAssistProvider *functionHintAssistProvider() = 0;
virtual TextEditor::BaseHoverHandler *createHoverHandler() = 0;
virtual BaseEditorDocumentProcessor *createEditorDocumentProcessor(
TextEditor::TextDocument *baseTextDocument) = 0;

View File

@@ -1745,6 +1745,16 @@ void QuickfixTest::testGeneric_data()
<< CppQuickFixFactoryPtr(new EscapeStringLiteral)
<< _(R"(const char *str = @"\xc3\xa0""f23\xd0\xb1g\xd0\xb1""1";)")
<< _(R"(const char *str = "àf23бgб1";)");
QTest::newRow("AddLocalDeclaration_QTCREATORBUG-26004")
<< CppQuickFixFactoryPtr(new AddLocalDeclaration)
<< _("void func() {\n"
" QStringList list;\n"
" @it = list.cbegin();\n"
"}\n")
<< _("void func() {\n"
" QStringList list;\n"
" auto it = list.cbegin();\n"
"}\n");
}
void QuickfixTest::testGeneric()

Some files were not shown because too many files have changed in this diff Show More