Merge remote-tracking branch 'origin/4.3'

Conflicts:
	src/plugins/genericprojectmanager/genericproject.cpp
	src/plugins/genericprojectmanager/genericproject.h
	src/plugins/genericprojectmanager/genericprojectnodes.cpp
	src/plugins/genericprojectmanager/genericprojectnodes.h

Change-Id: Ie0c870f68c8d200a75489b75860987655b2f6175
This commit is contained in:
Eike Ziller
2017-04-19 09:56:14 +02:00
232 changed files with 3919 additions and 1830 deletions
+2 -3
View File
@@ -110,9 +110,8 @@ phony.c
/dist/gdb/qtcreator-*/
/dist/gdb/source/
/dist/gdb/staging/
/doc/api/html/
/doc/html-dev/
/doc/html/
/doc/qtcreator/
/doc/qtcreator-dev/
/doc/pluginhowto/html/
/lib/
/lib64/
+2 -1
View File
@@ -21,7 +21,8 @@ Editing
All Projects
* Added support for `.qrc` files in project tree for all projects
* Added Qt Creator variable `CurrentRun:Runnable:FilePath`
* Added Qt Creator variable `CurrentRun:Executable` (QTCREATORBUG-12201,
QTCREATORBUG-16830)
* Added choice of build system to most project wizards (QTCREATORBUG-17308)
QMake Projects
+2 -2
View File
@@ -34,8 +34,8 @@
is not sufficient for your case, you can write wizards in code.
A wizard in \QC is an instance of a class implementing
the Core::IWizardFactory interface that is registered with
ExtensionSystem::PluginManager.
the Core::IWizardFactory interface that has a creator function registered
with IWizardFactory::registerFactoryCreator.
Implementing wizards requires:
\list
@@ -58,10 +58,13 @@
\image qtcreator-gs-build-example-open.png "Selecting an example"
If no examples are listed, check that a \l{Adding Qt Versions}
{Qt version} is installed and configured.
\li Select an example in the list of examples.
You can also search for examples. Enter the \uicontrol android or
\uicontrol iOS keyword in the search field (2) to list all the
\uicontrol ios keyword in the search field (2) to list all the
examples tested for Android or iOS. To list examples that you can
run on embedded devices, enter the \uicontrol Boot2Qt keyword in the
search field (commercial only).
@@ -83,8 +86,8 @@
\li To see the compilation progress, press \key{Alt+4} to open the
\uicontrol {Compile Output} pane.
If build errors occur, check that a \l{Adding Qt Versions}
{Qt version} and \l{Adding Compilers}{compiler} are installed and
If build errors occur, check that a Qt version and
\l{Adding Compilers}{compiler} are installed and
configured and that the necessary kits are configured. If you are
building for an \l{Connecting Android Devices}{Android device} or
\l{Connecting iOS Devices}{iOS device}, check that the development
+8 -7
View File
@@ -79,15 +79,16 @@ exists(src/shared/qbs/qbs.pro) {
}
# Create qbs documentation targets.
QBS_DOCS_BUILD_DIR=$$IDE_DOC_PATH
QBS_HTML_DOC_PATH=$$OUT_PWD/doc/html-qbs
QBS_DOCS_INSTALL_DIR=$$INSTALL_DOC_PATH
QDOC_BIN = $$QDOC
HELPGENERATOR = $$QHELPGENERATOR
include(src/shared/qbs/qbs_version.pri)
DOC_FILES =
DOC_TARGET_PREFIX = qbs_
include(src/shared/qbs/doc/doc_shared.pri)
include(src/shared/qbs/doc/doc_targets.pri)
docs.depends += qbs_docs
install_docs.depends += install_inst_qbs_qch_docs
!build_online_docs {
install_docs.depends += install_qbs_docs
}
unset(DOC_FILES)
unset(DOC_TARGET_PREFIX)
}
contains(QT_ARCH, i386): ARCHITECTURE = x86
+2 -2
View File
@@ -134,9 +134,9 @@ if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then
qbsapp="$app_path/Contents/MacOS/qbs"
echo "- Running macdeployqt ($(which macdeployqt))"
echo "- Running macdeployqt ($bin_src/macdeployqt)"
macdeployqt "$app_path" \
"$bin_src/macdeployqt" "$app_path" \
"-executable=$app_path/Contents/MacOS/qtdiag" \
"-executable=$resource_path/qtpromaker" \
"-executable=$resource_path/sdktool" \
+5 -4
View File
@@ -125,7 +125,6 @@ class Dumper(DumperBase):
pass
val.isBaseClass = val.name == val.type.name
val.nativeValue = nativeValue
val.lIsInScope = True
val.laddress = nativeValue.address()
return val
@@ -405,12 +404,12 @@ class Dumper(DumperBase):
return cdbext.lookupType(name, module)
def reportResult(self, result, args):
self.report('result={%s}' % (result))
cdbext.reportResult('result={%s}' % result)
def readRawMemory(self, address, size):
mem = cdbext.readRawMemory(address, size)
if len(mem) != size:
raise Exception("Invalid memory request")
raise Exception("Invalid memory request: %d bytes from 0x%x" % (size, address))
return mem
def findStaticMetaObject(self, type):
@@ -440,7 +439,9 @@ class Dumper(DumperBase):
variables = []
for val in cdbext.listOfLocals(self.partialVariable):
variables.append(self.fromNativeValue(val))
dumperVal = self.fromNativeValue(val)
dumperVal.lIsInScope = not dumperVal.name in self.uninitialized
variables.append(dumperVal)
self.handleLocals(variables)
self.handleWatches(args)
+5 -21
View File
@@ -317,6 +317,8 @@ class DumperBase:
self.nativeMixed = int(args.get('nativemixed', '0'))
self.autoDerefPointers = int(args.get('autoderef', '0'))
self.partialVariable = args.get('partialvar', '')
self.uninitialized = args.get('uninitialized', [])
self.uninitialized = list(map(lambda x: self.hexdecode(x), self.uninitialized))
self.partialUpdate = int(args.get('partial', '0'))
self.fallbackQtVersion = 0x50200
#warn('NAMESPACE: "%s"' % self.qtNamespace())
@@ -1497,7 +1499,10 @@ class DumperBase:
def getJumpAddress_x86(dumper, address):
relativeJumpCode = 0xe9
jumpCode = 0xff
try:
data = dumper.readRawMemory(address, 6)
except:
return 0
primaryOpcode = data[0]
if primaryOpcode == relativeJumpCode:
# relative jump on 32 and 64 bit with a 32bit offset
@@ -3467,27 +3472,6 @@ class DumperBase:
def target(self):
return self.typeData().ltarget
def field(self, value, name, bitoffset = 0):
#warn('GETTING FIELD %s FOR: %s' % (name, self.name))
for f in self.fields(value):
#warn('EXAMINING MEMBER %s' % f.name)
if f.name == name:
ff = copy.copy(f)
if ff.lbitpos is None:
ff.lbitpos = bitoffset
else:
ff.lbitpos += bitoffset
#warn('FOUND: %s' % ff)
return ff
if f.isBaseClass:
#warn('EXAMINING BASE %s' % f.type)
res = f.type.field(name, bitoffset + f.bitpos())
if res is not None:
return res
#warn('FIELD %s NOT FOUND IN %s' % (name, self))
return None
def stripTypedefs(self):
if isinstance(self, self.dumper.Type) and self.code != TypeCodeTypedef:
#warn('NO TYPEDEF: %s' % self)
+3
View File
@@ -811,7 +811,10 @@ class Dumper(DumperBase):
self.nativeMixed = int(args.get('nativemixed', 0))
self.workingDirectory_ = args.get('workingdirectory', '')
if self.workingDirectory_ == '':
try:
self.workingDirectory_ = os.getcwd()
except: # Could have been deleted in the mean time.
pass
self.ignoreStops = 0
self.silentStops = 0
+25 -3
View File
@@ -642,10 +642,10 @@ def qdump__QFiniteStack(d, value):
def qdump__QFlags(d, value):
i = value.split('{int}')[0]
enumType = value.type[0]
if d.isLldb:
d.putValue(i.cast(enumType.name).display())
else:
if d.isGdb:
d.putValue(i.cast('enum ' + enumType.name).display())
else:
d.putValue(i.cast(enumType.name).display())
d.putNumChild(0)
@@ -2758,3 +2758,25 @@ def qdump__QSqlField(d, value):
qdump__QVariant(d, val)
d.putBetterType(d.currentType.value.replace('QVariant', 'QSqlField'))
d.putPlainChildren(value)
def qdump__qfloat16(d, value):
h = value.split('H')[0]
# Stole^H^H^HHeavily inspired by J.F. Sebastian at
# http://math.stackexchange.com/questions/1128204/how-to-convert-
# from-floating-point-binary-to-decimal-in-half-precision16-bits
sign = h >> 15
exp = (h >> 10) & 0b011111
fraction = h & (2**10 - 1)
if exp == 0:
if fraction == 0:
res = -0.0 if sign else 0.0
else:
res = (-1)**sign * fraction / 2**10 * 2**(-14) # subnormal
elif exp == 0b11111:
res = ('-inf' if sign else 'inf') if fraction == 0 else 'nan'
else:
res = (-1)**sign * (1 + fraction / 2**10) * 2**(exp - 15)
d.putValue(res)
d.putNumChild(1)
d.putPlainChildren(value)
@@ -420,7 +420,7 @@ void ObjectNodeInstance::setPropertyVariant(const PropertyName &name, const QVar
if (oldValue.type() == QVariant::Url) {
QUrl url = oldValue.toUrl();
QString path = url.toLocalFile();
if (QFileInfo(path).exists() && nodeInstanceServer() && !path.isEmpty())
if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty())
nodeInstanceServer()->removeFilePropertyFromFileSystemWatcher(object(), name, path);
}
@@ -437,7 +437,7 @@ void ObjectNodeInstance::setPropertyVariant(const PropertyName &name, const QVar
if (newValue.type() == QVariant::Url) {
QUrl url = newValue.toUrl();
QString path = url.toLocalFile();
if (QFileInfo(path).exists() && nodeInstanceServer() && !path.isEmpty())
if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty())
nodeInstanceServer()->addFilePropertyToFileSystemWatcher(object(), name, path);
}
}
@@ -678,7 +678,7 @@ static inline QString fixComponentPathForIncompatibleQt(const QString &component
const QString relativeImportPath = componentPath.right(componentPath.length() - index);
QString fixedComponentPath = QLibraryInfo::location(QLibraryInfo::ImportsPath) + relativeImportPath;
fixedComponentPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
if (QFileInfo(fixedComponentPath).exists())
if (QFileInfo::exists(fixedComponentPath))
return fixedComponentPath;
QString fixedPath = QFileInfo(fixedComponentPath).path();
if (fixedPath.endsWith(QLatin1String(".1.0"))) {
@@ -389,7 +389,7 @@ QVariant fixResourcePaths(const QVariant &value)
if (qrcDefintion.count() == 2) {
QString fixedPath = path;
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
if (QFileInfo(fixedPath).exists()) {
if (QFileInfo::exists(fixedPath)) {
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
return QUrl::fromLocalFile(fixedPath);
@@ -410,7 +410,7 @@ QVariant fixResourcePaths(const QVariant &value)
if (qrcDefintion.count() == 2) {
QString fixedPath = str;
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
if (QFileInfo(fixedPath).exists()) {
if (QFileInfo::exists(fixedPath)) {
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
return fixedPath;
@@ -102,7 +102,7 @@ QVariant fixResourcePaths(const QVariant &value)
if (qrcDefintion.count() == 2) {
QString fixedPath = path;
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
if (QFileInfo(fixedPath).exists()) {
if (QFileInfo::exists(fixedPath)) {
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
return QUrl::fromLocalFile(fixedPath);
@@ -123,7 +123,7 @@ QVariant fixResourcePaths(const QVariant &value)
if (qrcDefintion.count() == 2) {
QString fixedPath = str;
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
if (QFileInfo(fixedPath).exists()) {
if (QFileInfo::exists(fixedPath)) {
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
return QUrl::fromLocalFile(fixedPath);
+1
View File
@@ -181,6 +181,7 @@ QVariant OverviewModel::data(const QModelIndex &index, int role) const
if (Template *t = symbol->asTemplate())
if (Symbol *templateDeclaration = t->declaration()) {
QStringList parameters;
parameters.reserve(t->templateParameterCount());
for (unsigned i = 0; i < t->templateParameterCount(); ++i)
parameters.append(_overview.prettyName(t->templateParameterAt(i)->name()));
name += QLatin1Char('<') + parameters.join(QLatin1String(", ")) + QLatin1Char('>');
+1 -1
View File
@@ -361,7 +361,7 @@ QReadWriteLock *PluginManager::listLock()
*/
void PluginManager::loadPlugins()
{
return d->loadPlugins();
d->loadPlugins();
}
/*!
+4
View File
@@ -155,6 +155,10 @@ int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject,
}
}
// Root object: attribute all remaining width to "others"
if (!parentIndex.isValid())
skipped = parentSize - position;
if (skipped > 0) {
appendChild(parentObject, parentItem, context, QModelIndex(), position / parentSize,
skipped / parentSize);
@@ -147,7 +147,7 @@ void StereotypeDisplayVisitor::visitDItem(const DItem *item)
m_stereotypeSmartDisplay = DObject::StereotypeIcon;
visitDObject(item);
if (m_stereotypeIconId.isEmpty() && !item->shape().isEmpty())
m_shapeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, QStringList(item->shape()));
m_stereotypeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, QStringList(item->shape()));
if (m_shapeIconId.isEmpty() && !item->variety().isEmpty())
m_shapeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, QStringList(item->variety()));
}
@@ -768,7 +768,7 @@ void PropertiesView::MView::visitMAssociation(const MAssociation *association)
this, &PropertiesView::MView::onAssociationEndBKindChanged);
}
if (isSingleSelection) {
if ((!isValidAssociationKindIndex(m_endAKind->currentIndex())
if ((!isValidAssociationKindIndex(m_endBKind->currentIndex())
|| association->endB().kind() != translateIndexToAssociationKind(m_endBKind->currentIndex()))
&& !m_endBKind->hasFocus()) {
m_endBKind->setCurrentIndex(translateAssociationKindToIndex(association->endB().kind()));
@@ -193,6 +193,7 @@ void BaseEngineDebugClient::messageReceived(const QByteArray &data)
int count;
ds >> count;
QList<EngineReference> engines;
engines.reserve(count);
for (int ii = 0; ii < count; ++ii) {
EngineReference eng;
ds >> eng.m_name;
@@ -482,7 +482,7 @@ void ContextPaneWidgetImage::setPixmap(const QString &fileName)
if (m_borderImage) {
QString localFileName(fileName);
if (QFile(fileName).exists()) {
if (QFileInfo::exists(fileName)) {
if (fileName.endsWith(QLatin1String("sci"))) {
QString pixmapFileName;
int left = 0;
@@ -527,7 +527,7 @@ void ContextPaneWidgetImage::setPixmap(const QString &fileName)
}
uiBorderImage->label->setPixmap(pix);
} else {
if (QFile(fileName).exists()) {
if (QFileInfo::exists(fileName)) {
QPixmap source(fileName);
previewDialog()->setPixmap(source, 1);
ui->sizeLabel->setText(QString::number(source.width()) + QLatin1Char('x') + QString::number(source.height()));
@@ -703,8 +703,10 @@ static inline bool rangeCheck(int target, int pos)
void PreviewLabel::mousePressEvent(QMouseEvent * event)
{
if (!m_borderImage)
return QLabel::mouseMoveEvent(event);
if (!m_borderImage) {
QLabel::mouseMoveEvent(event);
return;
}
bool bottom = false;
@@ -790,8 +792,10 @@ static inline int limitPositive(int i)
void PreviewLabel::mouseMoveEvent(QMouseEvent * event)
{
if (!m_borderImage)
return QLabel::mouseMoveEvent(event);
if (!m_borderImage) {
QLabel::mouseMoveEvent(event);
return;
}
QPoint p = event->pos();
bool bottom = false;
+1 -1
View File
@@ -223,7 +223,7 @@ public:
static void accept(Node *node, Visitor *visitor);
inline static void acceptChild(Node *node, Visitor *visitor)
{ return accept(node, visitor); } // ### remove
{ accept(node, visitor); } // ### remove
virtual void accept0(Visitor *visitor) = 0;
virtual SourceLocation firstSourceLocation() const = 0;
+2 -3
View File
@@ -124,7 +124,7 @@ public:
fileName.prepend(QLatin1Char('/'));
fileName.prepend(_doc->path());
}
if (!QFileInfo(fileName).exists())
if (!QFileInfo::exists(fileName))
setMessage(WarnFileOrDirectoryDoesNotExist);
}
}
@@ -556,8 +556,7 @@ public:
"Scale",
"Translate",
"Package",
"Particles",
"Dialog"})
"Particles"})
{
}
+1
View File
@@ -59,6 +59,7 @@ enum Enum
Hint, // cosmetic or convention
MaybeWarning, // possibly a warning, insufficient information
Warning, // could cause unintended behavior
ReadingTypeInfoWarning, // currently dumping type information
MaybeError, // possibly an error, insufficient information
Error // definitely an error
};
-2
View File
@@ -325,7 +325,6 @@ void DescribeValueVisitor::visit(const ObjectValue *value)
basicDump("ObjectValue", value, printDetail);
}
if (printDetail) {
if (value) {
dumpNewline();
dump("className:");
dump(value->className());
@@ -338,7 +337,6 @@ void DescribeValueVisitor::visit(const ObjectValue *value)
dumpNewline();
dump("prototype:");
(*this)(value->prototype());
}
closeContext();
}
--m_depth;
+3 -2
View File
@@ -453,8 +453,9 @@ bool LinkPrivate::importLibrary(Document::Ptr doc,
}
}
if (errorLoc.isValid()) {
warning(doc, errorLoc,
Link::tr("QML module contains C++ plugins, currently reading type information..."));
appendDiagnostic(doc, DiagnosticMessage(Severity::ReadingTypeInfoWarning,
errorLoc,
Link::tr("QML module contains C++ plugins, currently reading type information...")));
import->valid = false;
}
} else if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::DumpError
@@ -782,7 +782,8 @@ static bool findNewQmlLibraryInPath(const QString &path,
}
// found a new library!
qmldirFile.open(QFile::ReadOnly);
if (!qmldirFile.open(QFile::ReadOnly))
return false;
QString qmldirData = QString::fromUtf8(qmldirFile.readAll());
QmlDirParser qmldirParser;
+5 -1
View File
@@ -150,8 +150,12 @@ protected:
void outCommentText(const QString &str)
{
QStringList lines = str.split(QLatin1Char('\n'));
bool multiline = lines.length() > 1;
for (int i = 0; i < lines.size(); ++i) {
if (multiline)
_line = lines.at(i); // multiline comments don't keep track of previos lines
else
_line += lines.at(i);
if (i != lines.size() - 1)
newLine();
}
@@ -582,7 +586,7 @@ protected:
out(ast->identifierToken);
}
} else { // signal
out("signal ");
out("signal ", ast->identifierToken);
out(ast->identifierToken);
if (ast->parameters) {
out("(");
@@ -641,7 +641,9 @@ void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMe
void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme)
{
if (!ast || !ast->statement) {
if (!ast)
return;
if (!ast->statement) {
addError(ast->colonToken, tr("Expected object literal after colon."));
return;
}
@@ -40,6 +40,7 @@
#include <iterator>
static CurrentSymbolGroup currentSymbolGroup;
static std::string results;
CurrentSymbolGroup::~CurrentSymbolGroup()
{
@@ -338,6 +339,16 @@ static PyObject *cdbext_call(PyObject *, PyObject *args)
return createPythonObject(PyValue(index, symbolGroup));
}
static PyObject *cdbext_reportResult(PyObject *, PyObject *args)
{
char *result;
if (!PyArg_ParseTuple(args, "s", &result))
Py_RETURN_NONE;
results += result;
Py_RETURN_NONE;
}
static PyMethodDef cdbextMethods[] = {
{"parseAndEvaluate", cdbext_parseAndEvaluate, METH_VARARGS,
"Returns value of expression or None if the expression can not be resolved"},
@@ -361,6 +372,8 @@ static PyMethodDef cdbextMethods[] = {
"Creates a value with the given type at the given address"},
{"call", cdbext_call, METH_VARARGS,
"Call a function and return a cdbext.Value representing the return value of that function."},
{"reportResult", cdbext_reportResult, METH_VARARGS,
"Adds a result"},
{NULL, NULL, 0,
NULL} /* Sentinel */
};
@@ -420,3 +433,25 @@ int pointerSize()
{
return ExtensionCommandContext::instance()->control()->IsPointer64Bit() == S_OK ? 8 : 4;
}
std::string collectOutput()
{
// construct a gdbmi output string with two children: messages and result
std::stringstream ret;
ret << "output=[msg=[";
std::istringstream pyStdout(getPyStdout());
std::string line;
// Add a child to messages for every line.
while (std::getline(pyStdout, line)) {
// there are two kinds of messages we want to handle here:
if (line.find("bridgemessage=") == 0) { // preformatted gdmi bridgemessages from warn()
ret << line << ',';
} else { // and a line of "normal" python output
replace(line, '"', '$'); // otherwise creators gdbmi parser would fail
ret << "line=\"" << line << "\",";
}
}
ret << "]," << results << "]";
results.clear();
return ret.str();
}
@@ -27,11 +27,13 @@
#include <Python.h>
#include <vector>
#include <string>
#include "dbgeng.h"
void initCdbextPythonModule();
int pointerSize();
std::string collectOutput();
constexpr bool debugPyCdbextModule = false;
using Bytes = std::vector<char>;
@@ -34,6 +34,7 @@
#ifdef WITH_PYTHON
#include <Python.h>
#include "pystdoutredirect.h"
#include "pycdbextmodule.h"
#endif
#include <cstdio>
@@ -588,7 +589,7 @@ extern "C" HRESULT CALLBACK script(CIDebugClient *client, PCSTR argsIn)
const char result = (PyRun_SimpleString(command.str().c_str()) == 0) ? 'R' : 'N';
if (PyErr_Occurred())
PyErr_Print();
ExtensionContext::instance().reportLong(result, token, "script", getPyStdout().c_str());
ExtensionContext::instance().reportLong(result, token, "script", collectOutput().c_str());
endCapturePyStdout();
PyErr_Restore(ptype, pvalue, ptraceback);
#else
@@ -849,11 +850,16 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
const std::string iname = tokens.front().substr(0, equalsPos);
const std::string value = tokens.front().substr(equalsPos + 1, tokens.front().size() - equalsPos - 1);
// get the symbolgroup
const int currentFrame = ExtensionContext::instance().symbolGroupFrame();
int currentFrame = ExtensionContext::instance().symbolGroupFrame();
if (currentFrame < 0) {
CIDebugControl *control = ExtensionCommandContext::instance()->control();
DEBUG_STACK_FRAME frame;
if (FAILED(control->GetStackTrace(0, 0, 0, &frame, 1, NULL))) {
errorMessage = "No current frame.";
break;
}
currentFrame = frame.FrameNumber;
}
SymbolGroup *symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), currentFrame, &errorMessage);
if (!symGroup)
break;
+5 -2
View File
@@ -465,8 +465,11 @@ bool SymbolGroup::assign(const std::string &nodeName,
return false;
}
return (node->dumperType() & KT_Editable) ? // Edit complex types
assignType(node, valueEncoding, value, ctx, errorMessage) :
int kt = node->dumperType();
if (kt < 0)
kt = knownType(node->type(), KnownTypeAutoStripPointer | KnownTypeHasClassPrefix);
return (kt & KT_Editable) ? // Edit complex types
assignType(node, kt, valueEncoding, value, ctx, errorMessage) :
node->assign(value, errorMessage);
}
@@ -3552,17 +3552,17 @@ static inline bool assignStdString(SymbolGroupNode *n,
return true;
}
bool assignType(SymbolGroupNode *n, int valueEncoding, const std::string &value,
bool assignType(SymbolGroupNode *n, int knownType, int valueEncoding, const std::string &value,
const SymbolGroupValueContext &ctx, std::string *errorMessage)
{
switch (n->dumperType()) {
switch (knownType) {
case KT_QString:
return assignQString(n, valueEncoding, value, ctx, errorMessage);
case KT_QByteArray:
return assignQByteArray(n, valueEncoding, value, ctx, errorMessage);
case KT_StdString:
case KT_StdWString:
return assignStdString(n, n->dumperType(), valueEncoding, value, ctx, errorMessage);
return assignStdString(n, knownType, valueEncoding, value, ctx, errorMessage);
default:
break;
}
+1 -1
View File
@@ -286,7 +286,7 @@ enum AssignEncoding
AssignHexEncodedUtf16
};
bool assignType(SymbolGroupNode *n, int valueEncoding, const std::string &value,
bool assignType(SymbolGroupNode *n, int knownType, int valueEncoding, const std::string &value,
const SymbolGroupValueContext &ctx,
std::string *errorMessage);
+1 -1
View File
@@ -92,7 +92,7 @@ Rectangle {
// This is called from outside to synchronize the timeline to other views
function selectByTypeId(typeId)
{
if (lockItemSelection || typeId === -1)
if (lockItemSelection || typeId === -1 || content.typeId === typeId)
return;
var itemIndex = -1;
+5 -1
View File
@@ -24,6 +24,7 @@
****************************************************************************/
#include "appmainwindow.h"
#include "theme/theme_p.h"
#ifdef Q_OS_WIN
#include <windows.h>
@@ -60,11 +61,14 @@ void AppMainWindow::raiseWindow()
#ifdef Q_OS_WIN
bool AppMainWindow::event(QEvent *event)
{
if (event->type() == m_deviceEventId) {
const QEvent::Type type = event->type();
if (type == m_deviceEventId) {
event->accept();
emit deviceChange();
return true;
}
if (type == QEvent::ThemeChange)
setThemeApplicationPalette();
return QMainWindow::event(event);
}
+1 -1
View File
@@ -62,7 +62,7 @@ void CompletingLineEdit::keyPressEvent(QKeyEvent *e)
comp->complete();
}
}
return QLineEdit::keyPressEvent(e);
QLineEdit::keyPressEvent(e);
}
} // namespace Utils
+3 -2
View File
@@ -31,6 +31,7 @@
#include <utils/qtcassert.h>
#include <QCoreApplication>
#include <QFileInfo>
#include <QSettings>
#include <QTimer>
@@ -359,7 +360,7 @@ QString ConsoleProcess::defaultTerminalEmulator()
{
if (HostOsInfo::isMacHost()) {
QString termCmd = QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/scripts/openTerminal.command");
if (QFile(termCmd).exists())
if (QFileInfo::exists(termCmd))
return termCmd.replace(QLatin1Char(' '), QLatin1String("\\ "));
return QLatin1String("/usr/X11/bin/xterm");
}
@@ -407,7 +408,7 @@ QString ConsoleProcess::terminalEmulator(const QSettings *settings, bool nonEmpt
void ConsoleProcess::setTerminalEmulator(QSettings *settings, const QString &term)
{
return settings->setValue(QLatin1String("General/TerminalEmulator"), term);
settings->setValue(QLatin1String("General/TerminalEmulator"), term);
}
bool ConsoleProcess::startTerminalEmulator(QSettings *settings, const QString &workingDir)
+1
View File
@@ -365,6 +365,7 @@ void CrumblePath::resizeButtons()
// compute relative sizes
QList<int> sizes;
int totalSize = 0;
sizes.reserve(m_buttons.length());
for (int i = 0; i < m_buttons.length() ; ++i) {
CrumblePathButton *button = m_buttons.at(i);
+1 -1
View File
@@ -56,7 +56,7 @@ static QString appBundleExpandedPath(const QString &path)
QFileInfo info(path);
if (info.isDir()) {
QString exePath = path + QLatin1String("/Contents/MacOS/") + info.completeBaseName();
if (QFileInfo(exePath).exists())
if (QFileInfo::exists(exePath))
return exePath;
}
}
+7 -2
View File
@@ -57,14 +57,19 @@ Theme *proxyTheme()
return new Theme(m_creatorTheme);
}
void setThemeApplicationPalette()
{
if (m_creatorTheme && m_creatorTheme->flag(Theme::ApplyThemePaletteGlobally))
QApplication::setPalette(m_creatorTheme->palette());
}
void setCreatorTheme(Theme *theme)
{
if (m_creatorTheme == theme)
return;
delete m_creatorTheme;
m_creatorTheme = theme;
if (theme && theme->flag(Theme::ApplyThemePaletteGlobally))
QApplication::setPalette(theme->palette());
setThemeApplicationPalette();
}
Theme::Theme(const QString &id, QObject *parent)
+1
View File
@@ -51,5 +51,6 @@ public:
};
QTCREATOR_UTILS_EXPORT void setCreatorTheme(Theme *theme);
QTCREATOR_UTILS_EXPORT void setThemeApplicationPalette();
} // namespace Utils
+2 -4
View File
@@ -919,11 +919,9 @@ QModelIndex BaseTreeModel::parent(const QModelIndex &idx) const
if (!grandparent)
return QModelIndex();
for (int i = 0, n = grandparent->childCount(); i < n; ++i)
if (grandparent->childAt(i) == parent)
// This is on the performance-critical path for ItemViewFind.
const int i = grandparent->m_children.indexOf(parent);
return createIndex(i, 0, static_cast<void*>(parent));
return QModelIndex();
}
int BaseTreeModel::rowCount(const QModelIndex &idx) const
+1 -1
View File
@@ -66,7 +66,7 @@ public:
using const_iterator = QVector<TreeItem *>::const_iterator;
using value_type = TreeItem *;
int childCount() const { return end() - begin(); }
int childCount() const { return m_children.size(); }
int indexInParent() const;
TreeItem *childAt(int index) const;
int indexOf(const TreeItem *item) const;
+1 -1
View File
@@ -47,7 +47,7 @@ QString UnixUtils::fileBrowser(const QSettings *settings)
void UnixUtils::setFileBrowser(QSettings *settings, const QString &term)
{
return settings->setValue(QLatin1String("General/FileBrowser"), term);
settings->setValue(QLatin1String("General/FileBrowser"), term);
}
+8 -2
View File
@@ -47,7 +47,10 @@ HEADERS += \
android_global.h \
androidbuildapkstep.h \
androidbuildapkwidget.h \
androidrunnable.h
androidrunnable.h \
androidtoolmanager.h \
androidsdkmanager.h \
androidavdmanager.h
SOURCES += \
androidconfigurations.cpp \
@@ -88,7 +91,10 @@ SOURCES += \
androidbuildapkstep.cpp \
androidbuildapkwidget.cpp \
androidqtsupport.cpp \
androidrunnable.cpp
androidrunnable.cpp \
androidtoolmanager.cpp \
androidsdkmanager.cpp \
androidavdmanager.cpp
FORMS += \
androidsettingswidget.ui \
+6
View File
@@ -22,6 +22,8 @@ Project {
"android.qrc",
"androidanalyzesupport.cpp",
"androidanalyzesupport.h",
"androidavdmanager.cpp",
"androidavdmanager.h",
"androidconfigurations.cpp",
"androidconfigurations.h",
"androidconstants.h",
@@ -84,6 +86,8 @@ Project {
"androidrunnable.h",
"androidrunner.cpp",
"androidrunner.h",
"androidsdkmanager.cpp",
"androidsdkmanager.h",
"androidsettingspage.cpp",
"androidsettingspage.h",
"androidsettingswidget.cpp",
@@ -93,6 +97,8 @@ Project {
"androidsignaloperation.h",
"androidtoolchain.cpp",
"androidtoolchain.h",
"androidtoolmanager.cpp",
"androidtoolmanager.h",
"avddialog.cpp",
"avddialog.h",
"certificatesmodel.cpp",
+441
View File
@@ -0,0 +1,441 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidavdmanager.h"
#include "androidtoolmanager.h"
#include "utils/algorithm.h"
#include "utils/qtcassert.h"
#include "utils/runextensions.h"
#include "utils/synchronousprocess.h"
#include <QApplication>
#include <QFileInfo>
#include <QLoggingCategory>
#include <QSettings>
#include <chrono>
namespace {
Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager")
}
namespace Android {
namespace Internal {
using namespace std;
// Avd list keys to parse avd
const char avdInfoNameKey[] = "Name:";
const char avdInfoPathKey[] = "Path:";
const char avdInfoAbiKey[] = "abi.type";
const char avdInfoTargetKey[] = "target";
const char avdInfoErrorKey[] = "Error:";
const QVersionNumber avdManagerIntroVersion(25, 3 ,0);
const int avdCreateTimeoutMs = 30000;
/*!
Runs the \c avdmanager tool specific to configuration \a config with arguments \a args. Returns
\c true if the command is successfully executed. Output is copied into \a output. The function
blocks the calling thread.
*/
static bool avdManagerCommand(const AndroidConfig config, const QStringList &args, QString *output)
{
QString avdManagerToolPath = config.avdManagerToolPath().toString();
Utils::SynchronousProcess proc;
Utils::SynchronousProcessResponse response = proc.runBlocking(avdManagerToolPath, args);
if (response.result == Utils::SynchronousProcessResponse::Finished) {
if (output)
*output = response.allOutput();
return true;
}
return false;
}
/*!
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
\c true if the key is found, \c false otherwise. The value is copied into \a value.
*/
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
{
auto trimmedInput = line.trimmed();
if (trimmedInput.startsWith(key)) {
if (value)
*value = trimmedInput.section(key, 1, 1).trimmed();
return true;
}
return false;
}
static bool checkForTimeout(const chrono::steady_clock::time_point &start,
int msecs = 3000)
{
bool timedOut = false;
auto end = chrono::steady_clock::now();
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
timedOut = true;
return timedOut;
}
static AndroidConfig::CreateAvdInfo createAvdCommand(const AndroidConfig config,
const AndroidConfig::CreateAvdInfo &info)
{
AndroidConfig::CreateAvdInfo result = info;
if (!result.isValid()) {
qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name
<< result.target.name << result.target.apiLevel;
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Invalid input.");
return result;
}
QStringList arguments({"create", "avd", "-k", result.target.package, "-n", result.name});
if (!result.abi.isEmpty()) {
SystemImage image = Utils::findOrDefault(result.target.systemImages,
Utils::equal(&SystemImage::abiName, result.abi));
if (image.isValid()) {
arguments << "-k" << image.package;
} else {
qCDebug(avdManagerLog) << "AVD Create failed. Cannot find system image for the platform"
<< result.abi << result.target.name;
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Cannot find system image for "
"the ABI %1(%2).").arg(result.abi).arg(result.target.name);
return result;
}
} else {
arguments << "-k" << result.target.package;
}
if (result.sdcardSize > 0)
arguments << "-c" << QString::fromLatin1("%1M").arg(result.sdcardSize);
QProcess proc;
proc.start(config.avdManagerToolPath().toString(), arguments);
if (!proc.waitForStarted()) {
result.error = QApplication::translate("AndroidAvdManager",
"Could not start process \"%1 %2\"")
.arg(config.avdManagerToolPath().toString(), arguments.join(' '));
return result;
}
QTC_CHECK(proc.state() == QProcess::Running);
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
auto start = chrono::steady_clock::now();
QString errorOutput;
QByteArray question;
while (errorOutput.isEmpty()) {
proc.waitForReadyRead(500);
question += proc.readAllStandardOutput();
if (question.endsWith(QByteArray("]:"))) {
// truncate to last line
int index = question.lastIndexOf(QByteArray("\n"));
if (index != -1)
question = question.mid(index);
if (question.contains("hw.gpu.enabled"))
proc.write(QByteArray("yes\n"));
else
proc.write(QByteArray("\n"));
question.clear();
}
// The exit code is always 0, so we need to check stderr
// For now assume that any output at all indicates a error
errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
if (proc.state() != QProcess::Running)
break;
// For a sane input and command, process should finish before timeout.
if (checkForTimeout(start, avdCreateTimeoutMs)) {
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Command timed out.");
}
}
// Kill the running process.
if (proc.state() != QProcess::NotRunning) {
proc.terminate();
if (!proc.waitForFinished(3000))
proc.kill();
}
QTC_CHECK(proc.state() == QProcess::NotRunning);
result.error = errorOutput;
return result;
}
/*!
\class AvdManagerOutputParser
\brief The AvdManagerOutputParser class is a helper class to parse the output of the avdmanager
commands.
*/
class AvdManagerOutputParser
{
public:
AndroidDeviceInfoList listVirtualDevices(const AndroidConfig &config);
AndroidDeviceInfoList parseAvdList(const QString &output);
private:
bool parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd);
};
AndroidAvdManager::AndroidAvdManager(const AndroidConfig &config):
m_config(config),
m_androidTool(new AndroidToolManager(m_config)),
m_parser(new AvdManagerOutputParser)
{
}
AndroidAvdManager::~AndroidAvdManager()
{
}
bool AndroidAvdManager::avdManagerUiToolAvailable() const
{
return m_config.sdkToolsVersion() < avdManagerIntroVersion;
}
void AndroidAvdManager::launchAvdManagerUiTool() const
{
if (avdManagerUiToolAvailable()) {
m_androidTool->launchAvdManager();
} else {
qCDebug(avdManagerLog) << "AVD Ui tool launch failed. UI tool not available"
<< m_config.sdkToolsVersion();
}
}
QFuture<AndroidConfig::CreateAvdInfo>
AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
return m_androidTool->createAvd(info);
return Utils::runAsync(&createAvdCommand, m_config, info);
}
bool AndroidAvdManager::removeAvd(const QString &name) const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
return m_androidTool->removeAvd(name);
Utils::SynchronousProcess proc;
proc.setTimeoutS(5);
Utils::SynchronousProcessResponse response
= proc.runBlocking(m_config.avdManagerToolPath().toString(),
QStringList({"delete", "avd", "-n", name}));
return response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0;
}
QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
return m_androidTool->androidVirtualDevicesFuture();
return Utils::runAsync(&AvdManagerOutputParser::listVirtualDevices, m_parser.get(), m_config);
}
QString AndroidAvdManager::startAvd(const QString &name) const
{
if (!findAvd(name).isEmpty() || startAvdAsync(name))
return waitForAvd(name);
return QString();
}
bool AndroidAvdManager::startAvdAsync(const QString &avdName) const
{
QProcess *avdProcess = new QProcess();
QObject::connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
avdProcess, &QObject::deleteLater);
// start the emulator
QStringList arguments;
if (AndroidConfigurations::force32bitEmulator())
arguments << "-force-32bit";
arguments << "-partition-size" << QString::number(m_config.partitionSize())
<< "-avd" << avdName;
avdProcess->start(m_config.emulatorToolPath().toString(), arguments);
if (!avdProcess->waitForStarted(-1)) {
delete avdProcess;
return false;
}
return true;
}
QString AndroidAvdManager::findAvd(const QString &avdName) const
{
QVector<AndroidDeviceInfo> devices = m_config.connectedDevices();
foreach (AndroidDeviceInfo device, devices) {
if (device.type != AndroidDeviceInfo::Emulator)
continue;
if (device.avdname == avdName)
return device.serialNumber;
}
return QString();
}
QString AndroidAvdManager::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const
{
// we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running
// 60 rounds of 2s sleeping, two minutes for the avd to start
QString serialNumber;
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return QString();
serialNumber = findAvd(avdName);
if (!serialNumber.isEmpty())
return waitForBooted(serialNumber, fi) ? serialNumber : QString();
QThread::sleep(2);
}
return QString();
}
bool AndroidAvdManager::isAvdBooted(const QString &device) const
{
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
arguments << "shell" << "getprop" << "init.svc.bootanim";
Utils::SynchronousProcess adbProc;
adbProc.setTimeoutS(10);
Utils::SynchronousProcessResponse response =
adbProc.runBlocking(m_config.adbToolPath().toString(), arguments);
if (response.result != Utils::SynchronousProcessResponse::Finished)
return false;
QString value = response.allOutput().trimmed();
return value == "stopped";
}
bool AndroidAvdManager::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
{
// found a serial number, now wait until it's done booting...
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return false;
if (isAvdBooted(serialNumber)) {
return true;
} else {
QThread::sleep(2);
if (!m_config.isConnected(serialNumber)) // device was disconnected
return false;
}
}
return false;
}
AndroidDeviceInfoList AvdManagerOutputParser::listVirtualDevices(const AndroidConfig &config)
{
QString output;
if (!avdManagerCommand(config, QStringList({"list", "avd"}), &output)) {
qCDebug(avdManagerLog) << "Avd list command failed" << output << config.sdkToolsVersion();
return {};
}
return parseAvdList(output);
}
AndroidDeviceInfoList AvdManagerOutputParser::parseAvdList(const QString &output)
{
AndroidDeviceInfoList avdList;
QStringList avdInfo;
auto parseAvdInfo = [&avdInfo, &avdList, this] () {
AndroidDeviceInfo avd;
if (parseAvd(avdInfo, &avd)) {
// armeabi-v7a devices can also run armeabi code
if (avd.cpuAbi.contains("armeabi-v7a"))
avd.cpuAbi << "armeabi";
avd.state = AndroidDeviceInfo::OkState;
avd.type = AndroidDeviceInfo::Emulator;
avdList << avd;
} else {
qCDebug(avdManagerLog) << "Avd Parsing: Parsing failed: " << avdInfo;
}
avdInfo.clear();
};
foreach (QString line, output.split('\n')) {
if (line.startsWith("---------") || line.isEmpty()) {
parseAvdInfo();
} else {
avdInfo << line;
}
}
if (!avdInfo.isEmpty())
parseAvdInfo();
Utils::sort(avdList);
return avdList;
}
bool AvdManagerOutputParser::parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd)
{
QTC_ASSERT(avd, return false);
foreach (const QString &line, deviceInfo) {
QString value;
if (valueForKey(avdInfoErrorKey, line)) {
qCDebug(avdManagerLog) << "Avd Parsing: Skip avd device. Error key found:" << line;
return false;
} else if (valueForKey(avdInfoNameKey, line, &value)) {
avd->avdname = value;
} else if (valueForKey(avdInfoPathKey, line, &value)) {
const Utils::FileName avdPath = Utils::FileName::fromString(value);
if (avdPath.exists())
{
// Get ABI.
Utils::FileName configFile = avdPath;
configFile.appendPath("config.ini");
QSettings config(configFile.toString(), QSettings::IniFormat);
value = config.value(avdInfoAbiKey).toString();
if (!value.isEmpty())
avd->cpuAbi << value;
else
qCDebug(avdManagerLog) << "Avd Parsing: Cannot find ABI:" << configFile;
// Get Target
Utils::FileName avdInfoFile = avdPath.parentDir();
QString avdInfoFileName = avdPath.toFileInfo().baseName() + ".ini";
avdInfoFile.appendPath(avdInfoFileName);
QSettings avdInfo(avdInfoFile.toString(), QSettings::IniFormat);
value = avdInfo.value(avdInfoTargetKey).toString();
if (!value.isEmpty())
avd->sdk = value.section('-', -1).toInt();
else
qCDebug(avdManagerLog) << "Avd Parsing: Cannot find sdk API:" << avdInfoFile.toString();
}
}
}
return true;
}
} // namespace Internal
} // namespace Android
+66
View File
@@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "androidconfigurations.h"
#include <memory>
namespace Android {
namespace Internal {
class AndroidToolManager;
class AvdManagerOutputParser;
class AndroidAvdManager
{
public:
AndroidAvdManager(const AndroidConfig& config = AndroidConfigurations::currentConfig());
~AndroidAvdManager();
bool avdManagerUiToolAvailable() const;
void launchAvdManagerUiTool() const;
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
bool removeAvd(const QString &name) const;
QFuture<AndroidDeviceInfoList> avdList() const;
QString startAvd(const QString &name) const;
bool startAvdAsync(const QString &avdName) const;
QString findAvd(const QString &avdName) const;
QString waitForAvd(const QString &avdName,
const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const;
bool isAvdBooted(const QString &device) const;
private:
bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const;
private:
const AndroidConfig &m_config;
std::unique_ptr<AndroidToolManager> m_androidTool;
std::unique_ptr<AvdManagerOutputParser> m_parser;
};
} // namespace Internal
} // namespace Android
@@ -60,6 +60,8 @@
namespace Android {
using namespace Internal;
const QVersionNumber gradleScriptRevokedSdkVersion(25, 3, 0);
const QVersionNumber gradleScriptsContainedQtVersion(5, 9, 0);
const QLatin1String DeployActionKey("Qt4ProjectManager.AndroidDeployQtStep.DeployQtAction");
const QLatin1String KeystoreLocationKey("KeystoreLocation");
const QLatin1String BuildTargetSdkKey("BuildTargetSdk");
@@ -140,6 +142,15 @@ bool AndroidBuildApkStep::init(QList<const BuildStep *> &earlierSteps)
if (!version)
return false;
if (AndroidConfigurations::currentConfig().sdkToolsVersion() >= gradleScriptRevokedSdkVersion &&
QVersionNumber::fromString(version->qtVersionString()) < gradleScriptsContainedQtVersion) {
emit addOutput(tr("The installed SDK tools version (%1) does not include Gradle scripts. The "
"minimum Qt version required for Gradle build to work is %2")
.arg(gradleScriptRevokedSdkVersion.toString())
.arg(gradleScriptsContainedQtVersion.toString()), OutputFormat::Stderr);
return false;
}
int minSDKForKit = AndroidManager::minimumSDK(target()->kit());
if (AndroidManager::minimumSDK(target()) < minSDKForKit) {
emit addOutput(tr("The API level set for the APK is less than the minimum required by the kit."
@@ -342,6 +353,16 @@ void AndroidBuildApkStep::setUseGradle(bool b)
}
}
bool AndroidBuildApkStep::addDebugger() const
{
return m_addDebugger;
}
void AndroidBuildApkStep::setAddDebugger(bool debug)
{
m_addDebugger = debug;
}
bool AndroidBuildApkStep::verboseOutput() const
{
return m_verbose;
+5 -1
View File
@@ -73,6 +73,9 @@ public:
bool useGradle() const;
void setUseGradle(bool b);
bool addDebugger() const;
void setAddDebugger(bool debug);
QString buildTargetSdk() const;
void setBuildTargetSdk(const QString &sdk);
@@ -99,9 +102,10 @@ protected:
AndroidDeployAction m_deployAction = BundleLibrariesDeployment;
bool m_signPackage = false;
bool m_verbose = false;
bool m_useGradle = false;
bool m_useGradle = true; // Ant builds are deprecated.
bool m_openPackageLocation = false;
bool m_openPackageLocationForRun = false;
bool m_addDebugger = true;
QString m_buildTargetSdk;
Utils::FileName m_keystorePath;
+11 -2
View File
@@ -54,9 +54,12 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
{
m_ui->setupUi(this);
m_ui->deprecatedInfoIconLabel->setPixmap(Utils::Icons::INFO.pixmap());
// Target sdk combobox
int minApiLevel = 9;
QStringList targets = AndroidConfig::apiLevelNamesFor(AndroidConfigurations::currentConfig().sdkTargets(minApiLevel));
const AndroidConfig &config = AndroidConfigurations::currentConfig();
QStringList targets = AndroidConfig::apiLevelNamesFor(config.sdkTargets(minApiLevel));
targets.removeDuplicates();
m_ui->targetSDKComboBox->addItems(targets);
m_ui->targetSDKComboBox->setCurrentIndex(targets.indexOf(AndroidManager::buildTargetSDK(step->target())));
@@ -91,9 +94,12 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
m_ui->signingDebugDeployErrorIcon->setPixmap(Utils::Icons::CRITICAL.pixmap());
signPackageCheckBoxToggled(m_step->signPackage());
m_ui->useGradleCheckBox->setChecked(m_step->useGradle());
m_ui->useGradleCheckBox->setEnabled(config.antScriptsAvailable());
m_ui->useGradleCheckBox->setChecked(!config.antScriptsAvailable() ||
m_step->useGradle());
m_ui->verboseOutputCheckBox->setChecked(m_step->verboseOutput());
m_ui->openPackageLocationCheckBox->setChecked(m_step->openPackageLocation());
m_ui->addDebuggerCheckBox->setChecked(m_step->addDebugger());
// target sdk
connect(m_ui->targetSDKComboBox,
@@ -120,6 +126,8 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
this, &AndroidBuildApkWidget::openPackageLocationCheckBoxToggled);
connect(m_ui->verboseOutputCheckBox, &QAbstractButton::toggled,
this, &AndroidBuildApkWidget::verboseOutputCheckBoxToggled);
connect(m_ui->addDebuggerCheckBox, &QAbstractButton::toggled,
m_step, &AndroidBuildApkStep::setAddDebugger);
//signing
connect(m_ui->signPackageCheckBox, &QAbstractButton::toggled,
@@ -185,6 +193,7 @@ void AndroidBuildApkWidget::signPackageCheckBoxToggled(bool checked)
{
m_ui->certificatesAliasComboBox->setEnabled(checked);
m_step->setSignPackage(checked);
m_ui->addDebuggerCheckBox->setChecked(!checked);
updateSigningWarning();
if (!checked)
return;
+77 -9
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>819</width>
<height>390</height>
<height>478</height>
</rect>
</property>
<property name="windowTitle">
@@ -176,24 +176,75 @@ Deploying local Qt libraries is incompatible with Android 5.</string>
<string>Advanced Actions</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QCheckBox" name="verboseOutputCheckBox">
<item row="0" column="0">
<widget class="QCheckBox" name="useGradleCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Verbose output</string>
<string>Use Gradle (Ant builds are deprecated)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="0" column="1">
<widget class="QLabel" name="deprecatedInfoIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Gradle builds are forced from Android SDK tools version 25.3.0 onwards as Ant scripts are no longer available.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="3">
<widget class="QCheckBox" name="openPackageLocationCheckBox">
<property name="text">
<string>Open package location after build</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="useGradleCheckBox">
<item row="2" column="0" colspan="3">
<widget class="QCheckBox" name="verboseOutputCheckBox">
<property name="text">
<string>Use Gradle</string>
<string>Verbose output</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QCheckBox" name="addDebuggerCheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Packages debug server with the APK to enable debugging. For the signed APK this option is unchecked by default.</string>
</property>
<property name="text">
<string>Add debug server</string>
</property>
</widget>
</item>
@@ -254,5 +305,22 @@ The APK will not be usable on any other device.</string>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<connections>
<connection>
<sender>signPackageCheckBox</sender>
<signal>clicked(bool)</signal>
<receiver>addDebuggerCheckBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>113</x>
<y>178</y>
</hint>
<hint type="destinationlabel">
<x>510</x>
<y>452</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+81 -349
View File
@@ -28,8 +28,11 @@
#include "androidtoolchain.h"
#include "androiddevice.h"
#include "androidgdbserverkitinformation.h"
#include "androidmanager.h"
#include "androidqtversion.h"
#include "androiddevicedialog.h"
#include "androidsdkmanager.h"
#include "androidtoolmanager.h"
#include "avddialog.h"
#include <coreplugin/icore.h>
@@ -73,6 +76,9 @@ namespace Android {
using namespace Internal;
namespace {
const QVersionNumber sdkToolsAntMissingVersion(25, 3, 0);
const QLatin1String SettingsGroup("AndroidConfigurations");
const QLatin1String SDKLocationKey("SDKLocation");
const QLatin1String NDKLocationKey("NDKLocation");
@@ -107,39 +113,14 @@ namespace {
const QLatin1String keytoolName("keytool");
const QLatin1String changeTimeStamp("ChangeTimeStamp");
const QLatin1String sdkToolsVersionKey("Pkg.Revision");
static QString sdkSettingsFileName()
{
return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
+ QLatin1String("/qtcreator/android.xml");
}
bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
{
if (dev1.serialNumber.contains(QLatin1String("????")) != dev2.serialNumber.contains(QLatin1String("????")))
return !dev1.serialNumber.contains(QLatin1String("????"));
if (dev1.type != dev2.type)
return dev1.type == AndroidDeviceInfo::Hardware;
if (dev1.sdk != dev2.sdk)
return dev1.sdk < dev2.sdk;
if (dev1.avdname != dev2.avdname)
return dev1.avdname < dev2.avdname;
return dev1.serialNumber < dev2.serialNumber;
}
static QStringList cleanAndroidABIs(const QStringList &abis)
{
QStringList res;
foreach (const QString &abi, abis) {
int index = abi.lastIndexOf(QLatin1Char('/'));
if (index == -1)
res << abi;
else
res << abi.mid(index + 1);
}
return res;
}
static bool is32BitUserSpace()
{
// Do the exact same check as android's emulator is doing:
@@ -162,25 +143,6 @@ namespace {
}
return false;
}
// Some preview sdks use a non integer version
int apiLevelFromAndroidList(const QString &string)
{
bool ok;
int result = string.toInt(&ok);
if (ok)
return result;
Utils::FileName sdkLocation = AndroidConfigurations::currentConfig().sdkLocation();
sdkLocation.appendPath(QLatin1String("/platforms/android-") + string + QLatin1String("/source.properties"));
result = QSettings(sdkLocation.toString(), QSettings::IniFormat).value(QLatin1String("AndroidVersion.ApiLevel")).toInt(&ok);
if (ok)
return result;
if (string == QLatin1String("L"))
return 21;
if (string == QLatin1String("MNC"))
return 22;
return 23; // At least
}
}
//////////////////////////////////
@@ -359,61 +321,14 @@ void AndroidConfig::updateNdkInformation() const
m_NdkInformationUpToDate = true;
}
bool AndroidConfig::sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b)
{
if (a.apiLevel != b.apiLevel)
return a.apiLevel > b.apiLevel;
if (a.name != b.name)
return a.name < b.name;
return false;
}
void AndroidConfig::updateAvailableSdkPlatforms() const
{
if (m_availableSdkPlatformsUpToDate)
return;
m_availableSdkPlatforms.clear();
SynchronousProcess proc;
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
SynchronousProcessResponse response
= proc.runBlocking(androidToolPath().toString(),
QStringList({"list", "target"})); // list available AVDs
if (response.result != SynchronousProcessResponse::Finished)
return;
SdkPlatform platform;
foreach (const QString &l, response.allOutput().split('\n')) {
const QString line = l.trimmed();
if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) {
int index = line.indexOf(QLatin1String("\"android-"));
if (index == -1)
continue;
QString androidTarget = line.mid(index + 1, line.length() - index - 2);
const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1);
platform.apiLevel = apiLevelFromAndroidList(tmp);
} else if (line.startsWith(QLatin1String("Name:"))) {
platform.name = line.mid(6);
} else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
} else if (line.startsWith(QLatin1String("ABIs"))) {
platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
} else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
if (platform.apiLevel == -1)
continue;
auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
platform, sortSdkPlatformByApiLevel);
m_availableSdkPlatforms.insert(it, platform);
platform = SdkPlatform();
}
}
if (platform.apiLevel != -1) {
auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
platform, sortSdkPlatformByApiLevel);
m_availableSdkPlatforms.insert(it, platform);
}
AndroidSdkManager sdkManager(*this);
m_availableSdkPlatforms = sdkManager.availableSdkPlatforms();
m_availableSdkPlatformsUpToDate = true;
}
@@ -446,18 +361,6 @@ FileName AndroidConfig::adbToolPath() const
return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
}
Environment AndroidConfig::androidToolEnvironment() const
{
Environment env = Environment::systemEnvironment();
if (!m_openJDKLocation.isEmpty()) {
env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput());
Utils::FileName binPath = m_openJDKLocation;
binPath.appendPath(QLatin1String("bin"));
env.prependOrSetPath(binPath.toUserOutput());
}
return env;
}
FileName AndroidConfig::androidToolPath() const
{
if (HostOsInfo::isWindowsHost()) {
@@ -486,7 +389,10 @@ FileName AndroidConfig::antToolPath() const
FileName AndroidConfig::emulatorToolPath() const
{
FileName path = m_sdkLocation;
return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX));
QString relativePath = "emulator/emulator";
if (sdkToolsVersion() < QVersionNumber(25, 3, 0))
relativePath = "tools/emulator";
return path.appendPath(relativePath + QTC_HOST_EXE_SUFFIX);
}
FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVersion) const
@@ -499,6 +405,26 @@ FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVers
.arg(toolsPrefix(abi)));
}
FileName AndroidConfig::sdkManagerToolPath() const
{
FileName sdkPath = m_sdkLocation;
QString toolPath = "tools/bin/sdkmanager";
if (HostOsInfo::isWindowsHost())
toolPath += ANDROID_BAT_SUFFIX;
sdkPath = sdkPath.appendPath(toolPath);
return sdkPath;
}
FileName AndroidConfig::avdManagerToolPath() const
{
FileName avdManagerPath = m_sdkLocation;
QString toolPath = "tools/bin/avdmanager";
if (HostOsInfo::isWindowsHost())
toolPath += ANDROID_BAT_SUFFIX;
avdManagerPath = avdManagerPath.appendPath(toolPath);
return avdManagerPath;
}
FileName AndroidConfig::gccPath(const Abi &abi, Core::Id lang,
const QString &ndkToolChainVersion) const
{
@@ -583,7 +509,7 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToo
devices.push_back(dev);
}
Utils::sort(devices, androidDevicesLessThan);
Utils::sort(devices);
if (devices.isEmpty() && error)
*error = QApplication::translate("AndroidConfiguration",
"No devices found in output of: %1")
@@ -605,197 +531,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent,
return result;
}
QFuture<AndroidConfig::CreateAvdInfo> AndroidConfig::createAVD(CreateAvdInfo info) const
{
return Utils::runAsync(&AndroidConfig::createAVDImpl, info,
androidToolPath(), androidToolEnvironment());
}
AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, FileName androidToolPath, Environment env)
{
QProcess proc;
proc.setProcessEnvironment(env.toProcessEnvironment());
QStringList arguments;
arguments << QLatin1String("create") << QLatin1String("avd")
<< QLatin1String("-t") << info.target
<< QLatin1String("-n") << info.name
<< QLatin1String("-b") << info.abi;
if (info.sdcardSize > 0)
arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize);
proc.start(androidToolPath.toString(), arguments);
if (!proc.waitForStarted()) {
info.error = QApplication::translate("AndroidConfig", "Could not start process \"%1 %2\"")
.arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
return info;
}
QTC_CHECK(proc.state() == QProcess::Running);
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
QByteArray question;
while (true) {
proc.waitForReadyRead(500);
question += proc.readAllStandardOutput();
if (question.endsWith(QByteArray("]:"))) {
// truncate to last line
int index = question.lastIndexOf(QByteArray("\n"));
if (index != -1)
question = question.mid(index);
if (question.contains("hw.gpu.enabled"))
proc.write(QByteArray("yes\n"));
else
proc.write(QByteArray("\n"));
question.clear();
}
if (proc.state() != QProcess::Running)
break;
}
QTC_CHECK(proc.state() == QProcess::NotRunning);
QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
// The exit code is always 0, so we need to check stderr
// For now assume that any output at all indicates a error
if (!errorOutput.isEmpty()) {
info.error = errorOutput;
}
return info;
}
bool AndroidConfig::removeAVD(const QString &name) const
{
SynchronousProcess proc;
proc.setTimeoutS(5);
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
SynchronousProcessResponse response
= proc.runBlocking(androidToolPath().toString(), QStringList({"delete", "avd", "-n", name}));
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
}
QFuture<QVector<AndroidDeviceInfo>> AndroidConfig::androidVirtualDevicesFuture() const
{
return Utils::runAsync(&AndroidConfig::androidVirtualDevices,
androidToolPath().toString(), androidToolEnvironment());
}
QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices(const QString &androidTool, const Environment &environment)
{
QVector<AndroidDeviceInfo> devices;
SynchronousProcess proc;
proc.setTimeoutS(20);
proc.setProcessEnvironment(environment.toProcessEnvironment());
SynchronousProcessResponse response = proc.run(androidTool, {"list", "avd"}); // list available AVDs
if (response.result != SynchronousProcessResponse::Finished)
return devices;
QStringList avds = response.allOutput().split('\n');
if (avds.empty())
return devices;
while (avds.first().startsWith(QLatin1String("* daemon")))
avds.removeFirst(); // remove the daemon logs
avds.removeFirst(); // remove "List of devices attached" header line
bool nextLineIsTargetLine = false;
AndroidDeviceInfo dev;
for (int i = 0; i < avds.size(); i++) {
QString line = avds.at(i);
if (!line.contains(QLatin1String("Name:")))
continue;
int index = line.indexOf(QLatin1Char(':')) + 2;
if (index >= line.size())
break;
dev.avdname = line.mid(index).trimmed();
dev.sdk = -1;
dev.cpuAbi.clear();
++i;
for (; i < avds.size(); ++i) {
line = avds.at(i);
if (line.contains(QLatin1String("---------")))
break;
if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) {
if (line.contains(QLatin1String("Google APIs"))) {
nextLineIsTargetLine = true;
continue;
}
nextLineIsTargetLine = false;
int lastIndex = line.lastIndexOf(QLatin1Char(' '));
if (lastIndex == -1) // skip line
break;
QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed();
dev.sdk = apiLevelFromAndroidList(tmp);
}
if (line.contains(QLatin1String("Tag/ABI:"))) {
int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1;
if (lastIndex >= line.size())
break;
dev.cpuAbi = QStringList(line.mid(lastIndex));
} else if (line.contains(QLatin1String("ABI:"))) {
int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1;
if (lastIndex >= line.size())
break;
dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed());
}
}
// armeabi-v7a devices can also run armeabi code
if (dev.cpuAbi == QStringList("armeabi-v7a"))
dev.cpuAbi << QLatin1String("armeabi");
dev.state = AndroidDeviceInfo::OkState;
dev.type = AndroidDeviceInfo::Emulator;
if (dev.cpuAbi.isEmpty() || dev.sdk == -1)
continue;
devices.push_back(dev);
}
Utils::sort(devices, androidDevicesLessThan);
return devices;
}
QString AndroidConfig::startAVD(const QString &name) const
{
if (!findAvd(name).isEmpty() || startAVDAsync(name))
return waitForAvd(name);
return QString();
}
bool AndroidConfig::startAVDAsync(const QString &avdName) const
{
QProcess *avdProcess = new QProcess();
QObject::connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
avdProcess, &QObject::deleteLater);
// start the emulator
QStringList arguments;
if (AndroidConfigurations::force32bitEmulator())
arguments << QLatin1String("-force-32bit");
arguments << QLatin1String("-partition-size") << QString::number(partitionSize())
<< QLatin1String("-avd") << avdName;
avdProcess->start(emulatorToolPath().toString(), arguments);
if (!avdProcess->waitForStarted(-1)) {
delete avdProcess;
return false;
}
return true;
}
QString AndroidConfig::findAvd(const QString &avdName) const
{
QVector<AndroidDeviceInfo> devices = connectedDevices();
foreach (AndroidDeviceInfo device, devices) {
if (device.type != AndroidDeviceInfo::Emulator)
continue;
if (device.avdname == avdName)
return device.serialNumber;
}
return QString();
}
bool AndroidConfig::isConnected(const QString &serialNumber) const
{
QVector<AndroidDeviceInfo> devices = connectedDevices();
@@ -806,39 +541,6 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const
return false;
}
bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
{
// found a serial number, now wait until it's done booting...
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return false;
if (hasFinishedBooting(serialNumber)) {
return true;
} else {
QThread::sleep(2);
if (!isConnected(serialNumber)) // device was disconnected
return false;
}
}
return false;
}
QString AndroidConfig::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const
{
// we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running
// 60 rounds of 2s sleeping, two minutes for the avd to start
QString serialNumber;
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return QString();
serialNumber = findAvd(avdName);
if (!serialNumber.isEmpty())
return waitForBooted(serialNumber, fi) ? serialNumber : QString();
QThread::sleep(2);
}
return QString();
}
bool AndroidConfig::isBootToQt(const QString &device) const
{
return isBootToQt(adbToolPath().toString(), device);
@@ -961,21 +663,6 @@ QString AndroidConfig::getProductModel(const QString &device) const
return model;
}
bool AndroidConfig::hasFinishedBooting(const QString &device) const
{
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
arguments << QLatin1String("shell") << QLatin1String("getprop")
<< QLatin1String("init.svc.bootanim");
SynchronousProcess adbProc;
adbProc.setTimeoutS(10);
SynchronousProcessResponse response = adbProc.runBlocking(adbToolPath().toString(), arguments);
if (response.result != SynchronousProcessResponse::Finished)
return false;
QString value = response.allOutput().trimmed();
return value == QLatin1String("stopped");
}
QStringList AndroidConfig::getAbis(const QString &device) const
{
return getAbis(adbToolPath().toString(), device);
@@ -1053,6 +740,19 @@ void AndroidConfig::setSdkLocation(const FileName &sdkLocation)
m_availableSdkPlatformsUpToDate = false;
}
QVersionNumber AndroidConfig::sdkToolsVersion() const
{
QVersionNumber version;
if (m_sdkLocation.exists()) {
Utils::FileName sdkToolsPropertiesPath(m_sdkLocation);
sdkToolsPropertiesPath.appendPath("tools/source.properties");
QSettings settings(sdkToolsPropertiesPath.toString(), QSettings::IniFormat);
auto versionStr = settings.value(sdkToolsVersionKey).toString();
version = QVersionNumber::fromString(versionStr);
}
return version;
}
FileName AndroidConfig::ndkLocation() const
{
return m_ndkLocation;
@@ -1126,10 +826,19 @@ void AndroidConfig::setAutomaticKitCreation(bool b)
m_automaticKitCreation = b;
}
bool AndroidConfig::antScriptsAvailable() const
{
return sdkToolsVersion() < sdkToolsAntMissingVersion;
}
bool AndroidConfig::useGrandle() const
{
if (antScriptsAvailable()) {
return m_useGradle;
}
// Force gradle builds.
return true;
}
void AndroidConfig::setUseGradle(bool b)
{
@@ -1353,6 +1062,20 @@ QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber)
return QStringList({"-s", serialNumber});
}
bool AndroidDeviceInfo::operator<(const AndroidDeviceInfo &other) const
{
if (serialNumber.contains("????") != other.serialNumber.contains("????"))
return !serialNumber.contains("????");
if (type != other.type)
return type == AndroidDeviceInfo::Hardware;
if (sdk != other.sdk)
return sdk < other.sdk;
if (avdname != other.avdname)
return avdname < other.avdname;
return serialNumber < other.serialNumber;
}
const AndroidConfig &AndroidConfigurations::currentConfig()
{
return m_instance->m_config; // ensure that m_instance is initialized
@@ -1494,4 +1217,13 @@ void AndroidConfigurations::updateAndroidDevice()
AndroidConfigurations *AndroidConfigurations::m_instance = 0;
bool SdkPlatform::operator <(const SdkPlatform &other) const
{
if (apiLevel != other.apiLevel)
return apiLevel > other.apiLevel;
if (name != other.name)
return name < other.name;
return false;
}
} // namespace Android
+36 -24
View File
@@ -36,6 +36,7 @@
#include <QHash>
#include <QMap>
#include <QFutureInterface>
#include <QVersionNumber>
#include <utils/fileutils.h>
@@ -68,19 +69,36 @@ public:
static QStringList adbSelector(const QString &serialNumber);
bool isValid() { return !serialNumber.isEmpty() || !avdname.isEmpty(); }
bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); }
bool operator<(const AndroidDeviceInfo &other) const;
};
using AndroidDeviceInfoList = QList<AndroidDeviceInfo>;
//! Defines an Android system image.
class SystemImage
{
public:
bool isValid() const { return (apiLevel != -1) && !abiName.isEmpty(); }
int apiLevel = -1;
QString abiName;
QString package;
Utils::FileName installedLocation;
};
using SystemImageList = QList<SystemImage>;
class SdkPlatform
{
public:
SdkPlatform()
: apiLevel(-1)
{}
int apiLevel;
bool isValid() const { return !name.isEmpty() && apiLevel != -1; }
bool operator <(const SdkPlatform &other) const;
int apiLevel = -1;
QString name;
QStringList abis;
QString package;
Utils::FileName installedLocation;
SystemImageList systemImages;
};
using SdkPlatformList = QList<SdkPlatform>;
class ANDROID_EXPORT AndroidConfig
{
@@ -94,6 +112,7 @@ public:
Utils::FileName sdkLocation() const;
void setSdkLocation(const Utils::FileName &sdkLocation);
QVersionNumber sdkToolsVersion() const;
Utils::FileName ndkLocation() const;
void setNdkLocation(const Utils::FileName &ndkLocation);
@@ -116,18 +135,21 @@ public:
bool automaticKitCreation() const;
void setAutomaticKitCreation(bool b);
bool antScriptsAvailable() const;
bool useGrandle() const;
void setUseGradle(bool b);
Utils::FileName adbToolPath() const;
Utils::FileName androidToolPath() const;
Utils::Environment androidToolEnvironment() const;
Utils::FileName antToolPath() const;
Utils::FileName emulatorToolPath() const;
Utils::FileName sdkManagerToolPath() const;
Utils::FileName avdManagerToolPath() const;
Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang,
const QString &ndkToolChainVersion) const;
Utils::FileName gdbPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
Utils::FileName keytoolPath() const;
@@ -135,7 +157,8 @@ public:
class CreateAvdInfo
{
public:
QString target;
bool isValid() const { return target.isValid() && !name.isEmpty(); }
SdkPlatform target;
QString name;
QString abi;
int sdcardSize = 0;
@@ -143,19 +166,10 @@ public:
};
CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const;
QFuture<CreateAvdInfo> createAVD(CreateAvdInfo info) const;
bool removeAVD(const QString &name) const;
QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const;
static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0);
QFuture<QVector<AndroidDeviceInfo> > androidVirtualDevicesFuture() const;
static QVector<AndroidDeviceInfo> androidVirtualDevices(const QString &androidTool, const Utils::Environment &environment);
QString startAVD(const QString &name) const;
bool startAVDAsync(const QString &avdName) const;
QString findAvd(const QString &avdName) const;
QString waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const;
QString bestNdkPlatformMatch(int target) const;
static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix);
@@ -166,13 +180,10 @@ public:
QString getProductModel(const QString &device) const;
enum class OpenGl { Enabled, Disabled, Unknown };
OpenGl getOpenGLEnabled(const QString &emulator) const;
bool hasFinishedBooting(const QString &device) const;
bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const;
bool isConnected(const QString &serialNumber) const;
SdkPlatform highestAndroidSdk() const;
private:
static CreateAvdInfo createAVDImpl(CreateAvdInfo info, Utils::FileName androidToolPath, Utils::Environment env);
static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property);
Utils::FileName toolPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
@@ -196,12 +207,11 @@ private:
QStringList m_makeExtraSearchDirectories;
unsigned m_partitionSize = 1024;
bool m_automaticKitCreation = true;
bool m_useGradle = false;
bool m_useGradle = true; // Ant builds are deprecated.
//caches
mutable bool m_availableSdkPlatformsUpToDate = false;
mutable QVector<SdkPlatform> m_availableSdkPlatforms;
static bool sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b);
mutable SdkPlatformList m_availableSdkPlatforms;
mutable bool m_NdkInformationUpToDate = false;
mutable QString m_toolchainHost;
@@ -247,3 +257,5 @@ private:
};
} // namespace Android
Q_DECLARE_METATYPE(Android::SdkPlatform)
@@ -180,6 +180,16 @@ AndroidDebugSupport::AndroidDebugSupport(RunControl *runControl)
[this](const QString &output) {
this->runControl()->showMessage(output, AppOutput);
});
QTC_ASSERT(runControl, return);
auto formatter = qobject_cast<AndroidOutputFormatter*>(runControl->outputFormatter());
QTC_ASSERT(formatter, return);
connect(m_runner, &AndroidRunner::pidFound, formatter, &AndroidOutputFormatter::appendPid);
connect(m_runner, &AndroidRunner::pidLost, formatter, &AndroidOutputFormatter::removePid);
connect(m_runner, &AndroidRunner::remoteProcessFinished, formatter,
[formatter] {
formatter->removePid(-1);
});
}
void AndroidDebugSupport::handleRemoteProcessStarted(Utils::Port gdbServerPort, Utils::Port qmlPort)
+5 -3
View File
@@ -33,6 +33,7 @@
#include "androidmanager.h"
#include "androidconstants.h"
#include "androidglobal.h"
#include "androidavdmanager.h"
#include <coreplugin/fileutils.h>
#include <coreplugin/icore.h>
@@ -262,8 +263,9 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString();
if (AndroidConfigurations::currentConfig().findAvd(m_avdName).isEmpty())
AndroidConfigurations::currentConfig().startAVDAsync(m_avdName);
AndroidAvdManager avdManager;
if (avdManager.findAvd(m_avdName).isEmpty())
avdManager.startAvdAsync(m_avdName);
return true;
}
@@ -414,7 +416,7 @@ void AndroidDeployQtStep::slotSetSerialNumber(const QString &serialNumber)
void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
{
if (!m_avdName.isEmpty()) {
QString serialNumber = AndroidConfigurations::currentConfig().waitForAvd(m_avdName, fi);
QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, fi);
if (serialNumber.isEmpty()) {
reportRunResult(fi, false);
return;
+7 -5
View File
@@ -25,6 +25,7 @@
#include "androiddevicedialog.h"
#include "androidmanager.h"
#include "androidavdmanager.h"
#include "ui_androiddevicedialog.h"
#include <utils/environment.h>
@@ -423,7 +424,8 @@ AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi, Andro
m_ui(new Ui::AndroidDeviceDialog),
m_apiLevel(apiLevel),
m_abi(abi),
m_defaultDevice(serialNumber)
m_defaultDevice(serialNumber),
m_avdManager(new AndroidAvdManager)
{
m_ui->setupUi(this);
m_ui->deviceView->setModel(m_model);
@@ -515,7 +517,7 @@ void AndroidDeviceDialog::refreshDeviceList()
m_ui->refreshDevicesButton->setEnabled(false);
m_progressIndicator->show();
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
m_futureWatcherRefreshDevices.setFuture(AndroidConfigurations::currentConfig().androidVirtualDevicesFuture());
m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList());
}
void AndroidDeviceDialog::devicesRefreshed()
@@ -530,7 +532,7 @@ void AndroidDeviceDialog::devicesRefreshed()
serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
}
QVector<AndroidDeviceInfo> devices = m_futureWatcherRefreshDevices.result();
AndroidDeviceInfoList devices = m_futureWatcherRefreshDevices.result();
QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices,
[] (const AndroidDeviceInfo &info) {
return info.avdname;
@@ -583,12 +585,12 @@ void AndroidDeviceDialog::createAvd()
m_ui->createAVDButton->setEnabled(false);
AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi);
if (info.target.isEmpty()) {
if (!info.target.isValid()) {
m_ui->createAVDButton->setEnabled(true);
return;
}
m_futureWatcherAddDevice.setFuture(AndroidConfigurations::currentConfig().createAVD(info));
m_futureWatcherAddDevice.setFuture(m_avdManager->createAvd(info));
}
void AndroidDeviceDialog::avdAdded()
+5 -1
View File
@@ -32,6 +32,8 @@
#include <QFutureWatcher>
#include <QTime>
#include <memory>
QT_BEGIN_NAMESPACE
class QModelIndex;
QT_END_NAMESPACE
@@ -41,6 +43,7 @@ namespace Utils { class ProgressIndicator; }
namespace Android {
namespace Internal {
class AndroidAvdManager;
class AndroidDeviceModel;
namespace Ui { class AndroidDeviceDialog; }
@@ -74,9 +77,10 @@ private:
QString m_abi;
QString m_avdNameFromAdd;
QString m_defaultDevice;
std::unique_ptr<AndroidAvdManager> m_avdManager;
QVector<AndroidDeviceInfo> m_connectedDevices;
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
QFutureWatcher<QVector<AndroidDeviceInfo>> m_futureWatcherRefreshDevices;
QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices;
};
}
+24 -6
View File
@@ -34,6 +34,7 @@
#include "androidqtsupport.h"
#include "androidqtversion.h"
#include "androidbuildapkstep.h"
#include "androidavdmanager.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/messagemanager.h>
@@ -60,11 +61,13 @@
#include <QMessageBox>
#include <QApplication>
#include <QDomDocument>
#include <QVersionNumber>
namespace {
const QLatin1String AndroidManifestName("AndroidManifest.xml");
const QLatin1String AndroidDefaultPropertiesName("project.properties");
const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber");
const QLatin1String ApiLevelKey("AndroidVersion.ApiLevel");
} // anonymous namespace
@@ -343,7 +346,7 @@ void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target)
QString deviceSerialNumber = info.serialNumber;
if (info.type == AndroidDeviceInfo::Emulator) {
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname);
deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
if (deviceSerialNumber.isEmpty())
Core::MessageManager::write(tr("Starting Android virtual device failed."));
}
@@ -372,7 +375,7 @@ void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const Q
QString deviceSerialNumber = info.serialNumber;
if (info.type == AndroidDeviceInfo::Emulator) {
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname);
deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
if (deviceSerialNumber.isEmpty())
Core::MessageManager::write(tr("Starting Android virtual device failed."));
}
@@ -565,18 +568,33 @@ bool AndroidManager::updateGradleProperties(ProjectExplorer::Target *target)
gradleProperties["buildDir"] = ".build";
gradleProperties["androidCompileSdkVersion"] = buildTargetSDK(target).split(QLatin1Char('-')).last().toLocal8Bit();
if (gradleProperties["androidBuildToolsVersion"].isEmpty()) {
QString maxVersion;
QVersionNumber maxVersion;
QDir buildToolsDir(AndroidConfigurations::currentConfig().sdkLocation().appendPath(QLatin1String("build-tools")).toString());
foreach (const QFileInfo &file, buildToolsDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot)) {
QString ver(file.fileName());
QVersionNumber ver = QVersionNumber::fromString(file.fileName());
if (maxVersion < ver)
maxVersion = ver;
}
if (maxVersion.isEmpty())
if (maxVersion.isNull())
return false;
gradleProperties["androidBuildToolsVersion"] = maxVersion.toLocal8Bit();
gradleProperties["androidBuildToolsVersion"] = maxVersion.toString().toLocal8Bit();
}
return mergeGradleProperties(gradlePropertiesPath, gradleProperties);
}
int AndroidManager::findApiLevel(const Utils::FileName &platformPath)
{
int apiLevel = -1;
Utils::FileName propertiesPath = platformPath;
propertiesPath.appendPath("/source.properties");
if (propertiesPath.exists()) {
QSettings sdkProperties(propertiesPath.toString(), QSettings::IniFormat);
bool validInt = false;
apiLevel = sdkProperties.value(ApiLevelKey).toInt(&validInt);
if (!validInt)
apiLevel = -1;
}
return apiLevel;
}
} // namespace Android
+1
View File
@@ -89,6 +89,7 @@ public:
static AndroidQtSupport *androidQtSupport(ProjectExplorer::Target *target);
static bool useGradle(ProjectExplorer::Target *target);
static bool updateGradleProperties(ProjectExplorer::Target *target);
static int findApiLevel(const Utils::FileName &platformPath);
};
} // namespace Android
+190 -1
View File
@@ -29,16 +29,205 @@
#include "androidmanager.h"
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorersettings.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtoutputformatter.h>
#include <qtsupport/qtkitinformation.h>
#include <QPlainTextEdit>
#include <QRegularExpression>
#include <QToolButton>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
using namespace ProjectExplorer;
namespace Android {
static QRegularExpression logCatRegExp("([0-9\\-]*\\s+[0-9\\-:.]*)" // 1. time
"\\s*"
"([DEIVWF])" // 2. log level
"\\/"
"(.*)" // 3. TAG
"\\(\\s*"
"(\\d+)" // 4. PID
"\\)\\:\\s"
"(.*)"); // 5. Message
AndroidOutputFormatter::AndroidOutputFormatter(Project *project)
: QtSupport::QtOutputFormatter(project)
, m_filtersButton(new QToolButton)
{
auto filtersMenu = new QMenu(m_filtersButton.data());
m_filtersButton->setToolTip(tr("Filters"));
m_filtersButton->setIcon(Utils::Icons::FILTER.icon());
m_filtersButton->setProperty("noArrow", true);
m_filtersButton->setAutoRaise(true);
m_filtersButton->setPopupMode(QToolButton::InstantPopup);
m_filtersButton->setMenu(filtersMenu);
auto logsMenu = filtersMenu->addMenu(tr("Log Level"));
addLogAction(All, logsMenu, tr("All"));
addLogAction(Verbose, logsMenu, tr("Verbose"));
addLogAction(Info, logsMenu, tr("Info"));
addLogAction(Debug, logsMenu, tr("Debug"));
addLogAction(Warning, logsMenu, tr("Warning"));
addLogAction(Error, logsMenu, tr("Error"));
addLogAction(Fatal, logsMenu, tr("Fatal"));
updateLogMenu();
m_appsMenu = filtersMenu->addMenu(tr("Applications"));
appendPid(-1, tr("All"));
}
AndroidOutputFormatter::~AndroidOutputFormatter()
{}
QList<QWidget *> AndroidOutputFormatter::toolbarWidgets() const
{
return QList<QWidget *>{m_filtersButton.data()};
}
void AndroidOutputFormatter::appendMessage(const QString &text, Utils::OutputFormat format)
{
if (text.isEmpty())
return;
CachedLine line;
line.content = text;
if (format < Utils::StdOutFormat) {
line.level = SkipFiltering;
line.pid = -1;
} else {
QRegularExpressionMatch match = logCatRegExp.match(text);
if (!match.hasMatch())
return;
line.level = None;
switch (match.captured(2).toLatin1()[0]) {
case 'D': line.level = Debug; break;
case 'I': line.level = Info; break;
case 'V': line.level = Verbose; break;
case 'W': line.level = Warning; break;
case 'E': line.level = Error; break;
case 'F': line.level = Fatal; break;
default: return;
}
line.pid = match.captured(4).toLongLong();
}
m_cachedLines.append(line);
if (m_cachedLines.size() > ProjectExplorerPlugin::projectExplorerSettings().maxAppOutputLines)
m_cachedLines.pop_front();
filterMessage(line);
}
void AndroidOutputFormatter::clear()
{
m_cachedLines.clear();
}
void AndroidOutputFormatter::appendPid(qint64 pid, const QString &name)
{
if (m_pids.contains(pid))
return;
auto action = m_appsMenu->addAction(name);
m_pids[pid] = action;
action->setCheckable(true);
action->setChecked(pid != -1);
connect(action, &QAction::triggered, this, &AndroidOutputFormatter::applyFilter);
applyFilter();
}
void AndroidOutputFormatter::removePid(qint64 pid)
{
if (pid == -1) {
for (auto action : m_pids)
m_appsMenu->removeAction(action);
m_pids.clear();
} else {
m_appsMenu->removeAction(m_pids[pid]);
m_pids.remove(pid);
}
}
void AndroidOutputFormatter::updateLogMenu(LogLevel set, LogLevel reset)
{
m_logLevelFlags |= set;
m_logLevelFlags &= ~reset;
for (const auto & pair : m_logLevels)
pair.second->setChecked((m_logLevelFlags & pair.first) == pair.first);
applyFilter();
}
void AndroidOutputFormatter::filterMessage(const CachedLine &line)
{
if (line.level == SkipFiltering || m_pids[-1]->isChecked()) {
QtOutputFormatter::appendMessage(line.content, Utils::NormalMessageFormat);
} else {
// Filter Log Level
if (!(m_logLevelFlags & line.level))
return;
// Filter PIDs
if (!m_pids[-1]->isChecked()) {
auto it = m_pids.find(line.pid);
if (it == m_pids.end() || !(*it)->isChecked())
return;
}
Utils::OutputFormat format = Utils::NormalMessageFormat;
switch (line.level) {
case Debug:
format = Utils::DebugFormat;
break;
case Info:
case Verbose:
format = Utils::StdOutFormat;
break;
case Warning:
case Error:
case Fatal:
format = Utils::StdErrFormat;
break;
default:
return;
}
Utils::OutputFormatter::appendMessage(line.content, format);
}
}
void AndroidOutputFormatter::applyFilter()
{
if (!plainTextEdit())
return;
plainTextEdit()->clear();
if (!m_pids[-1]->isChecked()) {
bool allOn = true;
for (auto action : m_pids) {
if (!action->isChecked()) {
allOn = false;
break;
}
}
m_pids[-1]->setChecked(allOn);
} else {
for (auto action : m_pids)
action->setChecked(true);
}
for (const auto &line : m_cachedLines)
filterMessage(line);
}
AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, Core::Id id)
: RunConfiguration(parent, id)
{
@@ -56,7 +245,7 @@ QWidget *AndroidRunConfiguration::createConfigurationWidget()
Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const
{
return new QtSupport::QtOutputFormatter(target()->project());
return new AndroidOutputFormatter(target()->project());
}
const QString AndroidRunConfiguration::remoteChannel() const
@@ -28,9 +28,75 @@
#include "android_global.h"
#include <projectexplorer/runconfiguration.h>
#include <qtsupport/qtoutputformatter.h>
#include <QMenu>
QT_BEGIN_NAMESPACE
class QToolButton;
QT_END_NAMESPACE
namespace Android {
class AndroidOutputFormatter : public QtSupport::QtOutputFormatter
{
Q_OBJECT
public:
enum LogLevel {
None = 0,
Verbose = 1,
Info = 1 << 1,
Debug = 1 << 2,
Warning = 1 << 3,
Error = 1 << 4,
Fatal = 1 << 5,
All = Verbose | Info | Debug | Warning | Error | Fatal,
SkipFiltering = ~All
};
public:
explicit AndroidOutputFormatter(ProjectExplorer::Project *project);
~AndroidOutputFormatter();
// OutputFormatter interface
QList<QWidget*> toolbarWidgets() const override;
void appendMessage(const QString &text, Utils::OutputFormat format) override;
void clear() override;
public slots:
void appendPid(qint64 pid, const QString &name);
void removePid(qint64 pid);
private:
struct CachedLine {
qint64 pid;
LogLevel level;
QString content;
};
private:
void updateLogMenu(LogLevel set = None, LogLevel reset = None);
void filterMessage(const CachedLine &line);
void applyFilter();
void addLogAction(LogLevel level, QMenu *logsMenu, const QString &name) {
auto action = logsMenu->addAction(name);
m_logLevels.push_back(qMakePair(level, action));
action->setCheckable(true);
connect(action, &QAction::triggered, this, [level, this](bool checked) {
updateLogMenu(checked ? level : None , checked ? None : level);
});
}
private:
int m_logLevelFlags = All;
QVector<QPair<LogLevel, QAction*>> m_logLevels;
QHash<qint64, QAction*> m_pids;
QScopedPointer<QToolButton> m_filtersButton;
QMenu *m_appsMenu;
QList<CachedLine> m_cachedLines;
};
class ANDROID_EXPORT AndroidRunConfiguration : public ProjectExplorer::RunConfiguration
{
Q_OBJECT
@@ -62,6 +62,13 @@ void AndroidRunControl::start()
this, &AndroidRunControl::handleRemoteOutput);
connect(m_runner, &AndroidRunner::remoteProcessFinished,
this, &AndroidRunControl::handleRemoteProcessFinished);
auto formatter = static_cast<AndroidOutputFormatter *>(outputFormatter());
connect(m_runner, &AndroidRunner::pidFound,
formatter, &AndroidOutputFormatter::appendPid);
connect(m_runner, &AndroidRunner::pidLost,
formatter, &AndroidOutputFormatter::removePid);
appendMessage(tr("Starting remote process."), Utils::NormalMessageFormat);
m_runner->setRunnable(runnable().as<AndroidRunnable>());
m_runner->start();
+165 -122
View File
@@ -31,6 +31,7 @@
#include "androidglobal.h"
#include "androidrunconfiguration.h"
#include "androidmanager.h"
#include "androidavdmanager.h"
#include <debugger/debuggerrunconfigurationaspect.h>
#include <projectexplorer/projectexplorer.h>
@@ -51,6 +52,7 @@
#include <QTime>
#include <QTcpServer>
#include <QTcpSocket>
#include <QRegularExpression>
using namespace std;
using namespace std::placeholders;
@@ -125,10 +127,10 @@ namespace Internal {
const int MIN_SOCKET_HANDSHAKE_PORT = 20001;
const int MAX_SOCKET_HANDSHAKE_PORT = 20999;
static const QString pidScript = QStringLiteral("for p in /proc/[0-9]*; "
"do cat <$p/cmdline && echo :${p##*/}; done");
static const QString pidPollingScript = QStringLiteral("while true; do sleep 1; "
"cat /proc/%1/cmdline > /dev/null; done");
static const QString pidScript = QStringLiteral("input keyevent KEYCODE_WAKEUP; "
"while true; do sleep 1; echo \"=\"; "
"for p in /proc/[0-9]*; "
"do cat <$p/cmdline && echo :${p##*/}; done; done");
static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
"\\s+"
@@ -146,55 +148,26 @@ static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
);
static int APP_START_TIMEOUT = 45000;
static bool isTimedOut(const chrono::high_resolution_clock::time_point &start,
int msecs = APP_START_TIMEOUT)
enum class PidStatus {
Found,
Lost
};
struct PidInfo
{
bool timedOut = false;
auto end = chrono::high_resolution_clock::now();
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
timedOut = true;
return timedOut;
}
static qint64 extractPID(const QByteArray &output, const QString &packageName)
{
qint64 pid = -1;
foreach (auto tuple, output.split('\n')) {
tuple = tuple.simplified();
if (!tuple.isEmpty()) {
auto parts = tuple.split(':');
QString commandName = QString::fromLocal8Bit(parts.first());
if (parts.length() == 2 && commandName == packageName) {
pid = parts.last().toLongLong();
break;
}
}
}
return pid;
}
void findProcessPID(QFutureInterface<qint64> &fi, const QString &adbPath,
QStringList selector, const QString &packageName)
{
if (packageName.isEmpty())
return;
qint64 processPID = -1;
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
do {
QThread::msleep(200);
const QByteArray out = Utils::SynchronousProcess()
.runBlocking(adbPath, selector << QStringLiteral("shell") << pidScript)
.allRawOutput();
processPID = extractPID(out, packageName);
} while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled());
if (!fi.isCanceled())
fi.reportResult(processPID);
}
PidInfo(qint64 pid = -1, PidStatus status = PidStatus::Lost, QString name = {})
: pid(pid)
, status(status)
, name(name)
{}
qint64 pid;
PidStatus status;
QString name;
};
static void deleter(QProcess *p)
{
p->disconnect();
p->kill();
p->waitForFinished();
// Might get deleted from its own signal handler.
@@ -228,29 +201,31 @@ signals:
void remoteOutput(const QString &output);
void remoteErrorOutput(const QString &output);
void pidFound(qint64, const QString &name);
void pidLost(qint64);
private:
void onProcessIdChanged(qint64 pid);
void findProcessPids();
void onProcessIdChanged(PidInfo pidInfo);
void logcatReadStandardError();
void logcatReadStandardOutput();
void adbKill(qint64 pid);
QStringList selector() const { return m_selector; }
void forceStop();
void findPs();
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
bool adbShellAmNeedsQuotes();
bool runAdb(const QStringList &args, QString *exitMessage = nullptr, int timeoutS = 10);
int deviceSdkVersion();
// Create the processes and timer in the worker thread, for correct thread affinity
std::unique_ptr<QProcess, decltype(&deleter)> m_adbLogcatProcess;
std::unique_ptr<QProcess, decltype(&deleter)> m_psIsAlive;
std::unique_ptr<QProcess, decltype(&deleter)> m_pidsFinderProcess;
QScopedPointer<QTcpSocket> m_socket;
QByteArray m_stdoutBuffer;
QByteArray m_stderrBuffer;
QFuture<qint64> m_pidFinder;
qint64 m_processPID = -1;
QSet<qint64> m_processPids;
bool m_useCppDebugger = false;
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket.
@@ -261,20 +236,20 @@ private:
QString m_gdbserverSocket;
QString m_adb;
QStringList m_selector;
QRegExp m_logCatRegExp;
DebugHandShakeType m_handShakeMethod = SocketHandShake;
bool m_customPort = false;
QString m_packageName;
int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT;
QByteArray m_pidsBuffer;
QScopedPointer<QTimer> m_timeoutTimer;
};
AndroidRunnerWorker::AndroidRunnerWorker(AndroidRunConfiguration *runConfig, Core::Id runMode,
const QString &packageName, const QStringList &selector)
: m_adbLogcatProcess(nullptr, deleter)
, m_psIsAlive(nullptr, deleter)
, m_pidsFinderProcess(nullptr, deleter)
, m_selector(selector)
, m_logCatRegExp(regExpLogcat)
, m_packageName(packageName)
{
Debugger::DebuggerRunConfigurationAspect *aspect
@@ -338,23 +313,18 @@ AndroidRunnerWorker::AndroidRunnerWorker(AndroidRunConfiguration *runConfig, Cor
AndroidRunnerWorker::~AndroidRunnerWorker()
{
if (!m_pidFinder.isFinished())
m_pidFinder.cancel();
}
void AndroidRunnerWorker::forceStop()
{
runAdb(selector() << "shell" << "am" << "force-stop" << m_packageName, nullptr, 30);
// try killing it via kill -9
const QByteArray out = Utils::SynchronousProcess()
.runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript)
.allRawOutput();
qint64 pid = extractPID(out.simplified(), m_packageName);
if (pid != -1) {
adbKill(pid);
for (auto it = m_processPids.constBegin(); it != m_processPids.constEnd(); ++it) {
emit pidLost(*it);
adbKill(*it);
}
m_processPids.clear();
m_pidsBuffer.clear();
}
void AndroidRunnerWorker::asyncStart(const QString &intentName,
@@ -368,8 +338,12 @@ void AndroidRunnerWorker::asyncStart(const QString &intentName,
this, &AndroidRunnerWorker::logcatReadStandardOutput);
connect(logcatProcess.get(), &QProcess::readyReadStandardError,
this, &AndroidRunnerWorker::logcatReadStandardError);
// Its assumed that the device or avd returned by selector() is online.
logcatProcess->start(m_adb, selector() << "logcat");
QStringList logcatArgs = selector() << "logcat" << "-v" << "time";
if (deviceSdkVersion() > 20)
logcatArgs << "-T" << "0";
logcatProcess->start(m_adb, logcatArgs);
QString errorMessage;
@@ -507,9 +481,20 @@ void AndroidRunnerWorker::asyncStart(const QString &intentName,
QTC_ASSERT(!m_adbLogcatProcess, /**/);
m_adbLogcatProcess = std::move(logcatProcess);
m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(),
m_packageName),
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
m_timeoutTimer.reset(new QTimer);
m_timeoutTimer->setSingleShot(true);
connect(m_timeoutTimer.data(), &QTimer::timeout,
this,[this] { onProcessIdChanged(PidInfo{}); });
m_timeoutTimer->start(APP_START_TIMEOUT);
m_pidsFinderProcess.reset(new QProcess);
m_pidsFinderProcess->setProcessChannelMode(QProcess::MergedChannels);
connect(m_pidsFinderProcess.get(), &QProcess::readyRead, this, &AndroidRunnerWorker::findProcessPids);
connect(m_pidsFinderProcess.get(),
static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, [this] { onProcessIdChanged(PidInfo{}); });
m_pidsFinderProcess->start(m_adb, selector() << "shell" << pidScript);
}
bool AndroidRunnerWorker::adbShellAmNeedsQuotes()
@@ -545,6 +530,19 @@ bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage,
return response.result == Utils::SynchronousProcessResponse::Finished;
}
int AndroidRunnerWorker::deviceSdkVersion()
{
Utils::SynchronousProcess adb;
adb.setTimeoutS(10);
Utils::SynchronousProcessResponse response
= adb.run(m_adb, selector() << "shell" << "getprop" << "ro.build.version.sdk");
if (response.result == Utils::SynchronousProcessResponse::StartFailed
|| response.result != Utils::SynchronousProcessResponse::Finished)
return -1;
return response.allOutput().trimmed().toInt();
}
void AndroidRunnerWorker::handleRemoteDebuggerRunning()
{
if (m_useCppDebugger) {
@@ -558,21 +556,79 @@ void AndroidRunnerWorker::handleRemoteDebuggerRunning()
runAdb(selector() << "push" << tmp.fileName() << m_pongFile);
}
QTC_CHECK(m_processPID != -1);
QTC_CHECK(!m_processPids.isEmpty());
}
emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
}
void AndroidRunnerWorker::findProcessPids()
{
static QMap<qint64, QByteArray> extractedPids;
static auto oldPids = m_processPids;
m_pidsBuffer += m_pidsFinderProcess->readAll();
while (!m_pidsBuffer.isEmpty()) {
const int to = m_pidsBuffer.indexOf('\n');
if (to < 0)
break;
if (to == 0) {
m_pidsBuffer = m_pidsBuffer.mid(1);
continue;
}
// = is used to delimit ps outputs
// is needed to know when an existins PID is killed
if (m_pidsBuffer[0] != '=') {
QByteArray tuple = m_pidsBuffer.left(to + 1).simplified();
QList<QByteArray> parts = tuple.split(':');
QByteArray commandName = parts.takeFirst();
if (QString::fromLocal8Bit(commandName) == m_packageName) {
auto pid = parts.last().toLongLong();
if (!m_processPids.contains(pid)) {
extractedPids[pid] = commandName + (parts.length() == 2
? ":" + parts.first() : QByteArray{});
} else {
oldPids.remove(pid);
}
}
} else {
// Add new PIDs
for (auto it = extractedPids.constBegin(); it != extractedPids.constEnd(); ++it) {
onProcessIdChanged(PidInfo(it.key(), PidStatus::Found,
QString::fromLocal8Bit(it.value())));
}
extractedPids.clear();
// Remove the dead ones
for (auto it = oldPids.constBegin(); it != oldPids.constEnd(); ++it)
onProcessIdChanged(PidInfo(*it, PidStatus::Lost));
// Save the current non dead PIDs
oldPids = m_processPids;
if (m_processPids.isEmpty()) {
extractedPids.clear();
m_pidsBuffer.clear();
break;
}
}
m_pidsBuffer = m_pidsBuffer.mid(to + 1);
}
}
void AndroidRunnerWorker::asyncStop(const QVector<QStringList> &adbCommands)
{
if (!m_pidFinder.isFinished())
m_pidFinder.cancel();
if (m_processPID != -1) {
m_timeoutTimer.reset();
m_pidsFinderProcess.reset();
if (!m_processPids.isEmpty())
forceStop();
}
foreach (const QStringList &entry, adbCommands)
runAdb(selector() << entry);
m_adbLogcatProcess.reset();
emit remoteProcessFinished(QLatin1String("\n\n") +
tr("\"%1\" terminated.").arg(m_packageName));
}
void AndroidRunnerWorker::setAdbParameters(const QString &packageName, const QStringList &selector)
@@ -594,58 +650,48 @@ void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buff
buffer.clear();
}
QString pidString = QString::number(m_processPID);
foreach (const QByteArray &msg, lines) {
const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n');
if (!line.contains(pidString))
continue;
if (m_logCatRegExp.exactMatch(line)) {
// Android M
if (m_logCatRegExp.cap(1) == pidString) {
const QString &messagetype = m_logCatRegExp.cap(2);
QString output = line.mid(m_logCatRegExp.pos(2));
if (onlyError
|| messagetype == QLatin1String("F")
|| messagetype == QLatin1String("E")
|| messagetype == QLatin1String("W"))
emit remoteErrorOutput(output);
else
emit remoteOutput(output);
}
} else {
if (onlyError || line.startsWith("F/")
|| line.startsWith("E/")
|| line.startsWith("W/"))
const QString line = QString::fromUtf8(msg.trimmed());
if (onlyError)
emit remoteErrorOutput(line);
else
emit remoteOutput(line);
}
}
}
void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
void AndroidRunnerWorker::onProcessIdChanged(PidInfo pidInfo)
{
// Don't write to m_psProc from a different thread
QTC_ASSERT(QThread::currentThread() == thread(), return);
m_processPID = pid;
if (m_processPID == -1) {
auto isFirst = m_processPids.isEmpty();
if (pidInfo.status == PidStatus::Lost) {
m_processPids.remove(pidInfo.pid);
emit pidLost(pidInfo.pid);
} else {
m_processPids.insert(pidInfo.pid);
emit pidFound(pidInfo.pid, pidInfo.name);
}
if (m_processPids.isEmpty() || pidInfo.pid == -1) {
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.")
.arg(m_packageName));
// App died/killed. Reset log and monitor processes.
forceStop();
m_adbLogcatProcess.reset();
m_psIsAlive.reset();
} else {
m_timeoutTimer.reset();
} else if (isFirst) {
m_timeoutTimer.reset();
if (m_useCppDebugger) {
// This will be funneled to the engine to actually start and attach
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort.number());
emit remoteServerRunning(serverChannel, m_processPID);
emit remoteServerRunning(serverChannel, pidInfo.pid);
} else if (m_qmlDebugServices == QmlDebug::QmlDebuggerServices) {
// This will be funneled to the engine to actually start and attach
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
QByteArray serverChannel = QByteArray::number(m_qmlPort.number());
emit remoteServerRunning(serverChannel, m_processPID);
emit remoteServerRunning(serverChannel, pidInfo.pid);
} else if (m_qmlDebugServices == QmlDebug::QmlProfilerServices) {
emit remoteProcessStarted(Utils::Port(), m_qmlPort);
} else {
@@ -653,27 +699,18 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
emit remoteProcessStarted(Utils::Port(), Utils::Port());
}
logcatReadStandardOutput();
QTC_ASSERT(!m_psIsAlive, /**/);
m_psIsAlive.reset(new QProcess);
m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels);
connect(m_psIsAlive.get(), &QProcess::readyRead, [this](){
if (!m_psIsAlive->readAll().simplified().isEmpty())
onProcessIdChanged(-1);
});
m_psIsAlive->start(m_adb, selector() << QStringLiteral("shell")
<< pidPollingScript.arg(m_processPID));
}
}
void AndroidRunnerWorker::logcatReadStandardError()
{
if (m_processPID != -1)
if (!m_processPids.isEmpty() && m_adbLogcatProcess)
logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true);
}
void AndroidRunnerWorker::logcatReadStandardOutput()
{
if (m_processPID != -1)
if (!m_processPids.isEmpty() && m_adbLogcatProcess)
logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false);
}
@@ -724,6 +761,10 @@ AndroidRunner::AndroidRunner(QObject *parent, RunConfiguration *runConfig, Core:
this, &AndroidRunner::remoteOutput);
connect(m_worker.data(), &AndroidRunnerWorker::remoteErrorOutput,
this, &AndroidRunner::remoteErrorOutput);
connect(m_worker.data(), &AndroidRunnerWorker::pidFound,
this, &AndroidRunner::pidFound);
connect(m_worker.data(), &AndroidRunnerWorker::pidLost,
this, &AndroidRunner::pidLost);
m_thread.start();
}
@@ -791,8 +832,9 @@ void AndroidRunner::launchAVD()
emit adbParametersChanged(m_androidRunnable.packageName,
AndroidDeviceInfo::adbSelector(info.serialNumber));
if (info.isValid()) {
if (AndroidConfigurations::currentConfig().findAvd(info.avdname).isEmpty()) {
bool launched = AndroidConfigurations::currentConfig().startAVDAsync(info.avdname);
AndroidAvdManager avdManager;
if (avdManager.findAvd(info.avdname).isEmpty()) {
bool launched = avdManager.startAvdAsync(info.avdname);
m_launchedAVDName = launched ? info.avdname:"";
} else {
m_launchedAVDName.clear();
@@ -803,11 +845,12 @@ void AndroidRunner::launchAVD()
void AndroidRunner::checkAVD()
{
const AndroidConfig &config = AndroidConfigurations::currentConfig();
QString serialNumber = config.findAvd(m_launchedAVDName);
AndroidAvdManager avdManager(config);
QString serialNumber = avdManager.findAvd(m_launchedAVDName);
if (!serialNumber.isEmpty())
return; // try again on next timer hit
if (config.hasFinishedBooting(serialNumber)) {
if (avdManager.isAvdBooted(serialNumber)) {
m_checkAVDTimer.stop();
AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber);
emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands);
+3
View File
@@ -75,6 +75,9 @@ signals:
void adbParametersChanged(const QString &packageName, const QStringList &selector);
void avdDetected();
void pidFound(qint64, const QString &name);
void pidLost(qint64);
private:
void checkAVD();
void launchAVD();
+337
View File
@@ -0,0 +1,337 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidsdkmanager.h"
#include "androidmanager.h"
#include "androidtoolmanager.h"
#include "utils/algorithm.h"
#include "utils/qtcassert.h"
#include "utils/synchronousprocess.h"
#include "utils/environment.h"
#include <QLoggingCategory>
#include <QSettings>
namespace {
Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager")
}
namespace Android {
namespace Internal {
// Though sdk manager is introduced in 25.2.3 but the verbose mode is avaialble in 25.3.0
// and android tool is supported in 25.2.3
const QVersionNumber sdkManagerIntroVersion(25, 3 ,0);
const char installLocationKey[] = "Installed Location:";
const char apiLevelPropertyKey[] = "AndroidVersion.ApiLevel";
const char abiPropertyKey[] = "SystemImage.Abi";
using namespace Utils;
/*!
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
\c true if \a key is found, false otherwise. Result is copied into \a value.
*/
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
{
auto trimmedInput = line.trimmed();
if (trimmedInput.startsWith(key)) {
if (value)
*value = trimmedInput.section(key, 1, 1).trimmed();
return true;
}
return false;
}
/*!
Runs the \c sdkmanger tool specific to configuration \a config with arguments \a args. Returns
\c true if the command is successfully executed. Output is copied into \a output. The function
blocks the calling thread.
*/
static bool sdkManagerCommand(const AndroidConfig config, const QStringList &args, QString *output)
{
QString sdkManagerToolPath = config.sdkManagerToolPath().toString();
SynchronousProcess proc;
SynchronousProcessResponse response = proc.runBlocking(sdkManagerToolPath, args);
if (response.result == SynchronousProcessResponse::Finished) {
if (output)
*output = response.allOutput();
return true;
}
return false;
}
/*!
\class SdkManagerOutputParser
\brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager
commands.
*/
class SdkManagerOutputParser
{
public:
enum MarkerTag
{
None = 0x01,
InstalledPackagesMarker = 0x02,
AvailablePackagesMarkers = 0x04,
AvailableUpdatesMarker = 0x08,
EmptyMarker = 0x10,
PlatformMarker = 0x20,
SystemImageMarker = 0x40,
SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker
};
void parsePackageListing(const QString &output);
SdkPlatformList m_installedPlatforms;
private:
void compileData();
void parsePackageData(MarkerTag packageMarker, const QStringList &data);
bool parsePlatform(const QStringList &data, SdkPlatform *platform) const;
bool parseSystemImage(const QStringList &data, SystemImage *image);
MarkerTag parseMarkers(const QString &line);
MarkerTag m_currentSection = MarkerTag::None;
SystemImageList m_installedSystemImages;
};
const std::map<SdkManagerOutputParser::MarkerTag, const char *> markerTags {
{SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"},
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"},
{SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Updates:"},
{SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"},
{SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"}
};
AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config):
m_config(config),
m_parser(new SdkManagerOutputParser)
{
QString packageListing;
if (sdkManagerCommand(config, QStringList({"--list", "--verbose"}), &packageListing)) {
m_parser->parsePackageListing(packageListing);
}
}
AndroidSdkManager::~AndroidSdkManager()
{
}
SdkPlatformList AndroidSdkManager::availableSdkPlatforms()
{
if (m_config.sdkToolsVersion() < sdkManagerIntroVersion) {
AndroidToolManager toolManager(m_config);
return toolManager.availableSdkPlatforms();
}
return m_parser->m_installedPlatforms;
}
void SdkManagerOutputParser::parsePackageListing(const QString &output)
{
QStringList packageData;
bool collectingPackageData = false;
MarkerTag currentPackageMarker = MarkerTag::None;
auto processCurrentPackage = [&]() {
if (collectingPackageData) {
collectingPackageData = false;
parsePackageData(currentPackageMarker, packageData);
packageData.clear();
}
};
foreach (QString outputLine, output.split('\n')) {
MarkerTag marker = parseMarkers(outputLine);
if (marker & SectionMarkers) {
// Section marker found. Update the current section being parsed.
m_currentSection = marker;
processCurrentPackage();
continue;
}
if (m_currentSection == None
|| m_currentSection == AvailablePackagesMarkers // At this point. Not interested in
|| m_currentSection == AvailableUpdatesMarker) { // available or update packages.
// Let go of verbose output utill a valid section starts.
continue;
}
if (marker == EmptyMarker) {
// Empty marker. Occurs at the end of a package details.
// Process the collected package data, if any.
processCurrentPackage();
continue;
}
if (marker == None) {
if (collectingPackageData)
packageData << outputLine; // Collect data until next marker.
else
continue;
} else {
// Package marker found.
processCurrentPackage(); // New package starts. Process the collected package data, if any.
currentPackageMarker = marker;
collectingPackageData = true;
packageData << outputLine;
}
}
compileData();
Utils::sort(m_installedPlatforms);
}
void SdkManagerOutputParser::compileData()
{
// Associate the system images with sdk platforms.
for (auto &image : m_installedSystemImages) {
auto findPlatfom = [image](const SdkPlatform &platform) {
return platform.apiLevel == image.apiLevel;
};
auto itr = std::find_if(m_installedPlatforms.begin(), m_installedPlatforms.end(), findPlatfom);
if (itr != m_installedPlatforms.end())
itr->systemImages.append(image);
}
}
void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QStringList &data)
{
QTC_ASSERT(!data.isEmpty() && packageMarker != None, return);
if (m_currentSection != MarkerTag::InstalledPackagesMarker)
return; // For now, only interested in installed packages.
switch (packageMarker) {
case MarkerTag::PlatformMarker:
{
SdkPlatform platform;
if (parsePlatform(data, &platform))
m_installedPlatforms.append(platform);
else
qCDebug(sdkManagerLog) << "Platform: Parsing failed: " << data;
}
break;
case MarkerTag::SystemImageMarker:
{
SystemImage image;
if (parseSystemImage(data, &image))
m_installedSystemImages.append(image);
else
qCDebug(sdkManagerLog) << "System Image: Parsing failed: " << data;
}
break;
default:
qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags.at(packageMarker);
break;
}
}
bool SdkManagerOutputParser::parsePlatform(const QStringList &data, SdkPlatform *platform) const
{
QTC_ASSERT(platform && !data.isEmpty(), return false);
QStringList parts = data.at(0).split(';');
if (parts.count() < 2) {
qCDebug(sdkManagerLog) << "Platform: Unexpected header: "<< data;
return false;
}
platform->name = parts[1];
platform->package = data.at(0);
foreach (QString line, data) {
QString value;
if (valueForKey(installLocationKey, line, &value))
platform->installedLocation = Utils::FileName::fromString(value);
}
int apiLevel = AndroidManager::findApiLevel(platform->installedLocation);
if (apiLevel != -1)
platform->apiLevel = apiLevel;
else
qCDebug(sdkManagerLog) << "Platform: Can not parse api level: "<< data;
return apiLevel != -1;
}
bool SdkManagerOutputParser::parseSystemImage(const QStringList &data, SystemImage *image)
{
QTC_ASSERT(image && !data.isEmpty(), return false);
QStringList parts = data.at(0).split(';');
QTC_ASSERT(!data.isEmpty() && parts.count() >= 4,
qCDebug(sdkManagerLog) << "System Image: Unexpected header: " << data);
image->package = data.at(0);
foreach (QString line, data) {
QString value;
if (valueForKey(installLocationKey, line, &value))
image->installedLocation = Utils::FileName::fromString(value);
}
Utils::FileName propertiesPath = image->installedLocation;
propertiesPath.appendPath("/source.properties");
if (propertiesPath.exists()) {
// Installed System Image.
QSettings imageProperties(propertiesPath.toString(), QSettings::IniFormat);
bool validApiLevel = false;
image->apiLevel = imageProperties.value(apiLevelPropertyKey).toInt(&validApiLevel);
if (!validApiLevel) {
qCDebug(sdkManagerLog) << "System Image: Can not parse api level: "<< data;
return false;
}
image->abiName = imageProperties.value(abiPropertyKey).toString();
} else if (parts.count() >= 4){
image->apiLevel = parts[1].section('-', 1, 1).toInt();
image->abiName = parts[3];
} else {
qCDebug(sdkManagerLog) << "System Image: Can not parse: "<< data;
return false;
}
return true;
}
SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line)
{
if (line.isEmpty())
return EmptyMarker;
for (auto pair: markerTags) {
if (line.startsWith(QLatin1String(pair.second)))
return pair.first;
}
return None;
}
} // namespace Internal
} // namespace Android
+51
View File
@@ -0,0 +1,51 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "utils/fileutils.h"
#include "androidconfigurations.h"
#include <memory>
namespace Android {
namespace Internal {
class SdkManagerOutputParser;
class AndroidSdkManager
{
public:
AndroidSdkManager(const AndroidConfig &config);
~AndroidSdkManager();
SdkPlatformList availableSdkPlatforms();
private:
const AndroidConfig &m_config;
std::unique_ptr<SdkManagerOutputParser> m_parser;
};
} // namespace Internal
} // namespace Android
+30 -18
View File
@@ -30,6 +30,7 @@
#include "androidconfigurations.h"
#include "androidconstants.h"
#include "androidtoolchain.h"
#include "androidavdmanager.h"
#include <utils/environment.h>
#include <utils/hostosinfo.h>
@@ -58,7 +59,7 @@
namespace Android {
namespace Internal {
void AvdModel::setAvdList(const QVector<AndroidDeviceInfo> &list)
void AvdModel::setAvdList(const AndroidDeviceInfoList &list)
{
beginResetModel();
m_list = list;
@@ -128,10 +129,13 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
m_ndkState(NotSet),
m_javaState(NotSet),
m_ui(new Ui_AndroidSettingsWidget),
m_androidConfig(AndroidConfigurations::currentConfig())
m_androidConfig(AndroidConfigurations::currentConfig()),
m_avdManager(new AndroidAvdManager(m_androidConfig))
{
m_ui->setupUi(this);
m_ui->deprecatedInfoIconLabel->setPixmap(Utils::Icons::INFO.pixmap());
connect(&m_checkGdbWatcher, &QFutureWatcherBase::finished,
this, &AndroidSettingsWidget::checkGdbFinished);
@@ -158,7 +162,8 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
m_ui->AntLocationPathChooser->setPromptDialogTitle(tr("Select ant Script"));
m_ui->AntLocationPathChooser->setInitialBrowsePathBackup(dir);
m_ui->AntLocationPathChooser->setPromptDialogFilter(filter);
m_ui->UseGradleCheckBox->setChecked(m_androidConfig.useGrandle());
updateGradleBuildUi();
m_ui->OpenJDKLocationPathChooser->setFileName(m_androidConfig.openJDKLocation());
m_ui->OpenJDKLocationPathChooser->setPromptDialogTitle(tr("Select JDK Path"));
@@ -463,7 +468,7 @@ void AndroidSettingsWidget::enableAvdControls()
void AndroidSettingsWidget::startUpdateAvd()
{
disableAvdControls();
m_virtualDevicesWatcher.setFuture(m_androidConfig.androidVirtualDevicesFuture());
m_virtualDevicesWatcher.setFuture(m_avdManager->avdList());
}
void AndroidSettingsWidget::updateAvds()
@@ -476,6 +481,13 @@ void AndroidSettingsWidget::updateAvds()
enableAvdControls();
}
void AndroidSettingsWidget::updateGradleBuildUi()
{
m_ui->UseGradleCheckBox->setEnabled(m_androidConfig.antScriptsAvailable());
m_ui->UseGradleCheckBox->setChecked(!m_androidConfig.antScriptsAvailable() ||
m_androidConfig.useGrandle());
}
bool AndroidSettingsWidget::sdkLocationIsValid() const
{
Utils::FileName androidExe = m_androidConfig.sdkLocation();
@@ -505,6 +517,7 @@ void AndroidSettingsWidget::saveSettings()
void AndroidSettingsWidget::sdkLocationEditingFinished()
{
m_androidConfig.setSdkLocation(Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath()));
updateGradleBuildUi();
check(Sdk);
@@ -587,12 +600,12 @@ void AndroidSettingsWidget::addAVD()
disableAvdControls();
AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this);
if (info.target.isEmpty()) {
if (!info.target.isValid()) {
enableAvdControls();
return;
}
m_futureWatcher.setFuture(m_androidConfig.createAVD(info));
m_futureWatcher.setFuture(m_avdManager->createAvd(info));
}
void AndroidSettingsWidget::avdAdded()
@@ -620,13 +633,13 @@ void AndroidSettingsWidget::removeAVD()
return;
}
m_androidConfig.removeAVD(avdName);
m_avdManager->removeAvd(avdName);
startUpdateAvd();
}
void AndroidSettingsWidget::startAVD()
{
m_androidConfig.startAVDAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex()));
m_avdManager->startAvdAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex()));
}
void AndroidSettingsWidget::avdActivated(const QModelIndex &index)
@@ -671,16 +684,15 @@ void AndroidSettingsWidget::showGdbWarningDialog()
void AndroidSettingsWidget::manageAVD()
{
QProcess *avdProcess = new QProcess();
connect(this, &QObject::destroyed, avdProcess, &QObject::deleteLater);
connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
avdProcess, &QObject::deleteLater);
avdProcess->setProcessEnvironment(m_androidConfig.androidToolEnvironment().toProcessEnvironment());
QString executable = m_androidConfig.androidToolPath().toString();
QStringList arguments = QStringList("avd");
avdProcess->start(executable, arguments);
if (m_avdManager->avdManagerUiToolAvailable()) {
m_avdManager->launchAvdManagerUiTool();
} else {
QMessageBox::warning(this, tr("AVD Manager Not Available"),
tr("AVD manager UI tool is not available in the installed SDK tools"
"(version %1). Use the command line tool \"avdmanager\" for "
"advanced AVD management.")
.arg(m_androidConfig.sdkToolsVersion().toString()));
}
}
+9 -3
View File
@@ -33,6 +33,8 @@
#include <QAbstractTableModel>
#include <QFutureWatcher>
#include <memory>
QT_BEGIN_NAMESPACE
class Ui_AndroidSettingsWidget;
QT_END_NAMESPACE
@@ -40,11 +42,13 @@ QT_END_NAMESPACE
namespace Android {
namespace Internal {
class AndroidAvdManager;
class AvdModel: public QAbstractTableModel
{
Q_OBJECT
public:
void setAvdList(const QVector<AndroidDeviceInfo> &list);
void setAvdList(const AndroidDeviceInfoList &list);
QString avdName(const QModelIndex &index) const;
QModelIndex indexForAvdName(const QString &avdName) const;
@@ -55,7 +59,7 @@ protected:
int columnCount(const QModelIndex &parent = QModelIndex()) const;
private:
QVector<AndroidDeviceInfo> m_list;
AndroidDeviceInfoList m_list;
};
class AndroidSettingsWidget : public QWidget
@@ -91,6 +95,7 @@ private:
void checkGdbFinished();
void showGdbWarningDialog();
void updateAvds();
void updateGradleBuildUi();
private:
enum Mode { Sdk = 1, Ndk = 2, Java = 4, All = Sdk | Ndk | Java };
@@ -117,8 +122,9 @@ private:
QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher;
QStringList m_gdbCheckPaths;
QFutureWatcher<QVector<AndroidDeviceInfo>> m_virtualDevicesWatcher;
QFutureWatcher<AndroidDeviceInfoList> m_virtualDevicesWatcher;
QString m_lastAddedAvd;
std::unique_ptr<AndroidAvdManager> m_avdManager;
};
} // namespace Internal
+54 -2
View File
@@ -237,6 +237,14 @@
</layout>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="CreateKitCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@@ -252,6 +260,8 @@
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
@@ -289,18 +299,60 @@
</layout>
</item>
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="UseGradleCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use Gradle instead of Ant</string>
<string>Use Gradle instead of Ant (Ant builds are deprecated)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="deprecatedInfoIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Gradle builds are forced from Android SDK tools version 25.3.0 onwards as Ant scripts are no longer available.</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="11" column="0">
<widget class="QLabel" name="AntLocationLabel">
<property name="sizePolicy">
+346
View File
@@ -0,0 +1,346 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidtoolmanager.h"
#include "androidmanager.h"
#include "utils/algorithm.h"
#include "utils/environment.h"
#include "utils/qtcassert.h"
#include "utils/runextensions.h"
#include "utils/synchronousprocess.h"
#include <QLoggingCategory>
namespace {
Q_LOGGING_CATEGORY(androidToolLog, "qtc.android.sdkManager")
}
namespace Android {
namespace Internal {
using namespace Utils;
class AndroidToolOutputParser
{
public:
void parseTargetListing(const QString &output, const FileName &sdkLocation,
SdkPlatformList *platformList);
QList<SdkPlatform> m_installedPlatforms;
};
/*!
Runs the \c android tool located at \a toolPath with arguments \a args and environment \a
environment. Returns \c true for successful execution. Command's output is copied to \a
output.
*/
static bool androidToolCommand(Utils::FileName toolPath, const QStringList &args,
const Environment &environment, QString *output)
{
QString androidToolPath = toolPath.toString();
SynchronousProcess proc;
proc.setProcessEnvironment(environment.toProcessEnvironment());
SynchronousProcessResponse response = proc.runBlocking(androidToolPath, args);
if (response.result == SynchronousProcessResponse::Finished) {
if (output)
*output = response.allOutput();
return true;
}
return false;
}
static QStringList cleanAndroidABIs(const QStringList &abis)
{
QStringList res;
foreach (const QString &abi, abis) {
int index = abi.lastIndexOf(QLatin1Char('/'));
if (index == -1)
res << abi;
else
res << abi.mid(index + 1);
}
return res;
}
AndroidToolManager::AndroidToolManager(const AndroidConfig &config) :
m_config(config),
m_parser(new AndroidToolOutputParser)
{
}
AndroidToolManager::~AndroidToolManager()
{
}
SdkPlatformList AndroidToolManager::availableSdkPlatforms() const
{
SdkPlatformList list;
QString targetListing;
if (androidToolCommand(m_config.androidToolPath(), QStringList({"list", "target"}),
androidToolEnvironment(), &targetListing)) {
m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), &list);
} else {
qCDebug(androidToolLog) << "Android tool target listing failed";
}
return list;
}
void AndroidToolManager::launchAvdManager() const
{
QProcess::startDetached(m_config.androidToolPath().toString(), QStringList("avd"));
}
QFuture<AndroidConfig::CreateAvdInfo>
AndroidToolManager::createAvd(AndroidConfig::CreateAvdInfo info) const
{
return Utils::runAsync(&AndroidToolManager::createAvdImpl, info,
m_config.androidToolPath(), androidToolEnvironment());
}
bool AndroidToolManager::removeAvd(const QString &name) const
{
SynchronousProcess proc;
proc.setTimeoutS(5);
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
SynchronousProcessResponse response
= proc.runBlocking(m_config.androidToolPath().toString(),
QStringList({"delete", "avd", "-n", name}));
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
}
QFuture<AndroidDeviceInfoList> AndroidToolManager::androidVirtualDevicesFuture() const
{
return Utils::runAsync(&AndroidToolManager::androidVirtualDevices,
m_config.androidToolPath(), m_config.sdkLocation(),
androidToolEnvironment());
}
Environment AndroidToolManager::androidToolEnvironment() const
{
Environment env = Environment::systemEnvironment();
Utils::FileName jdkLocation = m_config.openJDKLocation();
if (!jdkLocation.isEmpty()) {
env.set(QLatin1String("JAVA_HOME"), jdkLocation.toUserOutput());
Utils::FileName binPath = jdkLocation;
binPath.appendPath(QLatin1String("bin"));
env.prependOrSetPath(binPath.toUserOutput());
}
return env;
}
AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::CreateAvdInfo info,
FileName androidToolPath,
Environment env)
{
QProcess proc;
proc.setProcessEnvironment(env.toProcessEnvironment());
QStringList arguments;
arguments << QLatin1String("create") << QLatin1String("avd")
<< QLatin1String("-t") << info.target.name
<< QLatin1String("-n") << info.name
<< QLatin1String("-b") << info.abi;
if (info.sdcardSize > 0)
arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize);
proc.start(androidToolPath.toString(), arguments);
if (!proc.waitForStarted()) {
info.error = tr("Could not start process \"%1 %2\"")
.arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
return info;
}
QTC_CHECK(proc.state() == QProcess::Running);
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
QByteArray question;
while (true) {
proc.waitForReadyRead(500);
question += proc.readAllStandardOutput();
if (question.endsWith(QByteArray("]:"))) {
// truncate to last line
int index = question.lastIndexOf(QByteArray("\n"));
if (index != -1)
question = question.mid(index);
if (question.contains("hw.gpu.enabled"))
proc.write(QByteArray("yes\n"));
else
proc.write(QByteArray("\n"));
question.clear();
}
if (proc.state() != QProcess::Running)
break;
}
QTC_CHECK(proc.state() == QProcess::NotRunning);
QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
// The exit code is always 0, so we need to check stderr
// For now assume that any output at all indicates a error
if (!errorOutput.isEmpty()) {
info.error = errorOutput;
}
return info;
}
AndroidDeviceInfoList AndroidToolManager::androidVirtualDevices(const Utils::FileName &androidTool,
const FileName &sdkLocationPath,
const Environment &environment)
{
AndroidDeviceInfoList devices;
QString output;
if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), environment, &output))
return devices;
QStringList avds = output.split('\n');
if (avds.empty())
return devices;
while (avds.first().startsWith(QLatin1String("* daemon")))
avds.removeFirst(); // remove the daemon logs
avds.removeFirst(); // remove "List of devices attached" header line
bool nextLineIsTargetLine = false;
AndroidDeviceInfo dev;
for (int i = 0; i < avds.size(); i++) {
QString line = avds.at(i);
if (!line.contains(QLatin1String("Name:")))
continue;
int index = line.indexOf(QLatin1Char(':')) + 2;
if (index >= line.size())
break;
dev.avdname = line.mid(index).trimmed();
dev.sdk = -1;
dev.cpuAbi.clear();
++i;
for (; i < avds.size(); ++i) {
line = avds.at(i);
if (line.contains(QLatin1String("---------")))
break;
if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) {
if (line.contains(QLatin1String("Google APIs"))) {
nextLineIsTargetLine = true;
continue;
}
nextLineIsTargetLine = false;
int lastIndex = line.lastIndexOf(QLatin1Char(' '));
if (lastIndex == -1) // skip line
break;
QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed();
Utils::FileName platformPath = sdkLocationPath;
platformPath.appendPath(QString("/platforms/android-%1").arg(tmp));
dev.sdk = AndroidManager::findApiLevel(platformPath);
}
if (line.contains(QLatin1String("Tag/ABI:"))) {
int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1;
if (lastIndex >= line.size())
break;
dev.cpuAbi = QStringList(line.mid(lastIndex));
} else if (line.contains(QLatin1String("ABI:"))) {
int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1;
if (lastIndex >= line.size())
break;
dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed());
}
}
// armeabi-v7a devices can also run armeabi code
if (dev.cpuAbi == QStringList("armeabi-v7a"))
dev.cpuAbi << QLatin1String("armeabi");
dev.state = AndroidDeviceInfo::OkState;
dev.type = AndroidDeviceInfo::Emulator;
if (dev.cpuAbi.isEmpty() || dev.sdk == -1)
continue;
devices.push_back(dev);
}
Utils::sort(devices);
return devices;
}
void AndroidToolOutputParser::parseTargetListing(const QString &output,
const Utils::FileName &sdkLocation,
SdkPlatformList *platformList)
{
if (!platformList)
return;
auto addSystemImage = [](const QStringList& abiList, SdkPlatform &platform) {
foreach (auto imageAbi, abiList) {
SystemImage image;
image.abiName = imageAbi;
image.apiLevel = platform.apiLevel;
platform.systemImages.append(image);
}
};
SdkPlatform platform;
QStringList abiList;
foreach (const QString &l, output.split('\n')) {
const QString line = l.trimmed();
if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) {
int index = line.indexOf(QLatin1String("\"android-"));
if (index == -1)
continue;
QString androidTarget = line.mid(index + 1, line.length() - index - 2);
const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1);
Utils::FileName platformPath = sdkLocation;
platformPath.appendPath(QString("/platforms/android-%1").arg(tmp));
platform.installedLocation = platformPath;
platform.apiLevel = AndroidManager::findApiLevel(platformPath);
} else if (line.startsWith(QLatin1String("Name:"))) {
platform.name = line.mid(6);
} else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
abiList = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
} else if (line.startsWith(QLatin1String("ABIs"))) {
abiList = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
} else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
if (platform.apiLevel == -1)
continue;
addSystemImage(abiList, platform);
*platformList << platform;
platform = SdkPlatform();
abiList.clear();
}
}
// The last parsed Platform.
if (platform.apiLevel != -1) {
addSystemImage(abiList, platform);
*platformList << platform;
}
Utils::sort(*platformList);
}
} // namespace Internal
} // namespace Android
+72
View File
@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "utils/fileutils.h"
#include "androidconfigurations.h"
#include <QStringList>
#include <memory>
namespace Android {
class AndroidConfig;
namespace Internal {
class AndroidToolOutputParser;
/*!
Wraps the \c android tool's usage. The tool itself is deprecated since SDK tools version 25.3.0.
*/
class AndroidToolManager
{
Q_DECLARE_TR_FUNCTIONS(AndroidToolManager)
public:
AndroidToolManager(const AndroidConfig &config);
~AndroidToolManager();
SdkPlatformList availableSdkPlatforms() const;
void launchAvdManager() const;
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
bool removeAvd(const QString &name) const;
QFuture<AndroidDeviceInfoList> androidVirtualDevicesFuture() const;
// Helper methods
private:
Utils::Environment androidToolEnvironment() const;
static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info,
Utils::FileName androidToolPath, Utils::Environment env);
static AndroidDeviceInfoList androidVirtualDevices(const Utils::FileName &androidTool,
const Utils::FileName &sdkLlocationPath,
const Utils::Environment &environment);
private:
const AndroidConfig &m_config;
std::unique_ptr<AndroidToolOutputParser> m_parser;
};
} // namespace Internal
} // namespace Android
+19 -10
View File
@@ -26,6 +26,7 @@
#include "avddialog.h"
#include "androidconfigurations.h"
#include <utils/algorithm.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
@@ -67,12 +68,12 @@ AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidCo
bool AvdDialog::isValid() const
{
return !name().isEmpty() && !target().isEmpty() && !abi().isEmpty();
return !name().isEmpty() && target().isValid() && !abi().isEmpty();
}
QString AvdDialog::target() const
SdkPlatform AvdDialog::target() const
{
return m_avdDialog.targetComboBox->currentText();
return m_avdDialog.targetComboBox->currentData().value<SdkPlatform>();
}
QString AvdDialog::name() const
@@ -92,15 +93,23 @@ int AvdDialog::sdcardSize() const
void AvdDialog::updateApiLevelComboBox()
{
QList<SdkPlatform> filteredList;
QList<SdkPlatform> platforms = m_config->sdkTargets(m_minApiLevel);
foreach (const SdkPlatform &platform, platforms) {
if (platform.abis.contains(abi()))
filteredList << platform;
}
SdkPlatformList filteredList;
SdkPlatformList platforms = m_config->sdkTargets(m_minApiLevel);
QString selectedAbi = abi();
auto hasAbi = [selectedAbi](const SystemImage &image) {
return image.isValid() && (image.abiName == selectedAbi);
};
filteredList = Utils::filtered(platforms, [hasAbi](const SdkPlatform &platform) {
return Utils::anyOf(platform.systemImages,hasAbi);
});
m_avdDialog.targetComboBox->clear();
m_avdDialog.targetComboBox->addItems(AndroidConfig::apiLevelNamesFor(filteredList));
foreach (const SdkPlatform &platform, filteredList) {
m_avdDialog.targetComboBox->addItem(AndroidConfig::apiLevelNameFor(platform),
QVariant::fromValue<SdkPlatform>(platform));
}
if (platforms.isEmpty()) {
m_avdDialog.warningIcon->setVisible(true);
+2 -1
View File
@@ -32,6 +32,7 @@
namespace Android {
class AndroidConfig;
class SdkPlatform;
namespace Internal {
@@ -42,7 +43,7 @@ public:
explicit AvdDialog(int minApiLevel, const QString &targetArch,
const AndroidConfig *config, QWidget *parent = 0);
QString target() const;
Android::SdkPlatform target() const;
QString name() const;
QString abi() const;
int sdcardSize() const;
+1 -5
View File
@@ -130,6 +130,7 @@ void TestConfiguration::completeTestInformation(int runMode)
m_executableFile = exeString;
m_project = project;
m_guessedConfiguration = true;
m_guessedFrom = rc->displayName();
if (runMode == TestRunner::Debug)
m_runConfig = new TestRunConfiguration(rc->target(), this);
}
@@ -203,11 +204,6 @@ void TestConfiguration::setProject(Project *project)
m_project = project;
}
void TestConfiguration::setGuessedConfiguration(bool guessed)
{
m_guessedConfiguration = guessed;
}
QString TestConfiguration::executableFilePath() const
{
if (m_executableFile.isEmpty())
+4 -2
View File
@@ -66,7 +66,6 @@ public:
void setDisplayName(const QString &displayName);
void setEnvironment(const Utils::Environment &env);
void setProject(ProjectExplorer::Project *project);
void setGuessedConfiguration(bool guessed);
QStringList testCases() const { return m_testCases; }
int testCaseCount() const { return m_testCaseCount; }
@@ -77,7 +76,9 @@ public:
Utils::Environment environment() const { return m_environment; }
ProjectExplorer::Project *project() const { return m_project.data(); }
TestRunConfiguration *runConfiguration() const { return m_runConfig; }
bool guessedConfiguration() const { return m_guessedConfiguration; }
bool isGuessed() const { return m_guessedConfiguration; }
QString runConfigDisplayName() const { return m_guessedConfiguration ? m_guessedFrom
: m_displayName; }
virtual TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
QProcess *app) const = 0;
@@ -91,6 +92,7 @@ private:
QString m_workingDir;
QString m_buildDir;
QString m_displayName;
QString m_guessedFrom;
Utils::Environment m_environment;
QPointer<ProjectExplorer::Project> m_project;
bool m_guessedConfiguration = false;
+37 -7
View File
@@ -43,6 +43,7 @@
#include <utils/outputformat.h>
#include <utils/runextensions.h>
#include <utils/hostosinfo.h>
#include <QFuture>
#include <QFutureInterface>
@@ -96,6 +97,28 @@ void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
m_selectedTests = selected;
}
static QString processInformation(const QProcess &proc)
{
QString information("\nCommand line: " + proc.program() + ' ' + proc.arguments().join(' '));
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();
for (const QString &var : important)
information.append('\n' + var + ": " + environment.value(var));
return information;
}
static QString rcInfo(const TestConfiguration * const config)
{
QString info = '\n' + TestRunner::tr("Run configuration:") + ' ';
if (config->isGuessed())
info += TestRunner::tr("guessed from");
return info + " \"" + config->runConfigDisplayName() + '"';
}
static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
const QList<TestConfiguration *> selectedTests,
const TestSettings &settings)
@@ -108,11 +131,14 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
config->completeTestInformation(TestRunner::Run);
if (config->project()) {
testCaseCount += config->testCaseCount();
if (!omitRunConfigWarnings && config->guessedConfiguration()) {
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
TestRunner::tr("Project's run configuration was guessed for \"%1\".\n"
"This might cause trouble during execution."
).arg(config->displayName()))));
if (!omitRunConfigWarnings && config->isGuessed()) {
QString message = TestRunner::tr(
"Project's run configuration was guessed for \"%1\".\n"
"This might cause trouble during execution.\n"
"(guessed from \"%2\")");
message = message.arg(config->displayName()).arg(config->runConfigDisplayName());
futureInterface.reportResult(
TestResultPtr(new FaultyTestResult(Result::MessageWarn, message)));
}
} else {
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
@@ -173,11 +199,15 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
}
} else {
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageFatal,
TestRunner::tr("Failed to start test for project \"%1\".").arg(testConfiguration->displayName()))));
TestRunner::tr("Failed to start test for project \"%1\".")
.arg(testConfiguration->displayName()) + processInformation(testProcess)
+ rcInfo(testConfiguration))));
}
if (testProcess.exitStatus() == QProcess::CrashExit) {
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageFatal,
TestRunner::tr("Test for project \"%1\" crashed.").arg(testConfiguration->displayName()))));
TestRunner::tr("Test for project \"%1\" crashed.")
.arg(testConfiguration->displayName()) + processInformation(testProcess)
+ rcInfo(testConfiguration))));
}
if (canceledByTimeout) {
@@ -75,6 +75,7 @@ AutotoolsProject::AutotoolsProject(const Utils::FileName &fileName) :
setId(Constants::AUTOTOOLS_PROJECT_ID);
setProjectContext(Core::Context(Constants::PROJECT_CONTEXT));
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
setDisplayName(projectDirectory().fileName());
}
AutotoolsProject::~AutotoolsProject()
@@ -90,11 +91,6 @@ AutotoolsProject::~AutotoolsProject()
}
}
QString AutotoolsProject::displayName() const
{
return projectFilePath().toFileInfo().absoluteDir().dirName();
}
QString AutotoolsProject::defaultBuildDirectory(const QString &projectPath)
{
return QFileInfo(projectPath).absolutePath();
@@ -57,7 +57,6 @@ public:
explicit AutotoolsProject(const Utils::FileName &fileName);
~AutotoolsProject() override;
QString displayName() const override;
static QString defaultBuildDirectory(const QString &projectPath);
QStringList buildTargets() const;
@@ -42,9 +42,3 @@ bool AutotoolsProjectNode::showInSimpleTree() const
{
return true;
}
QList<ProjectAction> AutotoolsProjectNode::supportedActions(Node *node) const
{
Q_UNUSED(node);
return QList<ProjectAction>();
}
@@ -51,7 +51,6 @@ public:
AutotoolsProjectNode(const Utils::FileName &projectDirectory);
bool showInSimpleTree() const override;
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override;
};
} // namespace Internal
+1 -1
View File
@@ -215,7 +215,7 @@ void BeautifierPlugin::extensionsInitialized()
addAutoReleasedObject(object);
}
m_generalSettings = new GeneralSettings;
m_generalSettings.reset(new GeneralSettings);
auto settingsPage = new GeneralOptionsPage(m_generalSettings, toolIds, this);
addAutoReleasedObject(settingsPage);
+2 -1
View File
@@ -31,6 +31,7 @@
#include <QPlainTextEdit>
#include <QPointer>
#include <QSharedPointer>
namespace Core {
class IDocument;
@@ -86,7 +87,7 @@ public:
private:
void updateActions(Core::IEditor *editor = nullptr);
QList<BeautifierAbstractTool *> m_tools;
GeneralSettings *m_generalSettings = nullptr;
QSharedPointer<GeneralSettings> m_generalSettings;
QHash<QObject*, QMetaObject::Connection> m_autoFormatConnections;
void formatEditor(TextEditor::TextEditorWidget *editor, const Command &command,
int startPos = -1, int endPos = 0);
@@ -36,7 +36,7 @@
namespace Beautifier {
namespace Internal {
GeneralOptionsPageWidget::GeneralOptionsPageWidget(GeneralSettings *settings,
GeneralOptionsPageWidget::GeneralOptionsPageWidget(const QSharedPointer<GeneralSettings> &settings,
const QStringList &toolIds, QWidget *parent) :
QWidget(parent),
ui(new Ui::GeneralOptionsPage),
@@ -73,8 +73,8 @@ void GeneralOptionsPageWidget::apply(bool *autoFormatChanged)
m_settings->save();
}
GeneralOptionsPage::GeneralOptionsPage(GeneralSettings *settings, const QStringList &toolIds,
QObject *parent) :
GeneralOptionsPage::GeneralOptionsPage(const QSharedPointer<GeneralSettings> &settings,
const QStringList &toolIds, QObject *parent) :
IOptionsPage(parent),
m_settings(settings),
m_toolIds(toolIds)
+7 -6
View File
@@ -28,6 +28,7 @@
#include <coreplugin/dialogs/ioptionspage.h>
#include <QPointer>
#include <QSharedPointer>
#include <QWidget>
namespace Beautifier {
@@ -42,15 +43,15 @@ class GeneralOptionsPageWidget : public QWidget
Q_OBJECT
public:
explicit GeneralOptionsPageWidget(GeneralSettings *settings, const QStringList &toolIds,
QWidget *parent = nullptr);
explicit GeneralOptionsPageWidget(const QSharedPointer<GeneralSettings> &settings,
const QStringList &toolIds, QWidget *parent = nullptr);
virtual ~GeneralOptionsPageWidget();
void restore();
void apply(bool *autoFormatChanged);
private:
Ui::GeneralOptionsPage *ui;
GeneralSettings *m_settings;
QSharedPointer<GeneralSettings> m_settings;
};
class GeneralOptionsPage : public Core::IOptionsPage
@@ -58,8 +59,8 @@ class GeneralOptionsPage : public Core::IOptionsPage
Q_OBJECT
public:
explicit GeneralOptionsPage(GeneralSettings *settings, const QStringList &toolIds,
QObject *parent = nullptr);
explicit GeneralOptionsPage(const QSharedPointer<GeneralSettings> &settings,
const QStringList &toolIds, QObject *parent = nullptr);
QWidget *widget() override;
void apply() override;
void finish() override;
@@ -69,7 +70,7 @@ signals:
private:
QPointer<GeneralOptionsPageWidget> m_widget;
GeneralSettings *m_settings;
QSharedPointer<GeneralSettings> m_settings;
QStringList m_toolIds;
};
+26 -70
View File
@@ -101,7 +101,7 @@ public:
CPlusPlus::Overview overview;
//! timer
QPointer<QTimer> timer;
QTimer timer;
// documents
//! Documents read write lock
@@ -161,16 +161,14 @@ Parser::Parser(QObject *parent)
: QObject(parent),
d(new ParserPrivate())
{
d->timer = new QTimer(this);
d->timer->setObjectName(QLatin1String("ClassViewParser::timer"));
d->timer->setSingleShot(true);
d->timer.setSingleShot(true);
// connect signal/slots
// internal data reset
connect(this, &Parser::resetDataDone, this, &Parser::onResetDataDone, Qt::QueuedConnection);
// timer for emitting changes
connect(d->timer.data(), &QTimer::timeout, this, &Parser::requestCurrentState, Qt::QueuedConnection);
connect(&d->timer, &QTimer::timeout, this, &Parser::requestCurrentState, Qt::QueuedConnection);
}
/*!
@@ -294,11 +292,12 @@ ParserTreeItem::ConstPtr Parser::parse()
item = ParserTreeItem::Ptr(new ParserTreeItem());
if (d->flatMode)
addFlatTree(item, prj->rootProjectNode());
addFlatTree(item, prj);
else
addProjectNode(item, prj->rootProjectNode());
addProjectTree(item, prj);
item->setIcon(prj->containerNode()->icon());
item->setIcon(prj->rootProjectNode()->icon());
rootItem->appendChild(item, inf);
}
@@ -542,10 +541,8 @@ void Parser::parseDocument(const CPlusPlus::Document::Ptr &doc)
getParseDocumentTree(doc);
QTC_ASSERT(d->timer, return);
if (!d->timer->isActive())
d->timer->start(400); //! Delay in msecs before an update
if (!d->timer.isActive())
d->timer.start(400); //! Delay in msecs before an update
return;
}
@@ -688,7 +685,7 @@ void Parser::requestCurrentState()
void Parser::emitCurrentTree()
{
// stop timer if it is active right now
d->timer->stop();
d->timer.stop();
d->rootItemLocker.lockForWrite();
d->rootItem = parse();
@@ -702,26 +699,6 @@ void Parser::emitCurrentTree()
emit treeDataUpdate(std);
}
/*!
Generates a project node file list for the root node \a node.
*/
QStringList Parser::projectNodeFileList(const FolderNode *folderNode) const
{
QStringList list;
folderNode->forEachNode(
[&](FileNode *node) {
if (!node->isGenerated())
list.append(node->filePath().toString());
},
{},
[&](const FolderNode *node) {
return node->nodeType() == NodeType::Folder || node->nodeType() == NodeType::VirtualFolder;
}
);
return list;
}
/*!
Generates projects like the Project Explorer.
\a item specifies the item and \a node specifies the root node.
@@ -729,84 +706,63 @@ QStringList Parser::projectNodeFileList(const FolderNode *folderNode) const
Returns a list of projects which were added to the item.
*/
QStringList Parser::addProjectNode(const ParserTreeItem::Ptr &item, const ProjectNode *node)
QStringList Parser::addProjectTree(const ParserTreeItem::Ptr &item, const Project *project)
{
QStringList projectList;
if (!node)
if (!project)
return projectList;
const QString nodePath = node->filePath().toString();
const QString projectPath = project->projectFilePath().toString();
// our own files
QStringList fileList;
CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(nodePath);
CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(projectPath);
// try to improve parsing speed by internal cache
if (cit != d->cachedPrjFileLists.constEnd()) {
fileList = cit.value();
} else {
fileList = projectNodeFileList(node);
d->cachedPrjFileLists[nodePath] = fileList;
fileList = project->files(Project::SourceFiles);
d->cachedPrjFileLists[projectPath] = fileList;
}
if (fileList.count() > 0) {
addProject(item, fileList, node->filePath().toString());
projectList << node->filePath().toString();
}
// subnodes
for (const Node *n : node->nodes()) {
if (const ProjectNode *project = n->asProjectNode()) {
ParserTreeItem::Ptr itemPrj(new ParserTreeItem());
SymbolInformation information(project->displayName(), project->filePath().toString());
projectList += addProjectNode(itemPrj, project);
itemPrj->setIcon(project->icon());
// append child if item is not null and there is at least 1 child
if (!item.isNull() && itemPrj->childCount() > 0)
item->appendChild(itemPrj, information);
}
addProject(item, fileList, projectPath);
projectList << projectPath;
}
return projectList;
}
QStringList Parser::getAllFiles(const ProjectNode *node)
QStringList Parser::getAllFiles(const Project *project)
{
QStringList fileList;
if (!node)
if (!project)
return fileList;
const QString nodePath = node->filePath().toString();
const QString nodePath = project->projectFilePath().toString();
CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(nodePath);
// try to improve parsing speed by internal cache
if (cit != d->cachedPrjFileLists.constEnd()) {
fileList = cit.value();
} else {
fileList = projectNodeFileList(node);
fileList = project->files(Project::SourceFiles);
d->cachedPrjFileLists[nodePath] = fileList;
}
// subnodes
for (const Node *n : node->nodes())
if (const ProjectNode *project = n->asProjectNode())
fileList += getAllFiles(project);
return fileList;
}
void Parser::addFlatTree(const ParserTreeItem::Ptr &item, const ProjectNode *node)
void Parser::addFlatTree(const ParserTreeItem::Ptr &item, const Project *project)
{
if (!node)
if (!project)
return;
QStringList fileList = getAllFiles(node);
QStringList fileList = getAllFiles(project);
fileList.removeDuplicates();
if (fileList.count() > 0) {
addProject(item, fileList, node->filePath().toString());
addProject(item, fileList, project->projectFilePath().toString());
}
}
+3 -7
View File
@@ -111,13 +111,9 @@ protected:
ParserTreeItem::ConstPtr findItemByRoot(const QStandardItem *item, bool skipRoot = false) const;
QStringList addProjectNode(const ParserTreeItem::Ptr &item,
const ProjectExplorer::ProjectNode *node);
QStringList getAllFiles(const ProjectExplorer::ProjectNode *node);
void addFlatTree(const ParserTreeItem::Ptr &item,
const ProjectExplorer::ProjectNode *node);
QStringList projectNodeFileList(const ProjectExplorer::FolderNode *node) const;
QStringList addProjectTree(const ParserTreeItem::Ptr &item, const ProjectExplorer::Project *project);
QStringList getAllFiles(const ProjectExplorer::Project *project);
void addFlatTree(const ParserTreeItem::Ptr &item, const ProjectExplorer::Project *project);
private:
//! Private class data pointer
+2 -2
View File
@@ -257,7 +257,7 @@ FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const
const QString absFile =
viewRootDir.absoluteFilePath(
QDir::fromNativeSeparators(buffer.left(atatpos)));
QTC_CHECK(QFile(absFile).exists());
QTC_CHECK(QFileInfo::exists(absFile));
QTC_CHECK(!absFile.isEmpty());
// "cleartool ls" of a derived object looks like this:
@@ -274,7 +274,7 @@ FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const
else
return FileStatus::CheckedIn;
} else {
QTC_CHECK(QFile(fileName).exists());
QTC_CHECK(QFileInfo::exists(fileName));
QTC_CHECK(!fileName.isEmpty());
return FileStatus::NotManaged;
}
+1 -1
View File
@@ -92,7 +92,7 @@ void ClearCaseSync::processCleartoolLsLine(const QDir &viewRootDir, const QStrin
const QString absFile =
viewRootDir.absoluteFilePath(
QDir::fromNativeSeparators(buffer.left(atatpos)));
QTC_CHECK(QFile(absFile).exists());
QTC_CHECK(QFileInfo::exists(absFile));
QTC_CHECK(!absFile.isEmpty());
QString ccState;
@@ -149,10 +149,7 @@ void CMakeBuildConfiguration::ctor()
emit dataAvailable();
});
connect(m_buildDirManager.get(), &BuildDirManager::errorOccured,
this, [this, project](const QString &msg) {
project->updateProjectData(this);
setError(msg);
});
this, &CMakeBuildConfiguration::setError);
connect(m_buildDirManager.get(), &BuildDirManager::configurationStarted,
this, [this, project]() {
project->handleParsingStarted();
@@ -258,6 +258,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
updateFromKit();
connect(m_buildConfiguration->target(), &ProjectExplorer::Target::kitChanged,
this, &CMakeBuildSettingsWidget::updateFromKit);
connect(m_buildConfiguration, &CMakeBuildConfiguration::enabledChanged,
this, [this]() { setError(m_buildConfiguration->disabledReason()); });
}
void CMakeBuildSettingsWidget::setError(const QString &message)
@@ -444,19 +444,26 @@ QString CMakeBuildStepConfigWidget::displayName() const
return tr("Build", "CMakeProjectManager::CMakeBuildStepConfigWidget display name.");
}
static void createSpecialItem(const QString &text, const QString &data, QListWidget *parent)
{
auto item = new QListWidgetItem(text, parent);
item->setData(Qt::UserRole, data);
QFont f;
f.setItalic(true);
item->setFont(f);
}
void CMakeBuildStepConfigWidget::buildTargetsChanged()
{
const bool wasBlocked = m_buildTargetsList->blockSignals(true);
m_buildTargetsList->clear();
auto item = new QListWidgetItem(tr(ADD_RUNCONFIGURATION_TEXT), m_buildTargetsList);
createSpecialItem(tr(ADD_RUNCONFIGURATION_TEXT), ADD_RUNCONFIGURATION_TEXT, m_buildTargetsList);
createSpecialItem(tr("all"), "all", m_buildTargetsList);
createSpecialItem(tr("clean"), "clean", m_buildTargetsList);
item->setData(Qt::UserRole, QString::fromLatin1(ADD_RUNCONFIGURATION_TEXT));
QFont f;
f.setItalic(true);
item->setFont(f);
CMakeProject *pro = static_cast<CMakeProject *>(m_buildStep->project());
auto pro = static_cast<CMakeProject *>(m_buildStep->project());
QStringList targetList = pro->buildTargetTitles();
targetList.sort();
@@ -180,15 +180,6 @@ bool CMakeCbpParser::parseCbpFile(CMakeTool::PathMapper mapper, const FileName &
fi.close();
// There is always a clean target:
CMakeBuildTarget cleanTarget;
cleanTarget.title = "clean";
cleanTarget.targetType = UtilityType;
cleanTarget.workingDirectory = m_buildDirectory;
cleanTarget.sourceDirectory = m_sourceDirectory;
m_buildTargets.append(cleanTarget);
return true;
}
return false;
@@ -79,6 +79,7 @@ CMakeProject::CMakeProject(const FileName &fileName) : Project(Constants::CMAKEM
setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID);
setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT));
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
setDisplayName(projectDirectory().fileName());
connect(this, &CMakeProject::activeTargetChanged, this, &CMakeProject::handleActiveTargetChanged);
connect(&m_treeScanner, &TreeScanner::finished, this, &CMakeProject::handleTreeScanningFinished);
@@ -145,8 +146,10 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc)
Kit *k = t->kit();
auto newRoot = bc->generateProjectTree(m_allFiles);
if (newRoot)
if (newRoot) {
setRootProjectNode(newRoot);
setDisplayName(newRoot->displayName());
}
updateApplicationAndDeploymentTargets();
updateTargetRunConfigurations(t);
@@ -296,12 +299,6 @@ bool CMakeProject::hasBuildTarget(const QString &title) const
return anyOf(buildTargets(), [title](const CMakeBuildTarget &ct) { return ct.title == title; });
}
QString CMakeProject::displayName() const
{
auto root = dynamic_cast<CMakeProjectNode *>(rootProjectNode());
return root ? root->displayName() : projectDirectory().fileName();
}
Project::RestoreResult CMakeProject::fromMap(const QVariantMap &map, QString *errorMessage)
{
RestoreResult result = Project::fromMap(map, errorMessage);
@@ -86,8 +86,6 @@ public:
explicit CMakeProject(const Utils::FileName &filename);
~CMakeProject() final;
QString displayName() const final;
QStringList buildTargetTitles(bool runnable = false) const;
bool hasBuildTarget(const QString &title) const;
@@ -57,7 +57,7 @@ CMakeManager::CMakeManager() :
m_runCMakeAction(new QAction(QIcon(), tr("Run CMake"), this)),
m_clearCMakeCacheAction(new QAction(QIcon(), tr("Clear CMake Configuration"), this)),
m_runCMakeActionContextMenu(new QAction(QIcon(), tr("Run CMake"), this)),
m_rescanProjectAction(new QAction(QIcon(), tr("Rescan project"), this))
m_rescanProjectAction(new QAction(QIcon(), tr("Rescan Project"), this))
{
Core::ActionContainer *mbuild =
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
@@ -56,12 +56,6 @@ bool CMakeInputsNode::showInSimpleTree() const
return false;
}
QList<ProjectExplorer::ProjectAction> CMakeInputsNode::supportedActions(ProjectExplorer::Node *node) const
{
Q_UNUSED(node);
return QList<ProjectExplorer::ProjectAction>();
}
CMakeListsNode::CMakeListsNode(const Utils::FileName &cmakeListPath) :
ProjectExplorer::ProjectNode(cmakeListPath)
{
@@ -80,12 +74,6 @@ bool CMakeListsNode::showInSimpleTree() const
return false;
}
QList<ProjectExplorer::ProjectAction> CMakeListsNode::supportedActions(ProjectExplorer::Node *node) const
{
Q_UNUSED(node);
return QList<ProjectExplorer::ProjectAction>();
}
CMakeProjectNode::CMakeProjectNode(const Utils::FileName &directory) :
ProjectExplorer::ProjectNode(directory)
{
@@ -103,12 +91,6 @@ QString CMakeProjectNode::tooltip() const
return QString();
}
QList<ProjectExplorer::ProjectAction> CMakeProjectNode::supportedActions(ProjectExplorer::Node *node) const
{
Q_UNUSED(node);
return QList<ProjectExplorer::ProjectAction>();
}
CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory) :
ProjectExplorer::ProjectNode(directory)
{
@@ -126,12 +108,6 @@ QString CMakeTargetNode::tooltip() const
return m_tooltip;
}
QList<ProjectExplorer::ProjectAction> CMakeTargetNode::supportedActions(ProjectExplorer::Node *node) const
{
Q_UNUSED(node);
return QList<ProjectExplorer::ProjectAction>();
}
void CMakeTargetNode::setTargetInformation(const QList<Utils::FileName> &artifacts,
const QString &type)
{
@@ -28,8 +28,6 @@
#include <projectexplorer/projectnodes.h>
namespace CMakeProjectManager {
class CMakeProject;
namespace Internal {
class CMakeInputsNode : public ProjectExplorer::ProjectNode
@@ -40,7 +38,6 @@ public:
static Utils::FileName inputsPathFromCMakeListsPath(const Utils::FileName &cmakeLists);
bool showInSimpleTree() const final;
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
};
class CMakeListsNode : public ProjectExplorer::ProjectNode
@@ -49,7 +46,6 @@ public:
CMakeListsNode(const Utils::FileName &cmakeListPath);
bool showInSimpleTree() const final;
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
};
class CMakeProjectNode : public ProjectExplorer::ProjectNode
@@ -59,7 +55,6 @@ public:
bool showInSimpleTree() const final;
QString tooltip() const final;
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
};
class CMakeTargetNode : public ProjectExplorer::ProjectNode
@@ -71,7 +66,6 @@ public:
bool showInSimpleTree() const final;
QString tooltip() const final;
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
private:
QString m_tooltip;
@@ -414,14 +414,16 @@ void ServerModeReader::extractConfigurationData(const QVariantMap &data)
{
const QString name = data.value(NAME_KEY).toString();
Q_UNUSED(name);
QSet<QString> knownTargets; // To filter duplicate target names:-/
const QVariantList projects = data.value("projects").toList();
for (const QVariant &p : projects) {
const QVariantMap pData = p.toMap();
m_projects.append(extractProjectData(pData));
m_projects.append(extractProjectData(pData, knownTargets));
}
}
ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMap &data)
ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMap &data,
QSet<QString> &knownTargets)
{
auto project = new Project;
project->name = data.value(NAME_KEY).toString();
@@ -430,16 +432,28 @@ ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMa
const QVariantList targets = data.value("targets").toList();
for (const QVariant &t : targets) {
const QVariantMap tData = t.toMap();
project->targets.append(extractTargetData(tData, project));
Target *tp = extractTargetData(tData, project, knownTargets);
if (tp)
project->targets.append(tp);
}
return project;
}
ServerModeReader::Target *ServerModeReader::extractTargetData(const QVariantMap &data, Project *p)
ServerModeReader::Target *ServerModeReader::extractTargetData(const QVariantMap &data, Project *p,
QSet<QString> &knownTargets)
{
const QString targetName = data.value(NAME_KEY).toString();
// Remove duplicate targets: CMake unfortunately does duplicate targets for all projects that
// contain them. Keep at least till cmake 3.9 is deprecated.
const int count = knownTargets.count();
knownTargets.insert(targetName);
if (knownTargets.count() == count)
return nullptr;
auto target = new Target;
target->project = p;
target->name = data.value(NAME_KEY).toString();
target->name = targetName;
target->sourceDirectory = FileName::fromString(data.value(SOURCE_DIRECTORY_KEY).toString());
target->buildDirectory = FileName::fromString(data.value("buildDirectory").toString());
@@ -664,7 +678,6 @@ void ServerModeReader::addHeaderNodes(ProjectNode *root, const QList<FileNode *>
{
auto headerNode = new VirtualFolderNode(root->filePath(), Node::DefaultPriority - 5);
headerNode->setDisplayName(tr("<Headers>"));
root->addNode(headerNode);
// knownHeaders are already listed in their targets:
QSet<Utils::FileName> seenHeaders = Utils::transform<QSet>(knownHeaders, &FileNode::filePath);
@@ -681,6 +694,11 @@ void ServerModeReader::addHeaderNodes(ProjectNode *root, const QList<FileNode *>
headerNode->addNestedNode(node);
}
}
if (headerNode->nodes().isEmpty())
delete headerNode; // No Headers, do not show this Folder.
else
root->addNode(headerNode);
}
} // namespace Internal

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