forked from qt-creator/qt-creator
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:
+2
-3
@@ -110,9 +110,8 @@ phony.c
|
|||||||
/dist/gdb/qtcreator-*/
|
/dist/gdb/qtcreator-*/
|
||||||
/dist/gdb/source/
|
/dist/gdb/source/
|
||||||
/dist/gdb/staging/
|
/dist/gdb/staging/
|
||||||
/doc/api/html/
|
/doc/qtcreator/
|
||||||
/doc/html-dev/
|
/doc/qtcreator-dev/
|
||||||
/doc/html/
|
|
||||||
/doc/pluginhowto/html/
|
/doc/pluginhowto/html/
|
||||||
/lib/
|
/lib/
|
||||||
/lib64/
|
/lib64/
|
||||||
|
|||||||
Vendored
+2
-1
@@ -21,7 +21,8 @@ Editing
|
|||||||
All Projects
|
All Projects
|
||||||
|
|
||||||
* Added support for `.qrc` files in project tree for 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)
|
* Added choice of build system to most project wizards (QTCREATORBUG-17308)
|
||||||
|
|
||||||
QMake Projects
|
QMake Projects
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
is not sufficient for your case, you can write wizards in code.
|
is not sufficient for your case, you can write wizards in code.
|
||||||
|
|
||||||
A wizard in \QC is an instance of a class implementing
|
A wizard in \QC is an instance of a class implementing
|
||||||
the Core::IWizardFactory interface that is registered with
|
the Core::IWizardFactory interface that has a creator function registered
|
||||||
ExtensionSystem::PluginManager.
|
with IWizardFactory::registerFactoryCreator.
|
||||||
|
|
||||||
Implementing wizards requires:
|
Implementing wizards requires:
|
||||||
\list
|
\list
|
||||||
|
|||||||
@@ -58,10 +58,13 @@
|
|||||||
|
|
||||||
\image qtcreator-gs-build-example-open.png "Selecting an example"
|
\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.
|
\li Select an example in the list of examples.
|
||||||
|
|
||||||
You can also search for examples. Enter the \uicontrol android or
|
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
|
examples tested for Android or iOS. To list examples that you can
|
||||||
run on embedded devices, enter the \uicontrol Boot2Qt keyword in the
|
run on embedded devices, enter the \uicontrol Boot2Qt keyword in the
|
||||||
search field (commercial only).
|
search field (commercial only).
|
||||||
@@ -83,8 +86,8 @@
|
|||||||
\li To see the compilation progress, press \key{Alt+4} to open the
|
\li To see the compilation progress, press \key{Alt+4} to open the
|
||||||
\uicontrol {Compile Output} pane.
|
\uicontrol {Compile Output} pane.
|
||||||
|
|
||||||
If build errors occur, check that a \l{Adding Qt Versions}
|
If build errors occur, check that a Qt version and
|
||||||
{Qt version} and \l{Adding Compilers}{compiler} are installed and
|
\l{Adding Compilers}{compiler} are installed and
|
||||||
configured and that the necessary kits are configured. If you are
|
configured and that the necessary kits are configured. If you are
|
||||||
building for an \l{Connecting Android Devices}{Android device} or
|
building for an \l{Connecting Android Devices}{Android device} or
|
||||||
\l{Connecting iOS Devices}{iOS device}, check that the development
|
\l{Connecting iOS Devices}{iOS device}, check that the development
|
||||||
|
|||||||
+8
-7
@@ -79,15 +79,16 @@ exists(src/shared/qbs/qbs.pro) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Create qbs documentation targets.
|
# Create qbs documentation targets.
|
||||||
QBS_DOCS_BUILD_DIR=$$IDE_DOC_PATH
|
DOC_FILES =
|
||||||
QBS_HTML_DOC_PATH=$$OUT_PWD/doc/html-qbs
|
DOC_TARGET_PREFIX = qbs_
|
||||||
QBS_DOCS_INSTALL_DIR=$$INSTALL_DOC_PATH
|
include(src/shared/qbs/doc/doc_shared.pri)
|
||||||
QDOC_BIN = $$QDOC
|
|
||||||
HELPGENERATOR = $$QHELPGENERATOR
|
|
||||||
include(src/shared/qbs/qbs_version.pri)
|
|
||||||
include(src/shared/qbs/doc/doc_targets.pri)
|
include(src/shared/qbs/doc/doc_targets.pri)
|
||||||
docs.depends += qbs_docs
|
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
|
contains(QT_ARCH, i386): ARCHITECTURE = x86
|
||||||
|
|||||||
@@ -134,9 +134,9 @@ if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then
|
|||||||
|
|
||||||
qbsapp="$app_path/Contents/MacOS/qbs"
|
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=$app_path/Contents/MacOS/qtdiag" \
|
||||||
"-executable=$resource_path/qtpromaker" \
|
"-executable=$resource_path/qtpromaker" \
|
||||||
"-executable=$resource_path/sdktool" \
|
"-executable=$resource_path/sdktool" \
|
||||||
|
|||||||
@@ -125,7 +125,6 @@ class Dumper(DumperBase):
|
|||||||
pass
|
pass
|
||||||
val.isBaseClass = val.name == val.type.name
|
val.isBaseClass = val.name == val.type.name
|
||||||
val.nativeValue = nativeValue
|
val.nativeValue = nativeValue
|
||||||
val.lIsInScope = True
|
|
||||||
val.laddress = nativeValue.address()
|
val.laddress = nativeValue.address()
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@@ -405,12 +404,12 @@ class Dumper(DumperBase):
|
|||||||
return cdbext.lookupType(name, module)
|
return cdbext.lookupType(name, module)
|
||||||
|
|
||||||
def reportResult(self, result, args):
|
def reportResult(self, result, args):
|
||||||
self.report('result={%s}' % (result))
|
cdbext.reportResult('result={%s}' % result)
|
||||||
|
|
||||||
def readRawMemory(self, address, size):
|
def readRawMemory(self, address, size):
|
||||||
mem = cdbext.readRawMemory(address, size)
|
mem = cdbext.readRawMemory(address, size)
|
||||||
if len(mem) != size:
|
if len(mem) != size:
|
||||||
raise Exception("Invalid memory request")
|
raise Exception("Invalid memory request: %d bytes from 0x%x" % (size, address))
|
||||||
return mem
|
return mem
|
||||||
|
|
||||||
def findStaticMetaObject(self, type):
|
def findStaticMetaObject(self, type):
|
||||||
@@ -440,7 +439,9 @@ class Dumper(DumperBase):
|
|||||||
|
|
||||||
variables = []
|
variables = []
|
||||||
for val in cdbext.listOfLocals(self.partialVariable):
|
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.handleLocals(variables)
|
||||||
self.handleWatches(args)
|
self.handleWatches(args)
|
||||||
|
|||||||
@@ -317,6 +317,8 @@ class DumperBase:
|
|||||||
self.nativeMixed = int(args.get('nativemixed', '0'))
|
self.nativeMixed = int(args.get('nativemixed', '0'))
|
||||||
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
||||||
self.partialVariable = args.get('partialvar', '')
|
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.partialUpdate = int(args.get('partial', '0'))
|
||||||
self.fallbackQtVersion = 0x50200
|
self.fallbackQtVersion = 0x50200
|
||||||
#warn('NAMESPACE: "%s"' % self.qtNamespace())
|
#warn('NAMESPACE: "%s"' % self.qtNamespace())
|
||||||
@@ -1497,7 +1499,10 @@ class DumperBase:
|
|||||||
def getJumpAddress_x86(dumper, address):
|
def getJumpAddress_x86(dumper, address):
|
||||||
relativeJumpCode = 0xe9
|
relativeJumpCode = 0xe9
|
||||||
jumpCode = 0xff
|
jumpCode = 0xff
|
||||||
data = dumper.readRawMemory(address, 6)
|
try:
|
||||||
|
data = dumper.readRawMemory(address, 6)
|
||||||
|
except:
|
||||||
|
return 0
|
||||||
primaryOpcode = data[0]
|
primaryOpcode = data[0]
|
||||||
if primaryOpcode == relativeJumpCode:
|
if primaryOpcode == relativeJumpCode:
|
||||||
# relative jump on 32 and 64 bit with a 32bit offset
|
# relative jump on 32 and 64 bit with a 32bit offset
|
||||||
@@ -3467,27 +3472,6 @@ class DumperBase:
|
|||||||
def target(self):
|
def target(self):
|
||||||
return self.typeData().ltarget
|
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):
|
def stripTypedefs(self):
|
||||||
if isinstance(self, self.dumper.Type) and self.code != TypeCodeTypedef:
|
if isinstance(self, self.dumper.Type) and self.code != TypeCodeTypedef:
|
||||||
#warn('NO TYPEDEF: %s' % self)
|
#warn('NO TYPEDEF: %s' % self)
|
||||||
|
|||||||
@@ -811,7 +811,10 @@ class Dumper(DumperBase):
|
|||||||
self.nativeMixed = int(args.get('nativemixed', 0))
|
self.nativeMixed = int(args.get('nativemixed', 0))
|
||||||
self.workingDirectory_ = args.get('workingdirectory', '')
|
self.workingDirectory_ = args.get('workingdirectory', '')
|
||||||
if self.workingDirectory_ == '':
|
if self.workingDirectory_ == '':
|
||||||
self.workingDirectory_ = os.getcwd()
|
try:
|
||||||
|
self.workingDirectory_ = os.getcwd()
|
||||||
|
except: # Could have been deleted in the mean time.
|
||||||
|
pass
|
||||||
|
|
||||||
self.ignoreStops = 0
|
self.ignoreStops = 0
|
||||||
self.silentStops = 0
|
self.silentStops = 0
|
||||||
|
|||||||
@@ -642,10 +642,10 @@ def qdump__QFiniteStack(d, value):
|
|||||||
def qdump__QFlags(d, value):
|
def qdump__QFlags(d, value):
|
||||||
i = value.split('{int}')[0]
|
i = value.split('{int}')[0]
|
||||||
enumType = value.type[0]
|
enumType = value.type[0]
|
||||||
if d.isLldb:
|
if d.isGdb:
|
||||||
d.putValue(i.cast(enumType.name).display())
|
|
||||||
else:
|
|
||||||
d.putValue(i.cast('enum ' + enumType.name).display())
|
d.putValue(i.cast('enum ' + enumType.name).display())
|
||||||
|
else:
|
||||||
|
d.putValue(i.cast(enumType.name).display())
|
||||||
d.putNumChild(0)
|
d.putNumChild(0)
|
||||||
|
|
||||||
|
|
||||||
@@ -2758,3 +2758,25 @@ def qdump__QSqlField(d, value):
|
|||||||
qdump__QVariant(d, val)
|
qdump__QVariant(d, val)
|
||||||
d.putBetterType(d.currentType.value.replace('QVariant', 'QSqlField'))
|
d.putBetterType(d.currentType.value.replace('QVariant', 'QSqlField'))
|
||||||
d.putPlainChildren(value)
|
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) {
|
if (oldValue.type() == QVariant::Url) {
|
||||||
QUrl url = oldValue.toUrl();
|
QUrl url = oldValue.toUrl();
|
||||||
QString path = url.toLocalFile();
|
QString path = url.toLocalFile();
|
||||||
if (QFileInfo(path).exists() && nodeInstanceServer() && !path.isEmpty())
|
if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty())
|
||||||
nodeInstanceServer()->removeFilePropertyFromFileSystemWatcher(object(), name, path);
|
nodeInstanceServer()->removeFilePropertyFromFileSystemWatcher(object(), name, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +437,7 @@ void ObjectNodeInstance::setPropertyVariant(const PropertyName &name, const QVar
|
|||||||
if (newValue.type() == QVariant::Url) {
|
if (newValue.type() == QVariant::Url) {
|
||||||
QUrl url = newValue.toUrl();
|
QUrl url = newValue.toUrl();
|
||||||
QString path = url.toLocalFile();
|
QString path = url.toLocalFile();
|
||||||
if (QFileInfo(path).exists() && nodeInstanceServer() && !path.isEmpty())
|
if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty())
|
||||||
nodeInstanceServer()->addFilePropertyToFileSystemWatcher(object(), name, path);
|
nodeInstanceServer()->addFilePropertyToFileSystemWatcher(object(), name, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -678,7 +678,7 @@ static inline QString fixComponentPathForIncompatibleQt(const QString &component
|
|||||||
const QString relativeImportPath = componentPath.right(componentPath.length() - index);
|
const QString relativeImportPath = componentPath.right(componentPath.length() - index);
|
||||||
QString fixedComponentPath = QLibraryInfo::location(QLibraryInfo::ImportsPath) + relativeImportPath;
|
QString fixedComponentPath = QLibraryInfo::location(QLibraryInfo::ImportsPath) + relativeImportPath;
|
||||||
fixedComponentPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
fixedComponentPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
||||||
if (QFileInfo(fixedComponentPath).exists())
|
if (QFileInfo::exists(fixedComponentPath))
|
||||||
return fixedComponentPath;
|
return fixedComponentPath;
|
||||||
QString fixedPath = QFileInfo(fixedComponentPath).path();
|
QString fixedPath = QFileInfo(fixedComponentPath).path();
|
||||||
if (fixedPath.endsWith(QLatin1String(".1.0"))) {
|
if (fixedPath.endsWith(QLatin1String(".1.0"))) {
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ QVariant fixResourcePaths(const QVariant &value)
|
|||||||
if (qrcDefintion.count() == 2) {
|
if (qrcDefintion.count() == 2) {
|
||||||
QString fixedPath = path;
|
QString fixedPath = path;
|
||||||
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
||||||
if (QFileInfo(fixedPath).exists()) {
|
if (QFileInfo::exists(fixedPath)) {
|
||||||
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
||||||
return QUrl::fromLocalFile(fixedPath);
|
return QUrl::fromLocalFile(fixedPath);
|
||||||
@@ -410,7 +410,7 @@ QVariant fixResourcePaths(const QVariant &value)
|
|||||||
if (qrcDefintion.count() == 2) {
|
if (qrcDefintion.count() == 2) {
|
||||||
QString fixedPath = str;
|
QString fixedPath = str;
|
||||||
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
||||||
if (QFileInfo(fixedPath).exists()) {
|
if (QFileInfo::exists(fixedPath)) {
|
||||||
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
||||||
return fixedPath;
|
return fixedPath;
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ QVariant fixResourcePaths(const QVariant &value)
|
|||||||
if (qrcDefintion.count() == 2) {
|
if (qrcDefintion.count() == 2) {
|
||||||
QString fixedPath = path;
|
QString fixedPath = path;
|
||||||
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
||||||
if (QFileInfo(fixedPath).exists()) {
|
if (QFileInfo::exists(fixedPath)) {
|
||||||
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
||||||
return QUrl::fromLocalFile(fixedPath);
|
return QUrl::fromLocalFile(fixedPath);
|
||||||
@@ -123,7 +123,7 @@ QVariant fixResourcePaths(const QVariant &value)
|
|||||||
if (qrcDefintion.count() == 2) {
|
if (qrcDefintion.count() == 2) {
|
||||||
QString fixedPath = str;
|
QString fixedPath = str;
|
||||||
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
fixedPath.replace(QLatin1String("qrc:") + qrcDefintion.first(), qrcDefintion.last() + QLatin1Char('/'));
|
||||||
if (QFileInfo(fixedPath).exists()) {
|
if (QFileInfo::exists(fixedPath)) {
|
||||||
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
fixedPath.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
fixedPath.replace(QLatin1Char('\\'), QLatin1Char('/'));
|
||||||
return QUrl::fromLocalFile(fixedPath);
|
return QUrl::fromLocalFile(fixedPath);
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ QVariant OverviewModel::data(const QModelIndex &index, int role) const
|
|||||||
if (Template *t = symbol->asTemplate())
|
if (Template *t = symbol->asTemplate())
|
||||||
if (Symbol *templateDeclaration = t->declaration()) {
|
if (Symbol *templateDeclaration = t->declaration()) {
|
||||||
QStringList parameters;
|
QStringList parameters;
|
||||||
|
parameters.reserve(t->templateParameterCount());
|
||||||
for (unsigned i = 0; i < t->templateParameterCount(); ++i)
|
for (unsigned i = 0; i < t->templateParameterCount(); ++i)
|
||||||
parameters.append(_overview.prettyName(t->templateParameterAt(i)->name()));
|
parameters.append(_overview.prettyName(t->templateParameterAt(i)->name()));
|
||||||
name += QLatin1Char('<') + parameters.join(QLatin1String(", ")) + QLatin1Char('>');
|
name += QLatin1Char('<') + parameters.join(QLatin1String(", ")) + QLatin1Char('>');
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ QReadWriteLock *PluginManager::listLock()
|
|||||||
*/
|
*/
|
||||||
void PluginManager::loadPlugins()
|
void PluginManager::loadPlugins()
|
||||||
{
|
{
|
||||||
return d->loadPlugins();
|
d->loadPlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -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) {
|
if (skipped > 0) {
|
||||||
appendChild(parentObject, parentItem, context, QModelIndex(), position / parentSize,
|
appendChild(parentObject, parentItem, context, QModelIndex(), position / parentSize,
|
||||||
skipped / parentSize);
|
skipped / parentSize);
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ void StereotypeDisplayVisitor::visitDItem(const DItem *item)
|
|||||||
m_stereotypeSmartDisplay = DObject::StereotypeIcon;
|
m_stereotypeSmartDisplay = DObject::StereotypeIcon;
|
||||||
visitDObject(item);
|
visitDObject(item);
|
||||||
if (m_stereotypeIconId.isEmpty() && !item->shape().isEmpty())
|
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())
|
if (m_shapeIconId.isEmpty() && !item->variety().isEmpty())
|
||||||
m_shapeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, QStringList(item->variety()));
|
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);
|
this, &PropertiesView::MView::onAssociationEndBKindChanged);
|
||||||
}
|
}
|
||||||
if (isSingleSelection) {
|
if (isSingleSelection) {
|
||||||
if ((!isValidAssociationKindIndex(m_endAKind->currentIndex())
|
if ((!isValidAssociationKindIndex(m_endBKind->currentIndex())
|
||||||
|| association->endB().kind() != translateIndexToAssociationKind(m_endBKind->currentIndex()))
|
|| association->endB().kind() != translateIndexToAssociationKind(m_endBKind->currentIndex()))
|
||||||
&& !m_endBKind->hasFocus()) {
|
&& !m_endBKind->hasFocus()) {
|
||||||
m_endBKind->setCurrentIndex(translateAssociationKindToIndex(association->endB().kind()));
|
m_endBKind->setCurrentIndex(translateAssociationKindToIndex(association->endB().kind()));
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ void BaseEngineDebugClient::messageReceived(const QByteArray &data)
|
|||||||
int count;
|
int count;
|
||||||
ds >> count;
|
ds >> count;
|
||||||
QList<EngineReference> engines;
|
QList<EngineReference> engines;
|
||||||
|
engines.reserve(count);
|
||||||
for (int ii = 0; ii < count; ++ii) {
|
for (int ii = 0; ii < count; ++ii) {
|
||||||
EngineReference eng;
|
EngineReference eng;
|
||||||
ds >> eng.m_name;
|
ds >> eng.m_name;
|
||||||
|
|||||||
@@ -482,7 +482,7 @@ void ContextPaneWidgetImage::setPixmap(const QString &fileName)
|
|||||||
|
|
||||||
if (m_borderImage) {
|
if (m_borderImage) {
|
||||||
QString localFileName(fileName);
|
QString localFileName(fileName);
|
||||||
if (QFile(fileName).exists()) {
|
if (QFileInfo::exists(fileName)) {
|
||||||
if (fileName.endsWith(QLatin1String("sci"))) {
|
if (fileName.endsWith(QLatin1String("sci"))) {
|
||||||
QString pixmapFileName;
|
QString pixmapFileName;
|
||||||
int left = 0;
|
int left = 0;
|
||||||
@@ -527,7 +527,7 @@ void ContextPaneWidgetImage::setPixmap(const QString &fileName)
|
|||||||
}
|
}
|
||||||
uiBorderImage->label->setPixmap(pix);
|
uiBorderImage->label->setPixmap(pix);
|
||||||
} else {
|
} else {
|
||||||
if (QFile(fileName).exists()) {
|
if (QFileInfo::exists(fileName)) {
|
||||||
QPixmap source(fileName);
|
QPixmap source(fileName);
|
||||||
previewDialog()->setPixmap(source, 1);
|
previewDialog()->setPixmap(source, 1);
|
||||||
ui->sizeLabel->setText(QString::number(source.width()) + QLatin1Char('x') + QString::number(source.height()));
|
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)
|
void PreviewLabel::mousePressEvent(QMouseEvent * event)
|
||||||
{
|
{
|
||||||
if (!m_borderImage)
|
if (!m_borderImage) {
|
||||||
return QLabel::mouseMoveEvent(event);
|
QLabel::mouseMoveEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool bottom = false;
|
bool bottom = false;
|
||||||
|
|
||||||
@@ -790,8 +792,10 @@ static inline int limitPositive(int i)
|
|||||||
|
|
||||||
void PreviewLabel::mouseMoveEvent(QMouseEvent * event)
|
void PreviewLabel::mouseMoveEvent(QMouseEvent * event)
|
||||||
{
|
{
|
||||||
if (!m_borderImage)
|
if (!m_borderImage) {
|
||||||
return QLabel::mouseMoveEvent(event);
|
QLabel::mouseMoveEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QPoint p = event->pos();
|
QPoint p = event->pos();
|
||||||
bool bottom = false;
|
bool bottom = false;
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ public:
|
|||||||
static void accept(Node *node, Visitor *visitor);
|
static void accept(Node *node, Visitor *visitor);
|
||||||
|
|
||||||
inline static void acceptChild(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 void accept0(Visitor *visitor) = 0;
|
||||||
virtual SourceLocation firstSourceLocation() const = 0;
|
virtual SourceLocation firstSourceLocation() const = 0;
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public:
|
|||||||
fileName.prepend(QLatin1Char('/'));
|
fileName.prepend(QLatin1Char('/'));
|
||||||
fileName.prepend(_doc->path());
|
fileName.prepend(_doc->path());
|
||||||
}
|
}
|
||||||
if (!QFileInfo(fileName).exists())
|
if (!QFileInfo::exists(fileName))
|
||||||
setMessage(WarnFileOrDirectoryDoesNotExist);
|
setMessage(WarnFileOrDirectoryDoesNotExist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -556,8 +556,7 @@ public:
|
|||||||
"Scale",
|
"Scale",
|
||||||
"Translate",
|
"Translate",
|
||||||
"Package",
|
"Package",
|
||||||
"Particles",
|
"Particles"})
|
||||||
"Dialog"})
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,12 @@ enum Enum {
|
|||||||
namespace Severity {
|
namespace Severity {
|
||||||
enum Enum
|
enum Enum
|
||||||
{
|
{
|
||||||
Hint, // cosmetic or convention
|
Hint, // cosmetic or convention
|
||||||
MaybeWarning, // possibly a warning, insufficient information
|
MaybeWarning, // possibly a warning, insufficient information
|
||||||
Warning, // could cause unintended behavior
|
Warning, // could cause unintended behavior
|
||||||
MaybeError, // possibly an error, insufficient information
|
ReadingTypeInfoWarning, // currently dumping type information
|
||||||
Error // definitely an error
|
MaybeError, // possibly an error, insufficient information
|
||||||
|
Error // definitely an error
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -325,20 +325,18 @@ void DescribeValueVisitor::visit(const ObjectValue *value)
|
|||||||
basicDump("ObjectValue", value, printDetail);
|
basicDump("ObjectValue", value, printDetail);
|
||||||
}
|
}
|
||||||
if (printDetail) {
|
if (printDetail) {
|
||||||
if (value) {
|
dumpNewline();
|
||||||
dumpNewline();
|
dump("className:");
|
||||||
dump("className:");
|
dump(value->className());
|
||||||
dump(value->className());
|
dumpNewline();
|
||||||
dumpNewline();
|
dump("members:");
|
||||||
dump("members:");
|
openContext("[");
|
||||||
openContext("[");
|
PrintMembers printMembers(*this);
|
||||||
PrintMembers printMembers(*this);
|
value->processMembers(&printMembers);
|
||||||
value->processMembers(&printMembers);
|
closeContext("]");
|
||||||
closeContext("]");
|
dumpNewline();
|
||||||
dumpNewline();
|
dump("prototype:");
|
||||||
dump("prototype:");
|
(*this)(value->prototype());
|
||||||
(*this)(value->prototype());
|
|
||||||
}
|
|
||||||
closeContext();
|
closeContext();
|
||||||
}
|
}
|
||||||
--m_depth;
|
--m_depth;
|
||||||
|
|||||||
@@ -453,8 +453,9 @@ bool LinkPrivate::importLibrary(Document::Ptr doc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (errorLoc.isValid()) {
|
if (errorLoc.isValid()) {
|
||||||
warning(doc, errorLoc,
|
appendDiagnostic(doc, DiagnosticMessage(Severity::ReadingTypeInfoWarning,
|
||||||
Link::tr("QML module contains C++ plugins, currently reading type information..."));
|
errorLoc,
|
||||||
|
Link::tr("QML module contains C++ plugins, currently reading type information...")));
|
||||||
import->valid = false;
|
import->valid = false;
|
||||||
}
|
}
|
||||||
} else if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::DumpError
|
} else if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::DumpError
|
||||||
|
|||||||
@@ -782,7 +782,8 @@ static bool findNewQmlLibraryInPath(const QString &path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// found a new library!
|
// found a new library!
|
||||||
qmldirFile.open(QFile::ReadOnly);
|
if (!qmldirFile.open(QFile::ReadOnly))
|
||||||
|
return false;
|
||||||
QString qmldirData = QString::fromUtf8(qmldirFile.readAll());
|
QString qmldirData = QString::fromUtf8(qmldirFile.readAll());
|
||||||
|
|
||||||
QmlDirParser qmldirParser;
|
QmlDirParser qmldirParser;
|
||||||
|
|||||||
@@ -150,8 +150,12 @@ protected:
|
|||||||
void outCommentText(const QString &str)
|
void outCommentText(const QString &str)
|
||||||
{
|
{
|
||||||
QStringList lines = str.split(QLatin1Char('\n'));
|
QStringList lines = str.split(QLatin1Char('\n'));
|
||||||
|
bool multiline = lines.length() > 1;
|
||||||
for (int i = 0; i < lines.size(); ++i) {
|
for (int i = 0; i < lines.size(); ++i) {
|
||||||
_line = lines.at(i); // multiline comments don't keep track of previos lines
|
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)
|
if (i != lines.size() - 1)
|
||||||
newLine();
|
newLine();
|
||||||
}
|
}
|
||||||
@@ -582,7 +586,7 @@ protected:
|
|||||||
out(ast->identifierToken);
|
out(ast->identifierToken);
|
||||||
}
|
}
|
||||||
} else { // signal
|
} else { // signal
|
||||||
out("signal ");
|
out("signal ", ast->identifierToken);
|
||||||
out(ast->identifierToken);
|
out(ast->identifierToken);
|
||||||
if (ast->parameters) {
|
if (ast->parameters) {
|
||||||
out("(");
|
out("(");
|
||||||
|
|||||||
@@ -641,7 +641,9 @@ void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMe
|
|||||||
|
|
||||||
void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme)
|
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."));
|
addError(ast->colonToken, tr("Expected object literal after colon."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
static CurrentSymbolGroup currentSymbolGroup;
|
static CurrentSymbolGroup currentSymbolGroup;
|
||||||
|
static std::string results;
|
||||||
|
|
||||||
CurrentSymbolGroup::~CurrentSymbolGroup()
|
CurrentSymbolGroup::~CurrentSymbolGroup()
|
||||||
{
|
{
|
||||||
@@ -338,6 +339,16 @@ static PyObject *cdbext_call(PyObject *, PyObject *args)
|
|||||||
return createPythonObject(PyValue(index, symbolGroup));
|
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[] = {
|
static PyMethodDef cdbextMethods[] = {
|
||||||
{"parseAndEvaluate", cdbext_parseAndEvaluate, METH_VARARGS,
|
{"parseAndEvaluate", cdbext_parseAndEvaluate, METH_VARARGS,
|
||||||
"Returns value of expression or None if the expression can not be resolved"},
|
"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"},
|
"Creates a value with the given type at the given address"},
|
||||||
{"call", cdbext_call, METH_VARARGS,
|
{"call", cdbext_call, METH_VARARGS,
|
||||||
"Call a function and return a cdbext.Value representing the return value of that function."},
|
"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, NULL, 0,
|
||||||
NULL} /* Sentinel */
|
NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
@@ -420,3 +433,25 @@ int pointerSize()
|
|||||||
{
|
{
|
||||||
return ExtensionCommandContext::instance()->control()->IsPointer64Bit() == S_OK ? 8 : 4;
|
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 <Python.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "dbgeng.h"
|
#include "dbgeng.h"
|
||||||
|
|
||||||
void initCdbextPythonModule();
|
void initCdbextPythonModule();
|
||||||
int pointerSize();
|
int pointerSize();
|
||||||
|
std::string collectOutput();
|
||||||
|
|
||||||
constexpr bool debugPyCdbextModule = false;
|
constexpr bool debugPyCdbextModule = false;
|
||||||
using Bytes = std::vector<char>;
|
using Bytes = std::vector<char>;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#ifdef WITH_PYTHON
|
#ifdef WITH_PYTHON
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
#include "pystdoutredirect.h"
|
#include "pystdoutredirect.h"
|
||||||
|
#include "pycdbextmodule.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
#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';
|
const char result = (PyRun_SimpleString(command.str().c_str()) == 0) ? 'R' : 'N';
|
||||||
if (PyErr_Occurred())
|
if (PyErr_Occurred())
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
ExtensionContext::instance().reportLong(result, token, "script", getPyStdout().c_str());
|
ExtensionContext::instance().reportLong(result, token, "script", collectOutput().c_str());
|
||||||
endCapturePyStdout();
|
endCapturePyStdout();
|
||||||
PyErr_Restore(ptype, pvalue, ptraceback);
|
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||||
#else
|
#else
|
||||||
@@ -849,10 +850,15 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
|
|||||||
const std::string iname = tokens.front().substr(0, equalsPos);
|
const std::string iname = tokens.front().substr(0, equalsPos);
|
||||||
const std::string value = tokens.front().substr(equalsPos + 1, tokens.front().size() - equalsPos - 1);
|
const std::string value = tokens.front().substr(equalsPos + 1, tokens.front().size() - equalsPos - 1);
|
||||||
// get the symbolgroup
|
// get the symbolgroup
|
||||||
const int currentFrame = ExtensionContext::instance().symbolGroupFrame();
|
int currentFrame = ExtensionContext::instance().symbolGroupFrame();
|
||||||
if (currentFrame < 0) {
|
if (currentFrame < 0) {
|
||||||
errorMessage = "No current frame.";
|
CIDebugControl *control = ExtensionCommandContext::instance()->control();
|
||||||
break;
|
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);
|
SymbolGroup *symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), currentFrame, &errorMessage);
|
||||||
if (!symGroup)
|
if (!symGroup)
|
||||||
|
|||||||
@@ -465,8 +465,11 @@ bool SymbolGroup::assign(const std::string &nodeName,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (node->dumperType() & KT_Editable) ? // Edit complex types
|
int kt = node->dumperType();
|
||||||
assignType(node, valueEncoding, value, ctx, errorMessage) :
|
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);
|
node->assign(value, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3552,17 +3552,17 @@ static inline bool assignStdString(SymbolGroupNode *n,
|
|||||||
return true;
|
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)
|
const SymbolGroupValueContext &ctx, std::string *errorMessage)
|
||||||
{
|
{
|
||||||
switch (n->dumperType()) {
|
switch (knownType) {
|
||||||
case KT_QString:
|
case KT_QString:
|
||||||
return assignQString(n, valueEncoding, value, ctx, errorMessage);
|
return assignQString(n, valueEncoding, value, ctx, errorMessage);
|
||||||
case KT_QByteArray:
|
case KT_QByteArray:
|
||||||
return assignQByteArray(n, valueEncoding, value, ctx, errorMessage);
|
return assignQByteArray(n, valueEncoding, value, ctx, errorMessage);
|
||||||
case KT_StdString:
|
case KT_StdString:
|
||||||
case KT_StdWString:
|
case KT_StdWString:
|
||||||
return assignStdString(n, n->dumperType(), valueEncoding, value, ctx, errorMessage);
|
return assignStdString(n, knownType, valueEncoding, value, ctx, errorMessage);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ enum AssignEncoding
|
|||||||
AssignHexEncodedUtf16
|
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,
|
const SymbolGroupValueContext &ctx,
|
||||||
std::string *errorMessage);
|
std::string *errorMessage);
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Rectangle {
|
|||||||
// This is called from outside to synchronize the timeline to other views
|
// This is called from outside to synchronize the timeline to other views
|
||||||
function selectByTypeId(typeId)
|
function selectByTypeId(typeId)
|
||||||
{
|
{
|
||||||
if (lockItemSelection || typeId === -1)
|
if (lockItemSelection || typeId === -1 || content.typeId === typeId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var itemIndex = -1;
|
var itemIndex = -1;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#include "appmainwindow.h"
|
#include "appmainwindow.h"
|
||||||
|
#include "theme/theme_p.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -60,11 +61,14 @@ void AppMainWindow::raiseWindow()
|
|||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
bool AppMainWindow::event(QEvent *event)
|
bool AppMainWindow::event(QEvent *event)
|
||||||
{
|
{
|
||||||
if (event->type() == m_deviceEventId) {
|
const QEvent::Type type = event->type();
|
||||||
|
if (type == m_deviceEventId) {
|
||||||
event->accept();
|
event->accept();
|
||||||
emit deviceChange();
|
emit deviceChange();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (type == QEvent::ThemeChange)
|
||||||
|
setThemeApplicationPalette();
|
||||||
return QMainWindow::event(event);
|
return QMainWindow::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ void CompletingLineEdit::keyPressEvent(QKeyEvent *e)
|
|||||||
comp->complete();
|
comp->complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return QLineEdit::keyPressEvent(e);
|
QLineEdit::keyPressEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
@@ -359,7 +360,7 @@ QString ConsoleProcess::defaultTerminalEmulator()
|
|||||||
{
|
{
|
||||||
if (HostOsInfo::isMacHost()) {
|
if (HostOsInfo::isMacHost()) {
|
||||||
QString termCmd = QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/scripts/openTerminal.command");
|
QString termCmd = QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/scripts/openTerminal.command");
|
||||||
if (QFile(termCmd).exists())
|
if (QFileInfo::exists(termCmd))
|
||||||
return termCmd.replace(QLatin1Char(' '), QLatin1String("\\ "));
|
return termCmd.replace(QLatin1Char(' '), QLatin1String("\\ "));
|
||||||
return QLatin1String("/usr/X11/bin/xterm");
|
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)
|
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)
|
bool ConsoleProcess::startTerminalEmulator(QSettings *settings, const QString &workingDir)
|
||||||
|
|||||||
@@ -365,6 +365,7 @@ void CrumblePath::resizeButtons()
|
|||||||
// compute relative sizes
|
// compute relative sizes
|
||||||
QList<int> sizes;
|
QList<int> sizes;
|
||||||
int totalSize = 0;
|
int totalSize = 0;
|
||||||
|
sizes.reserve(m_buttons.length());
|
||||||
for (int i = 0; i < m_buttons.length() ; ++i) {
|
for (int i = 0; i < m_buttons.length() ; ++i) {
|
||||||
CrumblePathButton *button = m_buttons.at(i);
|
CrumblePathButton *button = m_buttons.at(i);
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ static QString appBundleExpandedPath(const QString &path)
|
|||||||
QFileInfo info(path);
|
QFileInfo info(path);
|
||||||
if (info.isDir()) {
|
if (info.isDir()) {
|
||||||
QString exePath = path + QLatin1String("/Contents/MacOS/") + info.completeBaseName();
|
QString exePath = path + QLatin1String("/Contents/MacOS/") + info.completeBaseName();
|
||||||
if (QFileInfo(exePath).exists())
|
if (QFileInfo::exists(exePath))
|
||||||
return exePath;
|
return exePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,14 +57,19 @@ Theme *proxyTheme()
|
|||||||
return new Theme(m_creatorTheme);
|
return new Theme(m_creatorTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setThemeApplicationPalette()
|
||||||
|
{
|
||||||
|
if (m_creatorTheme && m_creatorTheme->flag(Theme::ApplyThemePaletteGlobally))
|
||||||
|
QApplication::setPalette(m_creatorTheme->palette());
|
||||||
|
}
|
||||||
|
|
||||||
void setCreatorTheme(Theme *theme)
|
void setCreatorTheme(Theme *theme)
|
||||||
{
|
{
|
||||||
if (m_creatorTheme == theme)
|
if (m_creatorTheme == theme)
|
||||||
return;
|
return;
|
||||||
delete m_creatorTheme;
|
delete m_creatorTheme;
|
||||||
m_creatorTheme = theme;
|
m_creatorTheme = theme;
|
||||||
if (theme && theme->flag(Theme::ApplyThemePaletteGlobally))
|
setThemeApplicationPalette();
|
||||||
QApplication::setPalette(theme->palette());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Theme::Theme(const QString &id, QObject *parent)
|
Theme::Theme(const QString &id, QObject *parent)
|
||||||
|
|||||||
@@ -51,5 +51,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
QTCREATOR_UTILS_EXPORT void setCreatorTheme(Theme *theme);
|
QTCREATOR_UTILS_EXPORT void setCreatorTheme(Theme *theme);
|
||||||
|
QTCREATOR_UTILS_EXPORT void setThemeApplicationPalette();
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|||||||
@@ -919,11 +919,9 @@ QModelIndex BaseTreeModel::parent(const QModelIndex &idx) const
|
|||||||
if (!grandparent)
|
if (!grandparent)
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
|
|
||||||
for (int i = 0, n = grandparent->childCount(); i < n; ++i)
|
// This is on the performance-critical path for ItemViewFind.
|
||||||
if (grandparent->childAt(i) == parent)
|
const int i = grandparent->m_children.indexOf(parent);
|
||||||
return createIndex(i, 0, static_cast<void*>(parent));
|
return createIndex(i, 0, static_cast<void*>(parent));
|
||||||
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseTreeModel::rowCount(const QModelIndex &idx) const
|
int BaseTreeModel::rowCount(const QModelIndex &idx) const
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public:
|
|||||||
|
|
||||||
using const_iterator = QVector<TreeItem *>::const_iterator;
|
using const_iterator = QVector<TreeItem *>::const_iterator;
|
||||||
using value_type = TreeItem *;
|
using value_type = TreeItem *;
|
||||||
int childCount() const { return end() - begin(); }
|
int childCount() const { return m_children.size(); }
|
||||||
int indexInParent() const;
|
int indexInParent() const;
|
||||||
TreeItem *childAt(int index) const;
|
TreeItem *childAt(int index) const;
|
||||||
int indexOf(const TreeItem *item) const;
|
int indexOf(const TreeItem *item) const;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ QString UnixUtils::fileBrowser(const QSettings *settings)
|
|||||||
|
|
||||||
void UnixUtils::setFileBrowser(QSettings *settings, const QString &term)
|
void UnixUtils::setFileBrowser(QSettings *settings, const QString &term)
|
||||||
{
|
{
|
||||||
return settings->setValue(QLatin1String("General/FileBrowser"), term);
|
settings->setValue(QLatin1String("General/FileBrowser"), term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ HEADERS += \
|
|||||||
android_global.h \
|
android_global.h \
|
||||||
androidbuildapkstep.h \
|
androidbuildapkstep.h \
|
||||||
androidbuildapkwidget.h \
|
androidbuildapkwidget.h \
|
||||||
androidrunnable.h
|
androidrunnable.h \
|
||||||
|
androidtoolmanager.h \
|
||||||
|
androidsdkmanager.h \
|
||||||
|
androidavdmanager.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
androidconfigurations.cpp \
|
androidconfigurations.cpp \
|
||||||
@@ -88,7 +91,10 @@ SOURCES += \
|
|||||||
androidbuildapkstep.cpp \
|
androidbuildapkstep.cpp \
|
||||||
androidbuildapkwidget.cpp \
|
androidbuildapkwidget.cpp \
|
||||||
androidqtsupport.cpp \
|
androidqtsupport.cpp \
|
||||||
androidrunnable.cpp
|
androidrunnable.cpp \
|
||||||
|
androidtoolmanager.cpp \
|
||||||
|
androidsdkmanager.cpp \
|
||||||
|
androidavdmanager.cpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
androidsettingswidget.ui \
|
androidsettingswidget.ui \
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ Project {
|
|||||||
"android.qrc",
|
"android.qrc",
|
||||||
"androidanalyzesupport.cpp",
|
"androidanalyzesupport.cpp",
|
||||||
"androidanalyzesupport.h",
|
"androidanalyzesupport.h",
|
||||||
|
"androidavdmanager.cpp",
|
||||||
|
"androidavdmanager.h",
|
||||||
"androidconfigurations.cpp",
|
"androidconfigurations.cpp",
|
||||||
"androidconfigurations.h",
|
"androidconfigurations.h",
|
||||||
"androidconstants.h",
|
"androidconstants.h",
|
||||||
@@ -84,6 +86,8 @@ Project {
|
|||||||
"androidrunnable.h",
|
"androidrunnable.h",
|
||||||
"androidrunner.cpp",
|
"androidrunner.cpp",
|
||||||
"androidrunner.h",
|
"androidrunner.h",
|
||||||
|
"androidsdkmanager.cpp",
|
||||||
|
"androidsdkmanager.h",
|
||||||
"androidsettingspage.cpp",
|
"androidsettingspage.cpp",
|
||||||
"androidsettingspage.h",
|
"androidsettingspage.h",
|
||||||
"androidsettingswidget.cpp",
|
"androidsettingswidget.cpp",
|
||||||
@@ -93,6 +97,8 @@ Project {
|
|||||||
"androidsignaloperation.h",
|
"androidsignaloperation.h",
|
||||||
"androidtoolchain.cpp",
|
"androidtoolchain.cpp",
|
||||||
"androidtoolchain.h",
|
"androidtoolchain.h",
|
||||||
|
"androidtoolmanager.cpp",
|
||||||
|
"androidtoolmanager.h",
|
||||||
"avddialog.cpp",
|
"avddialog.cpp",
|
||||||
"avddialog.h",
|
"avddialog.h",
|
||||||
"certificatesmodel.cpp",
|
"certificatesmodel.cpp",
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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 {
|
namespace Android {
|
||||||
using namespace Internal;
|
using namespace Internal;
|
||||||
|
|
||||||
|
const QVersionNumber gradleScriptRevokedSdkVersion(25, 3, 0);
|
||||||
|
const QVersionNumber gradleScriptsContainedQtVersion(5, 9, 0);
|
||||||
const QLatin1String DeployActionKey("Qt4ProjectManager.AndroidDeployQtStep.DeployQtAction");
|
const QLatin1String DeployActionKey("Qt4ProjectManager.AndroidDeployQtStep.DeployQtAction");
|
||||||
const QLatin1String KeystoreLocationKey("KeystoreLocation");
|
const QLatin1String KeystoreLocationKey("KeystoreLocation");
|
||||||
const QLatin1String BuildTargetSdkKey("BuildTargetSdk");
|
const QLatin1String BuildTargetSdkKey("BuildTargetSdk");
|
||||||
@@ -140,6 +142,15 @@ bool AndroidBuildApkStep::init(QList<const BuildStep *> &earlierSteps)
|
|||||||
if (!version)
|
if (!version)
|
||||||
return false;
|
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());
|
int minSDKForKit = AndroidManager::minimumSDK(target()->kit());
|
||||||
if (AndroidManager::minimumSDK(target()) < minSDKForKit) {
|
if (AndroidManager::minimumSDK(target()) < minSDKForKit) {
|
||||||
emit addOutput(tr("The API level set for the APK is less than the minimum required by the kit."
|
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
|
bool AndroidBuildApkStep::verboseOutput() const
|
||||||
{
|
{
|
||||||
return m_verbose;
|
return m_verbose;
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ public:
|
|||||||
bool useGradle() const;
|
bool useGradle() const;
|
||||||
void setUseGradle(bool b);
|
void setUseGradle(bool b);
|
||||||
|
|
||||||
|
bool addDebugger() const;
|
||||||
|
void setAddDebugger(bool debug);
|
||||||
|
|
||||||
QString buildTargetSdk() const;
|
QString buildTargetSdk() const;
|
||||||
void setBuildTargetSdk(const QString &sdk);
|
void setBuildTargetSdk(const QString &sdk);
|
||||||
|
|
||||||
@@ -99,9 +102,10 @@ protected:
|
|||||||
AndroidDeployAction m_deployAction = BundleLibrariesDeployment;
|
AndroidDeployAction m_deployAction = BundleLibrariesDeployment;
|
||||||
bool m_signPackage = false;
|
bool m_signPackage = false;
|
||||||
bool m_verbose = false;
|
bool m_verbose = false;
|
||||||
bool m_useGradle = false;
|
bool m_useGradle = true; // Ant builds are deprecated.
|
||||||
bool m_openPackageLocation = false;
|
bool m_openPackageLocation = false;
|
||||||
bool m_openPackageLocationForRun = false;
|
bool m_openPackageLocationForRun = false;
|
||||||
|
bool m_addDebugger = true;
|
||||||
QString m_buildTargetSdk;
|
QString m_buildTargetSdk;
|
||||||
|
|
||||||
Utils::FileName m_keystorePath;
|
Utils::FileName m_keystorePath;
|
||||||
|
|||||||
@@ -54,9 +54,12 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
|
|||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->deprecatedInfoIconLabel->setPixmap(Utils::Icons::INFO.pixmap());
|
||||||
|
|
||||||
// Target sdk combobox
|
// Target sdk combobox
|
||||||
int minApiLevel = 9;
|
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();
|
targets.removeDuplicates();
|
||||||
m_ui->targetSDKComboBox->addItems(targets);
|
m_ui->targetSDKComboBox->addItems(targets);
|
||||||
m_ui->targetSDKComboBox->setCurrentIndex(targets.indexOf(AndroidManager::buildTargetSDK(step->target())));
|
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());
|
m_ui->signingDebugDeployErrorIcon->setPixmap(Utils::Icons::CRITICAL.pixmap());
|
||||||
signPackageCheckBoxToggled(m_step->signPackage());
|
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->verboseOutputCheckBox->setChecked(m_step->verboseOutput());
|
||||||
m_ui->openPackageLocationCheckBox->setChecked(m_step->openPackageLocation());
|
m_ui->openPackageLocationCheckBox->setChecked(m_step->openPackageLocation());
|
||||||
|
m_ui->addDebuggerCheckBox->setChecked(m_step->addDebugger());
|
||||||
|
|
||||||
// target sdk
|
// target sdk
|
||||||
connect(m_ui->targetSDKComboBox,
|
connect(m_ui->targetSDKComboBox,
|
||||||
@@ -120,6 +126,8 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
|
|||||||
this, &AndroidBuildApkWidget::openPackageLocationCheckBoxToggled);
|
this, &AndroidBuildApkWidget::openPackageLocationCheckBoxToggled);
|
||||||
connect(m_ui->verboseOutputCheckBox, &QAbstractButton::toggled,
|
connect(m_ui->verboseOutputCheckBox, &QAbstractButton::toggled,
|
||||||
this, &AndroidBuildApkWidget::verboseOutputCheckBoxToggled);
|
this, &AndroidBuildApkWidget::verboseOutputCheckBoxToggled);
|
||||||
|
connect(m_ui->addDebuggerCheckBox, &QAbstractButton::toggled,
|
||||||
|
m_step, &AndroidBuildApkStep::setAddDebugger);
|
||||||
|
|
||||||
//signing
|
//signing
|
||||||
connect(m_ui->signPackageCheckBox, &QAbstractButton::toggled,
|
connect(m_ui->signPackageCheckBox, &QAbstractButton::toggled,
|
||||||
@@ -185,6 +193,7 @@ void AndroidBuildApkWidget::signPackageCheckBoxToggled(bool checked)
|
|||||||
{
|
{
|
||||||
m_ui->certificatesAliasComboBox->setEnabled(checked);
|
m_ui->certificatesAliasComboBox->setEnabled(checked);
|
||||||
m_step->setSignPackage(checked);
|
m_step->setSignPackage(checked);
|
||||||
|
m_ui->addDebuggerCheckBox->setChecked(!checked);
|
||||||
updateSigningWarning();
|
updateSigningWarning();
|
||||||
if (!checked)
|
if (!checked)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>819</width>
|
<width>819</width>
|
||||||
<height>390</height>
|
<height>478</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -176,24 +176,75 @@ Deploying local Qt libraries is incompatible with Android 5.</string>
|
|||||||
<string>Advanced Actions</string>
|
<string>Advanced Actions</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="2" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QCheckBox" name="verboseOutputCheckBox">
|
<widget class="QCheckBox" name="useGradleCheckBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Verbose output</string>
|
<string>Use Gradle (Ant builds are deprecated)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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">
|
<widget class="QCheckBox" name="openPackageLocationCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Open package location after build</string>
|
<string>Open package location after build</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="2" column="0" colspan="3">
|
||||||
<widget class="QCheckBox" name="useGradleCheckBox">
|
<widget class="QCheckBox" name="verboseOutputCheckBox">
|
||||||
<property name="text">
|
<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>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -254,5 +305,22 @@ The APK will not be usable on any other device.</string>
|
|||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<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>
|
</ui>
|
||||||
|
|||||||
@@ -28,8 +28,11 @@
|
|||||||
#include "androidtoolchain.h"
|
#include "androidtoolchain.h"
|
||||||
#include "androiddevice.h"
|
#include "androiddevice.h"
|
||||||
#include "androidgdbserverkitinformation.h"
|
#include "androidgdbserverkitinformation.h"
|
||||||
|
#include "androidmanager.h"
|
||||||
#include "androidqtversion.h"
|
#include "androidqtversion.h"
|
||||||
#include "androiddevicedialog.h"
|
#include "androiddevicedialog.h"
|
||||||
|
#include "androidsdkmanager.h"
|
||||||
|
#include "androidtoolmanager.h"
|
||||||
#include "avddialog.h"
|
#include "avddialog.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
@@ -73,6 +76,9 @@ namespace Android {
|
|||||||
using namespace Internal;
|
using namespace Internal;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
const QVersionNumber sdkToolsAntMissingVersion(25, 3, 0);
|
||||||
|
|
||||||
const QLatin1String SettingsGroup("AndroidConfigurations");
|
const QLatin1String SettingsGroup("AndroidConfigurations");
|
||||||
const QLatin1String SDKLocationKey("SDKLocation");
|
const QLatin1String SDKLocationKey("SDKLocation");
|
||||||
const QLatin1String NDKLocationKey("NDKLocation");
|
const QLatin1String NDKLocationKey("NDKLocation");
|
||||||
@@ -107,39 +113,14 @@ namespace {
|
|||||||
const QLatin1String keytoolName("keytool");
|
const QLatin1String keytoolName("keytool");
|
||||||
const QLatin1String changeTimeStamp("ChangeTimeStamp");
|
const QLatin1String changeTimeStamp("ChangeTimeStamp");
|
||||||
|
|
||||||
|
const QLatin1String sdkToolsVersionKey("Pkg.Revision");
|
||||||
|
|
||||||
static QString sdkSettingsFileName()
|
static QString sdkSettingsFileName()
|
||||||
{
|
{
|
||||||
return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
|
return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath()
|
||||||
+ QLatin1String("/qtcreator/android.xml");
|
+ 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()
|
static bool is32BitUserSpace()
|
||||||
{
|
{
|
||||||
// Do the exact same check as android's emulator is doing:
|
// Do the exact same check as android's emulator is doing:
|
||||||
@@ -162,25 +143,6 @@ namespace {
|
|||||||
}
|
}
|
||||||
return false;
|
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;
|
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
|
void AndroidConfig::updateAvailableSdkPlatforms() const
|
||||||
{
|
{
|
||||||
if (m_availableSdkPlatformsUpToDate)
|
if (m_availableSdkPlatformsUpToDate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_availableSdkPlatforms.clear();
|
m_availableSdkPlatforms.clear();
|
||||||
|
AndroidSdkManager sdkManager(*this);
|
||||||
SynchronousProcess proc;
|
m_availableSdkPlatforms = sdkManager.availableSdkPlatforms();
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_availableSdkPlatformsUpToDate = true;
|
m_availableSdkPlatformsUpToDate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,18 +361,6 @@ FileName AndroidConfig::adbToolPath() const
|
|||||||
return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
|
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
|
FileName AndroidConfig::androidToolPath() const
|
||||||
{
|
{
|
||||||
if (HostOsInfo::isWindowsHost()) {
|
if (HostOsInfo::isWindowsHost()) {
|
||||||
@@ -486,7 +389,10 @@ FileName AndroidConfig::antToolPath() const
|
|||||||
FileName AndroidConfig::emulatorToolPath() const
|
FileName AndroidConfig::emulatorToolPath() const
|
||||||
{
|
{
|
||||||
FileName path = m_sdkLocation;
|
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
|
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)));
|
.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,
|
FileName AndroidConfig::gccPath(const Abi &abi, Core::Id lang,
|
||||||
const QString &ndkToolChainVersion) const
|
const QString &ndkToolChainVersion) const
|
||||||
{
|
{
|
||||||
@@ -583,7 +509,7 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToo
|
|||||||
devices.push_back(dev);
|
devices.push_back(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::sort(devices, androidDevicesLessThan);
|
Utils::sort(devices);
|
||||||
if (devices.isEmpty() && error)
|
if (devices.isEmpty() && error)
|
||||||
*error = QApplication::translate("AndroidConfiguration",
|
*error = QApplication::translate("AndroidConfiguration",
|
||||||
"No devices found in output of: %1")
|
"No devices found in output of: %1")
|
||||||
@@ -605,197 +531,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent,
|
|||||||
return result;
|
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
|
bool AndroidConfig::isConnected(const QString &serialNumber) const
|
||||||
{
|
{
|
||||||
QVector<AndroidDeviceInfo> devices = connectedDevices();
|
QVector<AndroidDeviceInfo> devices = connectedDevices();
|
||||||
@@ -806,39 +541,6 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const
|
|||||||
return false;
|
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
|
bool AndroidConfig::isBootToQt(const QString &device) const
|
||||||
{
|
{
|
||||||
return isBootToQt(adbToolPath().toString(), device);
|
return isBootToQt(adbToolPath().toString(), device);
|
||||||
@@ -961,21 +663,6 @@ QString AndroidConfig::getProductModel(const QString &device) const
|
|||||||
return model;
|
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
|
QStringList AndroidConfig::getAbis(const QString &device) const
|
||||||
{
|
{
|
||||||
return getAbis(adbToolPath().toString(), device);
|
return getAbis(adbToolPath().toString(), device);
|
||||||
@@ -1053,6 +740,19 @@ void AndroidConfig::setSdkLocation(const FileName &sdkLocation)
|
|||||||
m_availableSdkPlatformsUpToDate = false;
|
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
|
FileName AndroidConfig::ndkLocation() const
|
||||||
{
|
{
|
||||||
return m_ndkLocation;
|
return m_ndkLocation;
|
||||||
@@ -1126,9 +826,18 @@ void AndroidConfig::setAutomaticKitCreation(bool b)
|
|||||||
m_automaticKitCreation = b;
|
m_automaticKitCreation = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AndroidConfig::antScriptsAvailable() const
|
||||||
|
{
|
||||||
|
return sdkToolsVersion() < sdkToolsAntMissingVersion;
|
||||||
|
}
|
||||||
|
|
||||||
bool AndroidConfig::useGrandle() const
|
bool AndroidConfig::useGrandle() const
|
||||||
{
|
{
|
||||||
return m_useGradle;
|
if (antScriptsAvailable()) {
|
||||||
|
return m_useGradle;
|
||||||
|
}
|
||||||
|
// Force gradle builds.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidConfig::setUseGradle(bool b)
|
void AndroidConfig::setUseGradle(bool b)
|
||||||
@@ -1353,6 +1062,20 @@ QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber)
|
|||||||
return QStringList({"-s", 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()
|
const AndroidConfig &AndroidConfigurations::currentConfig()
|
||||||
{
|
{
|
||||||
return m_instance->m_config; // ensure that m_instance is initialized
|
return m_instance->m_config; // ensure that m_instance is initialized
|
||||||
@@ -1494,4 +1217,13 @@ void AndroidConfigurations::updateAndroidDevice()
|
|||||||
|
|
||||||
AndroidConfigurations *AndroidConfigurations::m_instance = 0;
|
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
|
} // namespace Android
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QFutureInterface>
|
#include <QFutureInterface>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
|
||||||
@@ -68,19 +69,36 @@ public:
|
|||||||
|
|
||||||
static QStringList adbSelector(const QString &serialNumber);
|
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
|
class SdkPlatform
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SdkPlatform()
|
bool isValid() const { return !name.isEmpty() && apiLevel != -1; }
|
||||||
: apiLevel(-1)
|
bool operator <(const SdkPlatform &other) const;
|
||||||
{}
|
int apiLevel = -1;
|
||||||
int apiLevel;
|
|
||||||
QString name;
|
QString name;
|
||||||
QStringList abis;
|
QString package;
|
||||||
|
Utils::FileName installedLocation;
|
||||||
|
SystemImageList systemImages;
|
||||||
};
|
};
|
||||||
|
using SdkPlatformList = QList<SdkPlatform>;
|
||||||
|
|
||||||
class ANDROID_EXPORT AndroidConfig
|
class ANDROID_EXPORT AndroidConfig
|
||||||
{
|
{
|
||||||
@@ -94,6 +112,7 @@ public:
|
|||||||
|
|
||||||
Utils::FileName sdkLocation() const;
|
Utils::FileName sdkLocation() const;
|
||||||
void setSdkLocation(const Utils::FileName &sdkLocation);
|
void setSdkLocation(const Utils::FileName &sdkLocation);
|
||||||
|
QVersionNumber sdkToolsVersion() const;
|
||||||
|
|
||||||
Utils::FileName ndkLocation() const;
|
Utils::FileName ndkLocation() const;
|
||||||
void setNdkLocation(const Utils::FileName &ndkLocation);
|
void setNdkLocation(const Utils::FileName &ndkLocation);
|
||||||
@@ -116,18 +135,21 @@ public:
|
|||||||
bool automaticKitCreation() const;
|
bool automaticKitCreation() const;
|
||||||
void setAutomaticKitCreation(bool b);
|
void setAutomaticKitCreation(bool b);
|
||||||
|
|
||||||
|
bool antScriptsAvailable() const;
|
||||||
|
|
||||||
bool useGrandle() const;
|
bool useGrandle() const;
|
||||||
void setUseGradle(bool b);
|
void setUseGradle(bool b);
|
||||||
|
|
||||||
Utils::FileName adbToolPath() const;
|
Utils::FileName adbToolPath() const;
|
||||||
Utils::FileName androidToolPath() const;
|
Utils::FileName androidToolPath() const;
|
||||||
Utils::Environment androidToolEnvironment() const;
|
|
||||||
Utils::FileName antToolPath() const;
|
Utils::FileName antToolPath() const;
|
||||||
Utils::FileName emulatorToolPath() const;
|
Utils::FileName emulatorToolPath() const;
|
||||||
|
Utils::FileName sdkManagerToolPath() const;
|
||||||
|
Utils::FileName avdManagerToolPath() const;
|
||||||
|
|
||||||
Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang,
|
Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang,
|
||||||
const QString &ndkToolChainVersion) const;
|
const QString &ndkToolChainVersion) const;
|
||||||
|
|
||||||
Utils::FileName gdbPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
Utils::FileName gdbPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
||||||
|
|
||||||
Utils::FileName keytoolPath() const;
|
Utils::FileName keytoolPath() const;
|
||||||
@@ -135,7 +157,8 @@ public:
|
|||||||
class CreateAvdInfo
|
class CreateAvdInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString target;
|
bool isValid() const { return target.isValid() && !name.isEmpty(); }
|
||||||
|
SdkPlatform target;
|
||||||
QString name;
|
QString name;
|
||||||
QString abi;
|
QString abi;
|
||||||
int sdcardSize = 0;
|
int sdcardSize = 0;
|
||||||
@@ -143,19 +166,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const;
|
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;
|
QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const;
|
||||||
static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0);
|
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;
|
QString bestNdkPlatformMatch(int target) const;
|
||||||
|
|
||||||
static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix);
|
static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix);
|
||||||
@@ -166,13 +180,10 @@ public:
|
|||||||
QString getProductModel(const QString &device) const;
|
QString getProductModel(const QString &device) const;
|
||||||
enum class OpenGl { Enabled, Disabled, Unknown };
|
enum class OpenGl { Enabled, Disabled, Unknown };
|
||||||
OpenGl getOpenGLEnabled(const QString &emulator) const;
|
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;
|
bool isConnected(const QString &serialNumber) const;
|
||||||
|
|
||||||
SdkPlatform highestAndroidSdk() const;
|
SdkPlatform highestAndroidSdk() const;
|
||||||
private:
|
private:
|
||||||
static CreateAvdInfo createAVDImpl(CreateAvdInfo info, Utils::FileName androidToolPath, Utils::Environment env);
|
|
||||||
static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property);
|
static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property);
|
||||||
|
|
||||||
Utils::FileName toolPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
Utils::FileName toolPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
||||||
@@ -196,12 +207,11 @@ private:
|
|||||||
QStringList m_makeExtraSearchDirectories;
|
QStringList m_makeExtraSearchDirectories;
|
||||||
unsigned m_partitionSize = 1024;
|
unsigned m_partitionSize = 1024;
|
||||||
bool m_automaticKitCreation = true;
|
bool m_automaticKitCreation = true;
|
||||||
bool m_useGradle = false;
|
bool m_useGradle = true; // Ant builds are deprecated.
|
||||||
|
|
||||||
//caches
|
//caches
|
||||||
mutable bool m_availableSdkPlatformsUpToDate = false;
|
mutable bool m_availableSdkPlatformsUpToDate = false;
|
||||||
mutable QVector<SdkPlatform> m_availableSdkPlatforms;
|
mutable SdkPlatformList m_availableSdkPlatforms;
|
||||||
static bool sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b);
|
|
||||||
|
|
||||||
mutable bool m_NdkInformationUpToDate = false;
|
mutable bool m_NdkInformationUpToDate = false;
|
||||||
mutable QString m_toolchainHost;
|
mutable QString m_toolchainHost;
|
||||||
@@ -247,3 +257,5 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
Q_DECLARE_METATYPE(Android::SdkPlatform)
|
||||||
|
|
||||||
|
|||||||
@@ -180,6 +180,16 @@ AndroidDebugSupport::AndroidDebugSupport(RunControl *runControl)
|
|||||||
[this](const QString &output) {
|
[this](const QString &output) {
|
||||||
this->runControl()->showMessage(output, AppOutput);
|
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)
|
void AndroidDebugSupport::handleRemoteProcessStarted(Utils::Port gdbServerPort, Utils::Port qmlPort)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
#include "androidconstants.h"
|
#include "androidconstants.h"
|
||||||
#include "androidglobal.h"
|
#include "androidglobal.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <coreplugin/fileutils.h>
|
#include <coreplugin/fileutils.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
@@ -262,8 +263,9 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
|
|||||||
|
|
||||||
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
||||||
|
|
||||||
if (AndroidConfigurations::currentConfig().findAvd(m_avdName).isEmpty())
|
AndroidAvdManager avdManager;
|
||||||
AndroidConfigurations::currentConfig().startAVDAsync(m_avdName);
|
if (avdManager.findAvd(m_avdName).isEmpty())
|
||||||
|
avdManager.startAvdAsync(m_avdName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +416,7 @@ void AndroidDeployQtStep::slotSetSerialNumber(const QString &serialNumber)
|
|||||||
void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
|
void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
|
||||||
{
|
{
|
||||||
if (!m_avdName.isEmpty()) {
|
if (!m_avdName.isEmpty()) {
|
||||||
QString serialNumber = AndroidConfigurations::currentConfig().waitForAvd(m_avdName, fi);
|
QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, fi);
|
||||||
if (serialNumber.isEmpty()) {
|
if (serialNumber.isEmpty()) {
|
||||||
reportRunResult(fi, false);
|
reportRunResult(fi, false);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "androiddevicedialog.h"
|
#include "androiddevicedialog.h"
|
||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
#include "ui_androiddevicedialog.h"
|
#include "ui_androiddevicedialog.h"
|
||||||
|
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
@@ -423,7 +424,8 @@ AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi, Andro
|
|||||||
m_ui(new Ui::AndroidDeviceDialog),
|
m_ui(new Ui::AndroidDeviceDialog),
|
||||||
m_apiLevel(apiLevel),
|
m_apiLevel(apiLevel),
|
||||||
m_abi(abi),
|
m_abi(abi),
|
||||||
m_defaultDevice(serialNumber)
|
m_defaultDevice(serialNumber),
|
||||||
|
m_avdManager(new AndroidAvdManager)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
m_ui->deviceView->setModel(m_model);
|
m_ui->deviceView->setModel(m_model);
|
||||||
@@ -515,7 +517,7 @@ void AndroidDeviceDialog::refreshDeviceList()
|
|||||||
m_ui->refreshDevicesButton->setEnabled(false);
|
m_ui->refreshDevicesButton->setEnabled(false);
|
||||||
m_progressIndicator->show();
|
m_progressIndicator->show();
|
||||||
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
|
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
|
||||||
m_futureWatcherRefreshDevices.setFuture(AndroidConfigurations::currentConfig().androidVirtualDevicesFuture());
|
m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceDialog::devicesRefreshed()
|
void AndroidDeviceDialog::devicesRefreshed()
|
||||||
@@ -530,7 +532,7 @@ void AndroidDeviceDialog::devicesRefreshed()
|
|||||||
serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
|
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,
|
QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices,
|
||||||
[] (const AndroidDeviceInfo &info) {
|
[] (const AndroidDeviceInfo &info) {
|
||||||
return info.avdname;
|
return info.avdname;
|
||||||
@@ -583,12 +585,12 @@ void AndroidDeviceDialog::createAvd()
|
|||||||
m_ui->createAVDButton->setEnabled(false);
|
m_ui->createAVDButton->setEnabled(false);
|
||||||
AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi);
|
AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi);
|
||||||
|
|
||||||
if (info.target.isEmpty()) {
|
if (!info.target.isValid()) {
|
||||||
m_ui->createAVDButton->setEnabled(true);
|
m_ui->createAVDButton->setEnabled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_futureWatcherAddDevice.setFuture(AndroidConfigurations::currentConfig().createAVD(info));
|
m_futureWatcherAddDevice.setFuture(m_avdManager->createAvd(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceDialog::avdAdded()
|
void AndroidDeviceDialog::avdAdded()
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
@@ -41,6 +43,7 @@ namespace Utils { class ProgressIndicator; }
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class AndroidAvdManager;
|
||||||
class AndroidDeviceModel;
|
class AndroidDeviceModel;
|
||||||
namespace Ui { class AndroidDeviceDialog; }
|
namespace Ui { class AndroidDeviceDialog; }
|
||||||
|
|
||||||
@@ -74,9 +77,10 @@ private:
|
|||||||
QString m_abi;
|
QString m_abi;
|
||||||
QString m_avdNameFromAdd;
|
QString m_avdNameFromAdd;
|
||||||
QString m_defaultDevice;
|
QString m_defaultDevice;
|
||||||
|
std::unique_ptr<AndroidAvdManager> m_avdManager;
|
||||||
QVector<AndroidDeviceInfo> m_connectedDevices;
|
QVector<AndroidDeviceInfo> m_connectedDevices;
|
||||||
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
|
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
|
||||||
QFutureWatcher<QVector<AndroidDeviceInfo>> m_futureWatcherRefreshDevices;
|
QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include "androidqtsupport.h"
|
#include "androidqtsupport.h"
|
||||||
#include "androidqtversion.h"
|
#include "androidqtversion.h"
|
||||||
#include "androidbuildapkstep.h"
|
#include "androidbuildapkstep.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <coreplugin/documentmanager.h>
|
#include <coreplugin/documentmanager.h>
|
||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
@@ -60,11 +61,13 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const QLatin1String AndroidManifestName("AndroidManifest.xml");
|
const QLatin1String AndroidManifestName("AndroidManifest.xml");
|
||||||
const QLatin1String AndroidDefaultPropertiesName("project.properties");
|
const QLatin1String AndroidDefaultPropertiesName("project.properties");
|
||||||
const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber");
|
const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber");
|
||||||
|
const QLatin1String ApiLevelKey("AndroidVersion.ApiLevel");
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
@@ -343,7 +346,7 @@ void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target)
|
|||||||
QString deviceSerialNumber = info.serialNumber;
|
QString deviceSerialNumber = info.serialNumber;
|
||||||
|
|
||||||
if (info.type == AndroidDeviceInfo::Emulator) {
|
if (info.type == AndroidDeviceInfo::Emulator) {
|
||||||
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname);
|
deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
|
||||||
if (deviceSerialNumber.isEmpty())
|
if (deviceSerialNumber.isEmpty())
|
||||||
Core::MessageManager::write(tr("Starting Android virtual device failed."));
|
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;
|
QString deviceSerialNumber = info.serialNumber;
|
||||||
if (info.type == AndroidDeviceInfo::Emulator) {
|
if (info.type == AndroidDeviceInfo::Emulator) {
|
||||||
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname);
|
deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
|
||||||
if (deviceSerialNumber.isEmpty())
|
if (deviceSerialNumber.isEmpty())
|
||||||
Core::MessageManager::write(tr("Starting Android virtual device failed."));
|
Core::MessageManager::write(tr("Starting Android virtual device failed."));
|
||||||
}
|
}
|
||||||
@@ -565,18 +568,33 @@ bool AndroidManager::updateGradleProperties(ProjectExplorer::Target *target)
|
|||||||
gradleProperties["buildDir"] = ".build";
|
gradleProperties["buildDir"] = ".build";
|
||||||
gradleProperties["androidCompileSdkVersion"] = buildTargetSDK(target).split(QLatin1Char('-')).last().toLocal8Bit();
|
gradleProperties["androidCompileSdkVersion"] = buildTargetSDK(target).split(QLatin1Char('-')).last().toLocal8Bit();
|
||||||
if (gradleProperties["androidBuildToolsVersion"].isEmpty()) {
|
if (gradleProperties["androidBuildToolsVersion"].isEmpty()) {
|
||||||
QString maxVersion;
|
QVersionNumber maxVersion;
|
||||||
QDir buildToolsDir(AndroidConfigurations::currentConfig().sdkLocation().appendPath(QLatin1String("build-tools")).toString());
|
QDir buildToolsDir(AndroidConfigurations::currentConfig().sdkLocation().appendPath(QLatin1String("build-tools")).toString());
|
||||||
foreach (const QFileInfo &file, buildToolsDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot)) {
|
foreach (const QFileInfo &file, buildToolsDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot)) {
|
||||||
QString ver(file.fileName());
|
QVersionNumber ver = QVersionNumber::fromString(file.fileName());
|
||||||
if (maxVersion < ver)
|
if (maxVersion < ver)
|
||||||
maxVersion = ver;
|
maxVersion = ver;
|
||||||
}
|
}
|
||||||
if (maxVersion.isEmpty())
|
if (maxVersion.isNull())
|
||||||
return false;
|
return false;
|
||||||
gradleProperties["androidBuildToolsVersion"] = maxVersion.toLocal8Bit();
|
gradleProperties["androidBuildToolsVersion"] = maxVersion.toString().toLocal8Bit();
|
||||||
}
|
}
|
||||||
return mergeGradleProperties(gradlePropertiesPath, gradleProperties);
|
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
|
} // namespace Android
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ public:
|
|||||||
static AndroidQtSupport *androidQtSupport(ProjectExplorer::Target *target);
|
static AndroidQtSupport *androidQtSupport(ProjectExplorer::Target *target);
|
||||||
static bool useGradle(ProjectExplorer::Target *target);
|
static bool useGradle(ProjectExplorer::Target *target);
|
||||||
static bool updateGradleProperties(ProjectExplorer::Target *target);
|
static bool updateGradleProperties(ProjectExplorer::Target *target);
|
||||||
|
static int findApiLevel(const Utils::FileName &platformPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
|||||||
@@ -29,16 +29,205 @@
|
|||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
|
|
||||||
#include <projectexplorer/kitinformation.h>
|
#include <projectexplorer/kitinformation.h>
|
||||||
|
#include <projectexplorer/projectexplorer.h>
|
||||||
|
#include <projectexplorer/projectexplorersettings.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
#include <qtsupport/qtoutputformatter.h>
|
#include <qtsupport/qtoutputformatter.h>
|
||||||
#include <qtsupport/qtkitinformation.h>
|
#include <qtsupport/qtkitinformation.h>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QToolButton>
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
namespace Android {
|
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)
|
AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, Core::Id id)
|
||||||
: RunConfiguration(parent, id)
|
: RunConfiguration(parent, id)
|
||||||
{
|
{
|
||||||
@@ -56,7 +245,7 @@ QWidget *AndroidRunConfiguration::createConfigurationWidget()
|
|||||||
|
|
||||||
Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const
|
Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const
|
||||||
{
|
{
|
||||||
return new QtSupport::QtOutputFormatter(target()->project());
|
return new AndroidOutputFormatter(target()->project());
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString AndroidRunConfiguration::remoteChannel() const
|
const QString AndroidRunConfiguration::remoteChannel() const
|
||||||
|
|||||||
@@ -28,9 +28,75 @@
|
|||||||
#include "android_global.h"
|
#include "android_global.h"
|
||||||
|
|
||||||
#include <projectexplorer/runconfiguration.h>
|
#include <projectexplorer/runconfiguration.h>
|
||||||
|
#include <qtsupport/qtoutputformatter.h>
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QToolButton;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Android {
|
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
|
class ANDROID_EXPORT AndroidRunConfiguration : public ProjectExplorer::RunConfiguration
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ void AndroidRunControl::start()
|
|||||||
this, &AndroidRunControl::handleRemoteOutput);
|
this, &AndroidRunControl::handleRemoteOutput);
|
||||||
connect(m_runner, &AndroidRunner::remoteProcessFinished,
|
connect(m_runner, &AndroidRunner::remoteProcessFinished,
|
||||||
this, &AndroidRunControl::handleRemoteProcessFinished);
|
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);
|
appendMessage(tr("Starting remote process."), Utils::NormalMessageFormat);
|
||||||
m_runner->setRunnable(runnable().as<AndroidRunnable>());
|
m_runner->setRunnable(runnable().as<AndroidRunnable>());
|
||||||
m_runner->start();
|
m_runner->start();
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "androidglobal.h"
|
#include "androidglobal.h"
|
||||||
#include "androidrunconfiguration.h"
|
#include "androidrunconfiguration.h"
|
||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <debugger/debuggerrunconfigurationaspect.h>
|
#include <debugger/debuggerrunconfigurationaspect.h>
|
||||||
#include <projectexplorer/projectexplorer.h>
|
#include <projectexplorer/projectexplorer.h>
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QTcpServer>
|
#include <QTcpServer>
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
@@ -125,10 +127,10 @@ namespace Internal {
|
|||||||
|
|
||||||
const int MIN_SOCKET_HANDSHAKE_PORT = 20001;
|
const int MIN_SOCKET_HANDSHAKE_PORT = 20001;
|
||||||
const int MAX_SOCKET_HANDSHAKE_PORT = 20999;
|
const int MAX_SOCKET_HANDSHAKE_PORT = 20999;
|
||||||
static const QString pidScript = QStringLiteral("for p in /proc/[0-9]*; "
|
static const QString pidScript = QStringLiteral("input keyevent KEYCODE_WAKEUP; "
|
||||||
"do cat <$p/cmdline && echo :${p##*/}; done");
|
"while true; do sleep 1; echo \"=\"; "
|
||||||
static const QString pidPollingScript = QStringLiteral("while true; do sleep 1; "
|
"for p in /proc/[0-9]*; "
|
||||||
"cat /proc/%1/cmdline > /dev/null; done");
|
"do cat <$p/cmdline && echo :${p##*/}; done; done");
|
||||||
|
|
||||||
static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
|
static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
|
||||||
"\\s+"
|
"\\s+"
|
||||||
@@ -146,55 +148,26 @@ static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
|
|||||||
);
|
);
|
||||||
static int APP_START_TIMEOUT = 45000;
|
static int APP_START_TIMEOUT = 45000;
|
||||||
|
|
||||||
static bool isTimedOut(const chrono::high_resolution_clock::time_point &start,
|
enum class PidStatus {
|
||||||
int msecs = APP_START_TIMEOUT)
|
Found,
|
||||||
|
Lost
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PidInfo
|
||||||
{
|
{
|
||||||
bool timedOut = false;
|
PidInfo(qint64 pid = -1, PidStatus status = PidStatus::Lost, QString name = {})
|
||||||
auto end = chrono::high_resolution_clock::now();
|
: pid(pid)
|
||||||
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
|
, status(status)
|
||||||
timedOut = true;
|
, name(name)
|
||||||
return timedOut;
|
{}
|
||||||
}
|
qint64 pid;
|
||||||
|
PidStatus status;
|
||||||
static qint64 extractPID(const QByteArray &output, const QString &packageName)
|
QString name;
|
||||||
{
|
};
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void deleter(QProcess *p)
|
static void deleter(QProcess *p)
|
||||||
{
|
{
|
||||||
|
p->disconnect();
|
||||||
p->kill();
|
p->kill();
|
||||||
p->waitForFinished();
|
p->waitForFinished();
|
||||||
// Might get deleted from its own signal handler.
|
// Might get deleted from its own signal handler.
|
||||||
@@ -228,29 +201,31 @@ signals:
|
|||||||
|
|
||||||
void remoteOutput(const QString &output);
|
void remoteOutput(const QString &output);
|
||||||
void remoteErrorOutput(const QString &output);
|
void remoteErrorOutput(const QString &output);
|
||||||
|
void pidFound(qint64, const QString &name);
|
||||||
|
void pidLost(qint64);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onProcessIdChanged(qint64 pid);
|
void findProcessPids();
|
||||||
|
void onProcessIdChanged(PidInfo pidInfo);
|
||||||
void logcatReadStandardError();
|
void logcatReadStandardError();
|
||||||
void logcatReadStandardOutput();
|
void logcatReadStandardOutput();
|
||||||
void adbKill(qint64 pid);
|
void adbKill(qint64 pid);
|
||||||
QStringList selector() const { return m_selector; }
|
QStringList selector() const { return m_selector; }
|
||||||
void forceStop();
|
void forceStop();
|
||||||
void findPs();
|
|
||||||
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
|
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
|
||||||
bool adbShellAmNeedsQuotes();
|
bool adbShellAmNeedsQuotes();
|
||||||
bool runAdb(const QStringList &args, QString *exitMessage = nullptr, int timeoutS = 10);
|
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
|
// 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_adbLogcatProcess;
|
||||||
std::unique_ptr<QProcess, decltype(&deleter)> m_psIsAlive;
|
std::unique_ptr<QProcess, decltype(&deleter)> m_pidsFinderProcess;
|
||||||
QScopedPointer<QTcpSocket> m_socket;
|
QScopedPointer<QTcpSocket> m_socket;
|
||||||
|
|
||||||
QByteArray m_stdoutBuffer;
|
QByteArray m_stdoutBuffer;
|
||||||
QByteArray m_stderrBuffer;
|
QByteArray m_stderrBuffer;
|
||||||
|
|
||||||
QFuture<qint64> m_pidFinder;
|
QSet<qint64> m_processPids;
|
||||||
qint64 m_processPID = -1;
|
|
||||||
bool m_useCppDebugger = false;
|
bool m_useCppDebugger = false;
|
||||||
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
|
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
|
||||||
Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket.
|
Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket.
|
||||||
@@ -261,20 +236,20 @@ private:
|
|||||||
QString m_gdbserverSocket;
|
QString m_gdbserverSocket;
|
||||||
QString m_adb;
|
QString m_adb;
|
||||||
QStringList m_selector;
|
QStringList m_selector;
|
||||||
QRegExp m_logCatRegExp;
|
|
||||||
DebugHandShakeType m_handShakeMethod = SocketHandShake;
|
DebugHandShakeType m_handShakeMethod = SocketHandShake;
|
||||||
bool m_customPort = false;
|
bool m_customPort = false;
|
||||||
|
|
||||||
QString m_packageName;
|
QString m_packageName;
|
||||||
int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT;
|
int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT;
|
||||||
|
QByteArray m_pidsBuffer;
|
||||||
|
QScopedPointer<QTimer> m_timeoutTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
AndroidRunnerWorker::AndroidRunnerWorker(AndroidRunConfiguration *runConfig, Core::Id runMode,
|
AndroidRunnerWorker::AndroidRunnerWorker(AndroidRunConfiguration *runConfig, Core::Id runMode,
|
||||||
const QString &packageName, const QStringList &selector)
|
const QString &packageName, const QStringList &selector)
|
||||||
: m_adbLogcatProcess(nullptr, deleter)
|
: m_adbLogcatProcess(nullptr, deleter)
|
||||||
, m_psIsAlive(nullptr, deleter)
|
, m_pidsFinderProcess(nullptr, deleter)
|
||||||
, m_selector(selector)
|
, m_selector(selector)
|
||||||
, m_logCatRegExp(regExpLogcat)
|
|
||||||
, m_packageName(packageName)
|
, m_packageName(packageName)
|
||||||
{
|
{
|
||||||
Debugger::DebuggerRunConfigurationAspect *aspect
|
Debugger::DebuggerRunConfigurationAspect *aspect
|
||||||
@@ -338,23 +313,18 @@ AndroidRunnerWorker::AndroidRunnerWorker(AndroidRunConfiguration *runConfig, Cor
|
|||||||
|
|
||||||
AndroidRunnerWorker::~AndroidRunnerWorker()
|
AndroidRunnerWorker::~AndroidRunnerWorker()
|
||||||
{
|
{
|
||||||
if (!m_pidFinder.isFinished())
|
|
||||||
m_pidFinder.cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunnerWorker::forceStop()
|
void AndroidRunnerWorker::forceStop()
|
||||||
{
|
{
|
||||||
runAdb(selector() << "shell" << "am" << "force-stop" << m_packageName, nullptr, 30);
|
runAdb(selector() << "shell" << "am" << "force-stop" << m_packageName, nullptr, 30);
|
||||||
|
|
||||||
// try killing it via kill -9
|
for (auto it = m_processPids.constBegin(); it != m_processPids.constEnd(); ++it) {
|
||||||
const QByteArray out = Utils::SynchronousProcess()
|
emit pidLost(*it);
|
||||||
.runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript)
|
adbKill(*it);
|
||||||
.allRawOutput();
|
|
||||||
|
|
||||||
qint64 pid = extractPID(out.simplified(), m_packageName);
|
|
||||||
if (pid != -1) {
|
|
||||||
adbKill(pid);
|
|
||||||
}
|
}
|
||||||
|
m_processPids.clear();
|
||||||
|
m_pidsBuffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunnerWorker::asyncStart(const QString &intentName,
|
void AndroidRunnerWorker::asyncStart(const QString &intentName,
|
||||||
@@ -368,8 +338,12 @@ void AndroidRunnerWorker::asyncStart(const QString &intentName,
|
|||||||
this, &AndroidRunnerWorker::logcatReadStandardOutput);
|
this, &AndroidRunnerWorker::logcatReadStandardOutput);
|
||||||
connect(logcatProcess.get(), &QProcess::readyReadStandardError,
|
connect(logcatProcess.get(), &QProcess::readyReadStandardError,
|
||||||
this, &AndroidRunnerWorker::logcatReadStandardError);
|
this, &AndroidRunnerWorker::logcatReadStandardError);
|
||||||
|
|
||||||
// Its assumed that the device or avd returned by selector() is online.
|
// 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;
|
QString errorMessage;
|
||||||
|
|
||||||
@@ -507,9 +481,20 @@ void AndroidRunnerWorker::asyncStart(const QString &intentName,
|
|||||||
|
|
||||||
QTC_ASSERT(!m_adbLogcatProcess, /**/);
|
QTC_ASSERT(!m_adbLogcatProcess, /**/);
|
||||||
m_adbLogcatProcess = std::move(logcatProcess);
|
m_adbLogcatProcess = std::move(logcatProcess);
|
||||||
m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(),
|
|
||||||
m_packageName),
|
m_timeoutTimer.reset(new QTimer);
|
||||||
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
|
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()
|
bool AndroidRunnerWorker::adbShellAmNeedsQuotes()
|
||||||
@@ -545,6 +530,19 @@ bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage,
|
|||||||
return response.result == Utils::SynchronousProcessResponse::Finished;
|
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()
|
void AndroidRunnerWorker::handleRemoteDebuggerRunning()
|
||||||
{
|
{
|
||||||
if (m_useCppDebugger) {
|
if (m_useCppDebugger) {
|
||||||
@@ -558,21 +556,79 @@ void AndroidRunnerWorker::handleRemoteDebuggerRunning()
|
|||||||
|
|
||||||
runAdb(selector() << "push" << tmp.fileName() << m_pongFile);
|
runAdb(selector() << "push" << tmp.fileName() << m_pongFile);
|
||||||
}
|
}
|
||||||
QTC_CHECK(m_processPID != -1);
|
QTC_CHECK(!m_processPids.isEmpty());
|
||||||
}
|
}
|
||||||
emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
|
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)
|
void AndroidRunnerWorker::asyncStop(const QVector<QStringList> &adbCommands)
|
||||||
{
|
{
|
||||||
if (!m_pidFinder.isFinished())
|
m_timeoutTimer.reset();
|
||||||
m_pidFinder.cancel();
|
m_pidsFinderProcess.reset();
|
||||||
|
if (!m_processPids.isEmpty())
|
||||||
if (m_processPID != -1) {
|
|
||||||
forceStop();
|
forceStop();
|
||||||
}
|
|
||||||
foreach (const QStringList &entry, adbCommands)
|
foreach (const QStringList &entry, adbCommands)
|
||||||
runAdb(selector() << entry);
|
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)
|
void AndroidRunnerWorker::setAdbParameters(const QString &packageName, const QStringList &selector)
|
||||||
@@ -594,58 +650,48 @@ void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buff
|
|||||||
buffer.clear();
|
buffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString pidString = QString::number(m_processPID);
|
|
||||||
foreach (const QByteArray &msg, lines) {
|
foreach (const QByteArray &msg, lines) {
|
||||||
const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n');
|
const QString line = QString::fromUtf8(msg.trimmed());
|
||||||
if (!line.contains(pidString))
|
if (onlyError)
|
||||||
continue;
|
emit remoteErrorOutput(line);
|
||||||
if (m_logCatRegExp.exactMatch(line)) {
|
else
|
||||||
// Android M
|
emit remoteOutput(line);
|
||||||
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/"))
|
|
||||||
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
|
// Don't write to m_psProc from a different thread
|
||||||
QTC_ASSERT(QThread::currentThread() == thread(), return);
|
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.")
|
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.")
|
||||||
.arg(m_packageName));
|
.arg(m_packageName));
|
||||||
// App died/killed. Reset log and monitor processes.
|
// App died/killed. Reset log and monitor processes.
|
||||||
|
forceStop();
|
||||||
m_adbLogcatProcess.reset();
|
m_adbLogcatProcess.reset();
|
||||||
m_psIsAlive.reset();
|
m_timeoutTimer.reset();
|
||||||
} else {
|
} else if (isFirst) {
|
||||||
|
m_timeoutTimer.reset();
|
||||||
if (m_useCppDebugger) {
|
if (m_useCppDebugger) {
|
||||||
// This will be funneled to the engine to actually start and attach
|
// This will be funneled to the engine to actually start and attach
|
||||||
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
||||||
QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort.number());
|
QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort.number());
|
||||||
emit remoteServerRunning(serverChannel, m_processPID);
|
emit remoteServerRunning(serverChannel, pidInfo.pid);
|
||||||
} else if (m_qmlDebugServices == QmlDebug::QmlDebuggerServices) {
|
} else if (m_qmlDebugServices == QmlDebug::QmlDebuggerServices) {
|
||||||
// This will be funneled to the engine to actually start and attach
|
// This will be funneled to the engine to actually start and attach
|
||||||
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
||||||
QByteArray serverChannel = QByteArray::number(m_qmlPort.number());
|
QByteArray serverChannel = QByteArray::number(m_qmlPort.number());
|
||||||
emit remoteServerRunning(serverChannel, m_processPID);
|
emit remoteServerRunning(serverChannel, pidInfo.pid);
|
||||||
} else if (m_qmlDebugServices == QmlDebug::QmlProfilerServices) {
|
} else if (m_qmlDebugServices == QmlDebug::QmlProfilerServices) {
|
||||||
emit remoteProcessStarted(Utils::Port(), m_qmlPort);
|
emit remoteProcessStarted(Utils::Port(), m_qmlPort);
|
||||||
} else {
|
} else {
|
||||||
@@ -653,27 +699,18 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
|
|||||||
emit remoteProcessStarted(Utils::Port(), Utils::Port());
|
emit remoteProcessStarted(Utils::Port(), Utils::Port());
|
||||||
}
|
}
|
||||||
logcatReadStandardOutput();
|
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()
|
void AndroidRunnerWorker::logcatReadStandardError()
|
||||||
{
|
{
|
||||||
if (m_processPID != -1)
|
if (!m_processPids.isEmpty() && m_adbLogcatProcess)
|
||||||
logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true);
|
logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidRunnerWorker::logcatReadStandardOutput()
|
void AndroidRunnerWorker::logcatReadStandardOutput()
|
||||||
{
|
{
|
||||||
if (m_processPID != -1)
|
if (!m_processPids.isEmpty() && m_adbLogcatProcess)
|
||||||
logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false);
|
logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,6 +761,10 @@ AndroidRunner::AndroidRunner(QObject *parent, RunConfiguration *runConfig, Core:
|
|||||||
this, &AndroidRunner::remoteOutput);
|
this, &AndroidRunner::remoteOutput);
|
||||||
connect(m_worker.data(), &AndroidRunnerWorker::remoteErrorOutput,
|
connect(m_worker.data(), &AndroidRunnerWorker::remoteErrorOutput,
|
||||||
this, &AndroidRunner::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();
|
m_thread.start();
|
||||||
}
|
}
|
||||||
@@ -791,8 +832,9 @@ void AndroidRunner::launchAVD()
|
|||||||
emit adbParametersChanged(m_androidRunnable.packageName,
|
emit adbParametersChanged(m_androidRunnable.packageName,
|
||||||
AndroidDeviceInfo::adbSelector(info.serialNumber));
|
AndroidDeviceInfo::adbSelector(info.serialNumber));
|
||||||
if (info.isValid()) {
|
if (info.isValid()) {
|
||||||
if (AndroidConfigurations::currentConfig().findAvd(info.avdname).isEmpty()) {
|
AndroidAvdManager avdManager;
|
||||||
bool launched = AndroidConfigurations::currentConfig().startAVDAsync(info.avdname);
|
if (avdManager.findAvd(info.avdname).isEmpty()) {
|
||||||
|
bool launched = avdManager.startAvdAsync(info.avdname);
|
||||||
m_launchedAVDName = launched ? info.avdname:"";
|
m_launchedAVDName = launched ? info.avdname:"";
|
||||||
} else {
|
} else {
|
||||||
m_launchedAVDName.clear();
|
m_launchedAVDName.clear();
|
||||||
@@ -803,11 +845,12 @@ void AndroidRunner::launchAVD()
|
|||||||
void AndroidRunner::checkAVD()
|
void AndroidRunner::checkAVD()
|
||||||
{
|
{
|
||||||
const AndroidConfig &config = AndroidConfigurations::currentConfig();
|
const AndroidConfig &config = AndroidConfigurations::currentConfig();
|
||||||
QString serialNumber = config.findAvd(m_launchedAVDName);
|
AndroidAvdManager avdManager(config);
|
||||||
|
QString serialNumber = avdManager.findAvd(m_launchedAVDName);
|
||||||
if (!serialNumber.isEmpty())
|
if (!serialNumber.isEmpty())
|
||||||
return; // try again on next timer hit
|
return; // try again on next timer hit
|
||||||
|
|
||||||
if (config.hasFinishedBooting(serialNumber)) {
|
if (avdManager.isAvdBooted(serialNumber)) {
|
||||||
m_checkAVDTimer.stop();
|
m_checkAVDTimer.stop();
|
||||||
AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber);
|
AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber);
|
||||||
emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands);
|
emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands);
|
||||||
|
|||||||
@@ -75,6 +75,9 @@ signals:
|
|||||||
void adbParametersChanged(const QString &packageName, const QStringList &selector);
|
void adbParametersChanged(const QString &packageName, const QStringList &selector);
|
||||||
void avdDetected();
|
void avdDetected();
|
||||||
|
|
||||||
|
void pidFound(qint64, const QString &name);
|
||||||
|
void pidLost(qint64);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void checkAVD();
|
void checkAVD();
|
||||||
void launchAVD();
|
void launchAVD();
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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,6 +30,7 @@
|
|||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidconstants.h"
|
#include "androidconstants.h"
|
||||||
#include "androidtoolchain.h"
|
#include "androidtoolchain.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
void AvdModel::setAvdList(const QVector<AndroidDeviceInfo> &list)
|
void AvdModel::setAvdList(const AndroidDeviceInfoList &list)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_list = list;
|
m_list = list;
|
||||||
@@ -128,10 +129,13 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
|
|||||||
m_ndkState(NotSet),
|
m_ndkState(NotSet),
|
||||||
m_javaState(NotSet),
|
m_javaState(NotSet),
|
||||||
m_ui(new Ui_AndroidSettingsWidget),
|
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->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->deprecatedInfoIconLabel->setPixmap(Utils::Icons::INFO.pixmap());
|
||||||
|
|
||||||
connect(&m_checkGdbWatcher, &QFutureWatcherBase::finished,
|
connect(&m_checkGdbWatcher, &QFutureWatcherBase::finished,
|
||||||
this, &AndroidSettingsWidget::checkGdbFinished);
|
this, &AndroidSettingsWidget::checkGdbFinished);
|
||||||
|
|
||||||
@@ -158,7 +162,8 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
|
|||||||
m_ui->AntLocationPathChooser->setPromptDialogTitle(tr("Select ant Script"));
|
m_ui->AntLocationPathChooser->setPromptDialogTitle(tr("Select ant Script"));
|
||||||
m_ui->AntLocationPathChooser->setInitialBrowsePathBackup(dir);
|
m_ui->AntLocationPathChooser->setInitialBrowsePathBackup(dir);
|
||||||
m_ui->AntLocationPathChooser->setPromptDialogFilter(filter);
|
m_ui->AntLocationPathChooser->setPromptDialogFilter(filter);
|
||||||
m_ui->UseGradleCheckBox->setChecked(m_androidConfig.useGrandle());
|
|
||||||
|
updateGradleBuildUi();
|
||||||
|
|
||||||
m_ui->OpenJDKLocationPathChooser->setFileName(m_androidConfig.openJDKLocation());
|
m_ui->OpenJDKLocationPathChooser->setFileName(m_androidConfig.openJDKLocation());
|
||||||
m_ui->OpenJDKLocationPathChooser->setPromptDialogTitle(tr("Select JDK Path"));
|
m_ui->OpenJDKLocationPathChooser->setPromptDialogTitle(tr("Select JDK Path"));
|
||||||
@@ -463,7 +468,7 @@ void AndroidSettingsWidget::enableAvdControls()
|
|||||||
void AndroidSettingsWidget::startUpdateAvd()
|
void AndroidSettingsWidget::startUpdateAvd()
|
||||||
{
|
{
|
||||||
disableAvdControls();
|
disableAvdControls();
|
||||||
m_virtualDevicesWatcher.setFuture(m_androidConfig.androidVirtualDevicesFuture());
|
m_virtualDevicesWatcher.setFuture(m_avdManager->avdList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::updateAvds()
|
void AndroidSettingsWidget::updateAvds()
|
||||||
@@ -476,6 +481,13 @@ void AndroidSettingsWidget::updateAvds()
|
|||||||
enableAvdControls();
|
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
|
bool AndroidSettingsWidget::sdkLocationIsValid() const
|
||||||
{
|
{
|
||||||
Utils::FileName androidExe = m_androidConfig.sdkLocation();
|
Utils::FileName androidExe = m_androidConfig.sdkLocation();
|
||||||
@@ -505,6 +517,7 @@ void AndroidSettingsWidget::saveSettings()
|
|||||||
void AndroidSettingsWidget::sdkLocationEditingFinished()
|
void AndroidSettingsWidget::sdkLocationEditingFinished()
|
||||||
{
|
{
|
||||||
m_androidConfig.setSdkLocation(Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath()));
|
m_androidConfig.setSdkLocation(Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath()));
|
||||||
|
updateGradleBuildUi();
|
||||||
|
|
||||||
check(Sdk);
|
check(Sdk);
|
||||||
|
|
||||||
@@ -587,12 +600,12 @@ void AndroidSettingsWidget::addAVD()
|
|||||||
disableAvdControls();
|
disableAvdControls();
|
||||||
AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this);
|
AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this);
|
||||||
|
|
||||||
if (info.target.isEmpty()) {
|
if (!info.target.isValid()) {
|
||||||
enableAvdControls();
|
enableAvdControls();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_futureWatcher.setFuture(m_androidConfig.createAVD(info));
|
m_futureWatcher.setFuture(m_avdManager->createAvd(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::avdAdded()
|
void AndroidSettingsWidget::avdAdded()
|
||||||
@@ -620,13 +633,13 @@ void AndroidSettingsWidget::removeAVD()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_androidConfig.removeAVD(avdName);
|
m_avdManager->removeAvd(avdName);
|
||||||
startUpdateAvd();
|
startUpdateAvd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::startAVD()
|
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)
|
void AndroidSettingsWidget::avdActivated(const QModelIndex &index)
|
||||||
@@ -671,16 +684,15 @@ void AndroidSettingsWidget::showGdbWarningDialog()
|
|||||||
|
|
||||||
void AndroidSettingsWidget::manageAVD()
|
void AndroidSettingsWidget::manageAVD()
|
||||||
{
|
{
|
||||||
QProcess *avdProcess = new QProcess();
|
if (m_avdManager->avdManagerUiToolAvailable()) {
|
||||||
connect(this, &QObject::destroyed, avdProcess, &QObject::deleteLater);
|
m_avdManager->launchAvdManagerUiTool();
|
||||||
connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
|
} else {
|
||||||
avdProcess, &QObject::deleteLater);
|
QMessageBox::warning(this, tr("AVD Manager Not Available"),
|
||||||
|
tr("AVD manager UI tool is not available in the installed SDK tools"
|
||||||
avdProcess->setProcessEnvironment(m_androidConfig.androidToolEnvironment().toProcessEnvironment());
|
"(version %1). Use the command line tool \"avdmanager\" for "
|
||||||
QString executable = m_androidConfig.androidToolPath().toString();
|
"advanced AVD management.")
|
||||||
QStringList arguments = QStringList("avd");
|
.arg(m_androidConfig.sdkToolsVersion().toString()));
|
||||||
|
}
|
||||||
avdProcess->start(executable, arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class Ui_AndroidSettingsWidget;
|
class Ui_AndroidSettingsWidget;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
@@ -40,11 +42,13 @@ QT_END_NAMESPACE
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class AndroidAvdManager;
|
||||||
|
|
||||||
class AvdModel: public QAbstractTableModel
|
class AvdModel: public QAbstractTableModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
void setAvdList(const QVector<AndroidDeviceInfo> &list);
|
void setAvdList(const AndroidDeviceInfoList &list);
|
||||||
QString avdName(const QModelIndex &index) const;
|
QString avdName(const QModelIndex &index) const;
|
||||||
QModelIndex indexForAvdName(const QString &avdName) const;
|
QModelIndex indexForAvdName(const QString &avdName) const;
|
||||||
|
|
||||||
@@ -55,7 +59,7 @@ protected:
|
|||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<AndroidDeviceInfo> m_list;
|
AndroidDeviceInfoList m_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AndroidSettingsWidget : public QWidget
|
class AndroidSettingsWidget : public QWidget
|
||||||
@@ -91,6 +95,7 @@ private:
|
|||||||
void checkGdbFinished();
|
void checkGdbFinished();
|
||||||
void showGdbWarningDialog();
|
void showGdbWarningDialog();
|
||||||
void updateAvds();
|
void updateAvds();
|
||||||
|
void updateGradleBuildUi();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Mode { Sdk = 1, Ndk = 2, Java = 4, All = Sdk | Ndk | Java };
|
enum Mode { Sdk = 1, Ndk = 2, Java = 4, All = Sdk | Ndk | Java };
|
||||||
@@ -117,8 +122,9 @@ private:
|
|||||||
QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher;
|
QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher;
|
||||||
QStringList m_gdbCheckPaths;
|
QStringList m_gdbCheckPaths;
|
||||||
|
|
||||||
QFutureWatcher<QVector<AndroidDeviceInfo>> m_virtualDevicesWatcher;
|
QFutureWatcher<AndroidDeviceInfoList> m_virtualDevicesWatcher;
|
||||||
QString m_lastAddedAvd;
|
QString m_lastAddedAvd;
|
||||||
|
std::unique_ptr<AndroidAvdManager> m_avdManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -237,20 +237,30 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="1">
|
<item row="8" column="1">
|
||||||
<widget class="QCheckBox" name="CreateKitCheckBox">
|
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||||
<property name="sizePolicy">
|
<property name="spacing">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<number>2</number>
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="leftMargin">
|
||||||
<string>Automatically create kits for Android tool chains</string>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<item>
|
||||||
<bool>true</bool>
|
<widget class="QCheckBox" name="CreateKitCheckBox">
|
||||||
</property>
|
<property name="sizePolicy">
|
||||||
</widget>
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Automatically create kits for Android tool chains</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="9" column="1">
|
<item row="9" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
@@ -289,17 +299,59 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="1">
|
<item row="10" column="1">
|
||||||
<widget class="QCheckBox" name="UseGradleCheckBox">
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
<property name="sizePolicy">
|
<property name="spacing">
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
<number>4</number>
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="leftMargin">
|
||||||
<string>Use Gradle instead of Ant</string>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<item>
|
||||||
|
<widget class="QCheckBox" name="UseGradleCheckBox">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<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>
|
||||||
<item row="11" column="0">
|
<item row="11" column="0">
|
||||||
<widget class="QLabel" name="AntLocationLabel">
|
<widget class="QLabel" name="AntLocationLabel">
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "avddialog.h"
|
#include "avddialog.h"
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
#include <utils/tooltip/tooltip.h>
|
#include <utils/tooltip/tooltip.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
@@ -67,12 +68,12 @@ AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidCo
|
|||||||
|
|
||||||
bool AvdDialog::isValid() const
|
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
|
QString AvdDialog::name() const
|
||||||
@@ -92,15 +93,23 @@ int AvdDialog::sdcardSize() const
|
|||||||
|
|
||||||
void AvdDialog::updateApiLevelComboBox()
|
void AvdDialog::updateApiLevelComboBox()
|
||||||
{
|
{
|
||||||
QList<SdkPlatform> filteredList;
|
SdkPlatformList filteredList;
|
||||||
QList<SdkPlatform> platforms = m_config->sdkTargets(m_minApiLevel);
|
SdkPlatformList platforms = m_config->sdkTargets(m_minApiLevel);
|
||||||
foreach (const SdkPlatform &platform, platforms) {
|
|
||||||
if (platform.abis.contains(abi()))
|
QString selectedAbi = abi();
|
||||||
filteredList << platform;
|
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->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()) {
|
if (platforms.isEmpty()) {
|
||||||
m_avdDialog.warningIcon->setVisible(true);
|
m_avdDialog.warningIcon->setVisible(true);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
namespace Android {
|
namespace Android {
|
||||||
class AndroidConfig;
|
class AndroidConfig;
|
||||||
|
class SdkPlatform;
|
||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ public:
|
|||||||
explicit AvdDialog(int minApiLevel, const QString &targetArch,
|
explicit AvdDialog(int minApiLevel, const QString &targetArch,
|
||||||
const AndroidConfig *config, QWidget *parent = 0);
|
const AndroidConfig *config, QWidget *parent = 0);
|
||||||
|
|
||||||
QString target() const;
|
Android::SdkPlatform target() const;
|
||||||
QString name() const;
|
QString name() const;
|
||||||
QString abi() const;
|
QString abi() const;
|
||||||
int sdcardSize() const;
|
int sdcardSize() const;
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ void TestConfiguration::completeTestInformation(int runMode)
|
|||||||
m_executableFile = exeString;
|
m_executableFile = exeString;
|
||||||
m_project = project;
|
m_project = project;
|
||||||
m_guessedConfiguration = true;
|
m_guessedConfiguration = true;
|
||||||
|
m_guessedFrom = rc->displayName();
|
||||||
if (runMode == TestRunner::Debug)
|
if (runMode == TestRunner::Debug)
|
||||||
m_runConfig = new TestRunConfiguration(rc->target(), this);
|
m_runConfig = new TestRunConfiguration(rc->target(), this);
|
||||||
}
|
}
|
||||||
@@ -203,11 +204,6 @@ void TestConfiguration::setProject(Project *project)
|
|||||||
m_project = project;
|
m_project = project;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestConfiguration::setGuessedConfiguration(bool guessed)
|
|
||||||
{
|
|
||||||
m_guessedConfiguration = guessed;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString TestConfiguration::executableFilePath() const
|
QString TestConfiguration::executableFilePath() const
|
||||||
{
|
{
|
||||||
if (m_executableFile.isEmpty())
|
if (m_executableFile.isEmpty())
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ public:
|
|||||||
void setDisplayName(const QString &displayName);
|
void setDisplayName(const QString &displayName);
|
||||||
void setEnvironment(const Utils::Environment &env);
|
void setEnvironment(const Utils::Environment &env);
|
||||||
void setProject(ProjectExplorer::Project *project);
|
void setProject(ProjectExplorer::Project *project);
|
||||||
void setGuessedConfiguration(bool guessed);
|
|
||||||
|
|
||||||
QStringList testCases() const { return m_testCases; }
|
QStringList testCases() const { return m_testCases; }
|
||||||
int testCaseCount() const { return m_testCaseCount; }
|
int testCaseCount() const { return m_testCaseCount; }
|
||||||
@@ -77,7 +76,9 @@ public:
|
|||||||
Utils::Environment environment() const { return m_environment; }
|
Utils::Environment environment() const { return m_environment; }
|
||||||
ProjectExplorer::Project *project() const { return m_project.data(); }
|
ProjectExplorer::Project *project() const { return m_project.data(); }
|
||||||
TestRunConfiguration *runConfiguration() const { return m_runConfig; }
|
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,
|
virtual TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||||
QProcess *app) const = 0;
|
QProcess *app) const = 0;
|
||||||
@@ -91,6 +92,7 @@ private:
|
|||||||
QString m_workingDir;
|
QString m_workingDir;
|
||||||
QString m_buildDir;
|
QString m_buildDir;
|
||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
|
QString m_guessedFrom;
|
||||||
Utils::Environment m_environment;
|
Utils::Environment m_environment;
|
||||||
QPointer<ProjectExplorer::Project> m_project;
|
QPointer<ProjectExplorer::Project> m_project;
|
||||||
bool m_guessedConfiguration = false;
|
bool m_guessedConfiguration = false;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
|
|
||||||
#include <utils/outputformat.h>
|
#include <utils/outputformat.h>
|
||||||
#include <utils/runextensions.h>
|
#include <utils/runextensions.h>
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QFutureInterface>
|
#include <QFutureInterface>
|
||||||
@@ -96,6 +97,28 @@ void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
|
|||||||
m_selectedTests = 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,
|
static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
|
||||||
const QList<TestConfiguration *> selectedTests,
|
const QList<TestConfiguration *> selectedTests,
|
||||||
const TestSettings &settings)
|
const TestSettings &settings)
|
||||||
@@ -108,11 +131,14 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
|
|||||||
config->completeTestInformation(TestRunner::Run);
|
config->completeTestInformation(TestRunner::Run);
|
||||||
if (config->project()) {
|
if (config->project()) {
|
||||||
testCaseCount += config->testCaseCount();
|
testCaseCount += config->testCaseCount();
|
||||||
if (!omitRunConfigWarnings && config->guessedConfiguration()) {
|
if (!omitRunConfigWarnings && config->isGuessed()) {
|
||||||
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
|
QString message = TestRunner::tr(
|
||||||
TestRunner::tr("Project's run configuration was guessed for \"%1\".\n"
|
"Project's run configuration was guessed for \"%1\".\n"
|
||||||
"This might cause trouble during execution."
|
"This might cause trouble during execution.\n"
|
||||||
).arg(config->displayName()))));
|
"(guessed from \"%2\")");
|
||||||
|
message = message.arg(config->displayName()).arg(config->runConfigDisplayName());
|
||||||
|
futureInterface.reportResult(
|
||||||
|
TestResultPtr(new FaultyTestResult(Result::MessageWarn, message)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
|
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageWarn,
|
||||||
@@ -173,11 +199,15 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageFatal,
|
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) {
|
if (testProcess.exitStatus() == QProcess::CrashExit) {
|
||||||
futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageFatal,
|
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) {
|
if (canceledByTimeout) {
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ AutotoolsProject::AutotoolsProject(const Utils::FileName &fileName) :
|
|||||||
setId(Constants::AUTOTOOLS_PROJECT_ID);
|
setId(Constants::AUTOTOOLS_PROJECT_ID);
|
||||||
setProjectContext(Core::Context(Constants::PROJECT_CONTEXT));
|
setProjectContext(Core::Context(Constants::PROJECT_CONTEXT));
|
||||||
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
|
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
|
||||||
|
setDisplayName(projectDirectory().fileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
AutotoolsProject::~AutotoolsProject()
|
AutotoolsProject::~AutotoolsProject()
|
||||||
@@ -90,11 +91,6 @@ AutotoolsProject::~AutotoolsProject()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AutotoolsProject::displayName() const
|
|
||||||
{
|
|
||||||
return projectFilePath().toFileInfo().absoluteDir().dirName();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AutotoolsProject::defaultBuildDirectory(const QString &projectPath)
|
QString AutotoolsProject::defaultBuildDirectory(const QString &projectPath)
|
||||||
{
|
{
|
||||||
return QFileInfo(projectPath).absolutePath();
|
return QFileInfo(projectPath).absolutePath();
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ public:
|
|||||||
explicit AutotoolsProject(const Utils::FileName &fileName);
|
explicit AutotoolsProject(const Utils::FileName &fileName);
|
||||||
~AutotoolsProject() override;
|
~AutotoolsProject() override;
|
||||||
|
|
||||||
QString displayName() const override;
|
|
||||||
static QString defaultBuildDirectory(const QString &projectPath);
|
static QString defaultBuildDirectory(const QString &projectPath);
|
||||||
QStringList buildTargets() const;
|
QStringList buildTargets() const;
|
||||||
|
|
||||||
|
|||||||
@@ -42,9 +42,3 @@ bool AutotoolsProjectNode::showInSimpleTree() const
|
|||||||
{
|
{
|
||||||
return true;
|
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);
|
AutotoolsProjectNode(const Utils::FileName &projectDirectory);
|
||||||
|
|
||||||
bool showInSimpleTree() const override;
|
bool showInSimpleTree() const override;
|
||||||
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ void BeautifierPlugin::extensionsInitialized()
|
|||||||
addAutoReleasedObject(object);
|
addAutoReleasedObject(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_generalSettings = new GeneralSettings;
|
m_generalSettings.reset(new GeneralSettings);
|
||||||
auto settingsPage = new GeneralOptionsPage(m_generalSettings, toolIds, this);
|
auto settingsPage = new GeneralOptionsPage(m_generalSettings, toolIds, this);
|
||||||
addAutoReleasedObject(settingsPage);
|
addAutoReleasedObject(settingsPage);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class IDocument;
|
class IDocument;
|
||||||
@@ -86,7 +87,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void updateActions(Core::IEditor *editor = nullptr);
|
void updateActions(Core::IEditor *editor = nullptr);
|
||||||
QList<BeautifierAbstractTool *> m_tools;
|
QList<BeautifierAbstractTool *> m_tools;
|
||||||
GeneralSettings *m_generalSettings = nullptr;
|
QSharedPointer<GeneralSettings> m_generalSettings;
|
||||||
QHash<QObject*, QMetaObject::Connection> m_autoFormatConnections;
|
QHash<QObject*, QMetaObject::Connection> m_autoFormatConnections;
|
||||||
void formatEditor(TextEditor::TextEditorWidget *editor, const Command &command,
|
void formatEditor(TextEditor::TextEditorWidget *editor, const Command &command,
|
||||||
int startPos = -1, int endPos = 0);
|
int startPos = -1, int endPos = 0);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
namespace Beautifier {
|
namespace Beautifier {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
GeneralOptionsPageWidget::GeneralOptionsPageWidget(GeneralSettings *settings,
|
GeneralOptionsPageWidget::GeneralOptionsPageWidget(const QSharedPointer<GeneralSettings> &settings,
|
||||||
const QStringList &toolIds, QWidget *parent) :
|
const QStringList &toolIds, QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::GeneralOptionsPage),
|
ui(new Ui::GeneralOptionsPage),
|
||||||
@@ -73,8 +73,8 @@ void GeneralOptionsPageWidget::apply(bool *autoFormatChanged)
|
|||||||
m_settings->save();
|
m_settings->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
GeneralOptionsPage::GeneralOptionsPage(GeneralSettings *settings, const QStringList &toolIds,
|
GeneralOptionsPage::GeneralOptionsPage(const QSharedPointer<GeneralSettings> &settings,
|
||||||
QObject *parent) :
|
const QStringList &toolIds, QObject *parent) :
|
||||||
IOptionsPage(parent),
|
IOptionsPage(parent),
|
||||||
m_settings(settings),
|
m_settings(settings),
|
||||||
m_toolIds(toolIds)
|
m_toolIds(toolIds)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <coreplugin/dialogs/ioptionspage.h>
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QSharedPointer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
namespace Beautifier {
|
namespace Beautifier {
|
||||||
@@ -42,15 +43,15 @@ class GeneralOptionsPageWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GeneralOptionsPageWidget(GeneralSettings *settings, const QStringList &toolIds,
|
explicit GeneralOptionsPageWidget(const QSharedPointer<GeneralSettings> &settings,
|
||||||
QWidget *parent = nullptr);
|
const QStringList &toolIds, QWidget *parent = nullptr);
|
||||||
virtual ~GeneralOptionsPageWidget();
|
virtual ~GeneralOptionsPageWidget();
|
||||||
void restore();
|
void restore();
|
||||||
void apply(bool *autoFormatChanged);
|
void apply(bool *autoFormatChanged);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::GeneralOptionsPage *ui;
|
Ui::GeneralOptionsPage *ui;
|
||||||
GeneralSettings *m_settings;
|
QSharedPointer<GeneralSettings> m_settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GeneralOptionsPage : public Core::IOptionsPage
|
class GeneralOptionsPage : public Core::IOptionsPage
|
||||||
@@ -58,8 +59,8 @@ class GeneralOptionsPage : public Core::IOptionsPage
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GeneralOptionsPage(GeneralSettings *settings, const QStringList &toolIds,
|
explicit GeneralOptionsPage(const QSharedPointer<GeneralSettings> &settings,
|
||||||
QObject *parent = nullptr);
|
const QStringList &toolIds, QObject *parent = nullptr);
|
||||||
QWidget *widget() override;
|
QWidget *widget() override;
|
||||||
void apply() override;
|
void apply() override;
|
||||||
void finish() override;
|
void finish() override;
|
||||||
@@ -69,7 +70,7 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
QPointer<GeneralOptionsPageWidget> m_widget;
|
QPointer<GeneralOptionsPageWidget> m_widget;
|
||||||
GeneralSettings *m_settings;
|
QSharedPointer<GeneralSettings> m_settings;
|
||||||
QStringList m_toolIds;
|
QStringList m_toolIds;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public:
|
|||||||
CPlusPlus::Overview overview;
|
CPlusPlus::Overview overview;
|
||||||
|
|
||||||
//! timer
|
//! timer
|
||||||
QPointer<QTimer> timer;
|
QTimer timer;
|
||||||
|
|
||||||
// documents
|
// documents
|
||||||
//! Documents read write lock
|
//! Documents read write lock
|
||||||
@@ -161,16 +161,14 @@ Parser::Parser(QObject *parent)
|
|||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
d(new ParserPrivate())
|
d(new ParserPrivate())
|
||||||
{
|
{
|
||||||
d->timer = new QTimer(this);
|
d->timer.setSingleShot(true);
|
||||||
d->timer->setObjectName(QLatin1String("ClassViewParser::timer"));
|
|
||||||
d->timer->setSingleShot(true);
|
|
||||||
|
|
||||||
// connect signal/slots
|
// connect signal/slots
|
||||||
// internal data reset
|
// internal data reset
|
||||||
connect(this, &Parser::resetDataDone, this, &Parser::onResetDataDone, Qt::QueuedConnection);
|
connect(this, &Parser::resetDataDone, this, &Parser::onResetDataDone, Qt::QueuedConnection);
|
||||||
|
|
||||||
// timer for emitting changes
|
// 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());
|
item = ParserTreeItem::Ptr(new ParserTreeItem());
|
||||||
|
|
||||||
if (d->flatMode)
|
if (d->flatMode)
|
||||||
addFlatTree(item, prj->rootProjectNode());
|
addFlatTree(item, prj);
|
||||||
else
|
else
|
||||||
addProjectNode(item, prj->rootProjectNode());
|
addProjectTree(item, prj);
|
||||||
|
|
||||||
|
item->setIcon(prj->containerNode()->icon());
|
||||||
|
|
||||||
item->setIcon(prj->rootProjectNode()->icon());
|
|
||||||
rootItem->appendChild(item, inf);
|
rootItem->appendChild(item, inf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +314,7 @@ ParserTreeItem::ConstPtr Parser::parse()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
void Parser::addProject(const ParserTreeItem::Ptr &item, const QStringList &fileList,
|
void Parser::addProject(const ParserTreeItem::Ptr &item, const QStringList &fileList,
|
||||||
const QString &projectId)
|
const QString &projectId)
|
||||||
{
|
{
|
||||||
// recalculate cache tree if needed
|
// recalculate cache tree if needed
|
||||||
ParserTreeItem::Ptr prj(getCachedOrParseProjectTree(fileList, projectId));
|
ParserTreeItem::Ptr prj(getCachedOrParseProjectTree(fileList, projectId));
|
||||||
@@ -542,10 +541,8 @@ void Parser::parseDocument(const CPlusPlus::Document::Ptr &doc)
|
|||||||
|
|
||||||
getParseDocumentTree(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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,7 +685,7 @@ void Parser::requestCurrentState()
|
|||||||
void Parser::emitCurrentTree()
|
void Parser::emitCurrentTree()
|
||||||
{
|
{
|
||||||
// stop timer if it is active right now
|
// stop timer if it is active right now
|
||||||
d->timer->stop();
|
d->timer.stop();
|
||||||
|
|
||||||
d->rootItemLocker.lockForWrite();
|
d->rootItemLocker.lockForWrite();
|
||||||
d->rootItem = parse();
|
d->rootItem = parse();
|
||||||
@@ -702,26 +699,6 @@ void Parser::emitCurrentTree()
|
|||||||
emit treeDataUpdate(std);
|
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.
|
Generates projects like the Project Explorer.
|
||||||
\a item specifies the item and \a node specifies the root node.
|
\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.
|
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;
|
QStringList projectList;
|
||||||
if (!node)
|
if (!project)
|
||||||
return projectList;
|
return projectList;
|
||||||
|
|
||||||
const QString nodePath = node->filePath().toString();
|
const QString projectPath = project->projectFilePath().toString();
|
||||||
|
|
||||||
// our own files
|
// our own files
|
||||||
QStringList fileList;
|
QStringList fileList;
|
||||||
|
|
||||||
CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(nodePath);
|
CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(projectPath);
|
||||||
// try to improve parsing speed by internal cache
|
// try to improve parsing speed by internal cache
|
||||||
if (cit != d->cachedPrjFileLists.constEnd()) {
|
if (cit != d->cachedPrjFileLists.constEnd()) {
|
||||||
fileList = cit.value();
|
fileList = cit.value();
|
||||||
} else {
|
} else {
|
||||||
fileList = projectNodeFileList(node);
|
fileList = project->files(Project::SourceFiles);
|
||||||
d->cachedPrjFileLists[nodePath] = fileList;
|
d->cachedPrjFileLists[projectPath] = fileList;
|
||||||
}
|
}
|
||||||
if (fileList.count() > 0) {
|
if (fileList.count() > 0) {
|
||||||
addProject(item, fileList, node->filePath().toString());
|
addProject(item, fileList, projectPath);
|
||||||
projectList << node->filePath().toString();
|
projectList << projectPath;
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return projectList;
|
return projectList;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Parser::getAllFiles(const ProjectNode *node)
|
QStringList Parser::getAllFiles(const Project *project)
|
||||||
{
|
{
|
||||||
QStringList fileList;
|
QStringList fileList;
|
||||||
|
|
||||||
if (!node)
|
if (!project)
|
||||||
return fileList;
|
return fileList;
|
||||||
|
|
||||||
const QString nodePath = node->filePath().toString();
|
const QString nodePath = project->projectFilePath().toString();
|
||||||
|
|
||||||
CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(nodePath);
|
CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(nodePath);
|
||||||
// try to improve parsing speed by internal cache
|
// try to improve parsing speed by internal cache
|
||||||
if (cit != d->cachedPrjFileLists.constEnd()) {
|
if (cit != d->cachedPrjFileLists.constEnd()) {
|
||||||
fileList = cit.value();
|
fileList = cit.value();
|
||||||
} else {
|
} else {
|
||||||
fileList = projectNodeFileList(node);
|
fileList = project->files(Project::SourceFiles);
|
||||||
d->cachedPrjFileLists[nodePath] = fileList;
|
d->cachedPrjFileLists[nodePath] = fileList;
|
||||||
}
|
}
|
||||||
// subnodes
|
|
||||||
|
|
||||||
for (const Node *n : node->nodes())
|
|
||||||
if (const ProjectNode *project = n->asProjectNode())
|
|
||||||
fileList += getAllFiles(project);
|
|
||||||
return fileList;
|
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;
|
return;
|
||||||
|
|
||||||
QStringList fileList = getAllFiles(node);
|
QStringList fileList = getAllFiles(project);
|
||||||
fileList.removeDuplicates();
|
fileList.removeDuplicates();
|
||||||
|
|
||||||
if (fileList.count() > 0) {
|
if (fileList.count() > 0) {
|
||||||
addProject(item, fileList, node->filePath().toString());
|
addProject(item, fileList, project->projectFilePath().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,13 +111,9 @@ protected:
|
|||||||
|
|
||||||
ParserTreeItem::ConstPtr findItemByRoot(const QStandardItem *item, bool skipRoot = false) const;
|
ParserTreeItem::ConstPtr findItemByRoot(const QStandardItem *item, bool skipRoot = false) const;
|
||||||
|
|
||||||
QStringList addProjectNode(const ParserTreeItem::Ptr &item,
|
QStringList addProjectTree(const ParserTreeItem::Ptr &item, const ProjectExplorer::Project *project);
|
||||||
const ProjectExplorer::ProjectNode *node);
|
QStringList getAllFiles(const ProjectExplorer::Project *project);
|
||||||
QStringList getAllFiles(const ProjectExplorer::ProjectNode *node);
|
void addFlatTree(const ParserTreeItem::Ptr &item, const ProjectExplorer::Project *project);
|
||||||
void addFlatTree(const ParserTreeItem::Ptr &item,
|
|
||||||
const ProjectExplorer::ProjectNode *node);
|
|
||||||
|
|
||||||
QStringList projectNodeFileList(const ProjectExplorer::FolderNode *node) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! Private class data pointer
|
//! Private class data pointer
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const
|
|||||||
const QString absFile =
|
const QString absFile =
|
||||||
viewRootDir.absoluteFilePath(
|
viewRootDir.absoluteFilePath(
|
||||||
QDir::fromNativeSeparators(buffer.left(atatpos)));
|
QDir::fromNativeSeparators(buffer.left(atatpos)));
|
||||||
QTC_CHECK(QFile(absFile).exists());
|
QTC_CHECK(QFileInfo::exists(absFile));
|
||||||
QTC_CHECK(!absFile.isEmpty());
|
QTC_CHECK(!absFile.isEmpty());
|
||||||
|
|
||||||
// "cleartool ls" of a derived object looks like this:
|
// "cleartool ls" of a derived object looks like this:
|
||||||
@@ -274,7 +274,7 @@ FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const
|
|||||||
else
|
else
|
||||||
return FileStatus::CheckedIn;
|
return FileStatus::CheckedIn;
|
||||||
} else {
|
} else {
|
||||||
QTC_CHECK(QFile(fileName).exists());
|
QTC_CHECK(QFileInfo::exists(fileName));
|
||||||
QTC_CHECK(!fileName.isEmpty());
|
QTC_CHECK(!fileName.isEmpty());
|
||||||
return FileStatus::NotManaged;
|
return FileStatus::NotManaged;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void ClearCaseSync::processCleartoolLsLine(const QDir &viewRootDir, const QStrin
|
|||||||
const QString absFile =
|
const QString absFile =
|
||||||
viewRootDir.absoluteFilePath(
|
viewRootDir.absoluteFilePath(
|
||||||
QDir::fromNativeSeparators(buffer.left(atatpos)));
|
QDir::fromNativeSeparators(buffer.left(atatpos)));
|
||||||
QTC_CHECK(QFile(absFile).exists());
|
QTC_CHECK(QFileInfo::exists(absFile));
|
||||||
QTC_CHECK(!absFile.isEmpty());
|
QTC_CHECK(!absFile.isEmpty());
|
||||||
|
|
||||||
QString ccState;
|
QString ccState;
|
||||||
|
|||||||
@@ -149,10 +149,7 @@ void CMakeBuildConfiguration::ctor()
|
|||||||
emit dataAvailable();
|
emit dataAvailable();
|
||||||
});
|
});
|
||||||
connect(m_buildDirManager.get(), &BuildDirManager::errorOccured,
|
connect(m_buildDirManager.get(), &BuildDirManager::errorOccured,
|
||||||
this, [this, project](const QString &msg) {
|
this, &CMakeBuildConfiguration::setError);
|
||||||
project->updateProjectData(this);
|
|
||||||
setError(msg);
|
|
||||||
});
|
|
||||||
connect(m_buildDirManager.get(), &BuildDirManager::configurationStarted,
|
connect(m_buildDirManager.get(), &BuildDirManager::configurationStarted,
|
||||||
this, [this, project]() {
|
this, [this, project]() {
|
||||||
project->handleParsingStarted();
|
project->handleParsingStarted();
|
||||||
|
|||||||
@@ -258,6 +258,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
|
|||||||
updateFromKit();
|
updateFromKit();
|
||||||
connect(m_buildConfiguration->target(), &ProjectExplorer::Target::kitChanged,
|
connect(m_buildConfiguration->target(), &ProjectExplorer::Target::kitChanged,
|
||||||
this, &CMakeBuildSettingsWidget::updateFromKit);
|
this, &CMakeBuildSettingsWidget::updateFromKit);
|
||||||
|
connect(m_buildConfiguration, &CMakeBuildConfiguration::enabledChanged,
|
||||||
|
this, [this]() { setError(m_buildConfiguration->disabledReason()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMakeBuildSettingsWidget::setError(const QString &message)
|
void CMakeBuildSettingsWidget::setError(const QString &message)
|
||||||
|
|||||||
@@ -444,19 +444,26 @@ QString CMakeBuildStepConfigWidget::displayName() const
|
|||||||
return tr("Build", "CMakeProjectManager::CMakeBuildStepConfigWidget display name.");
|
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()
|
void CMakeBuildStepConfigWidget::buildTargetsChanged()
|
||||||
{
|
{
|
||||||
const bool wasBlocked = m_buildTargetsList->blockSignals(true);
|
const bool wasBlocked = m_buildTargetsList->blockSignals(true);
|
||||||
m_buildTargetsList->clear();
|
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));
|
auto pro = static_cast<CMakeProject *>(m_buildStep->project());
|
||||||
QFont f;
|
|
||||||
f.setItalic(true);
|
|
||||||
item->setFont(f);
|
|
||||||
|
|
||||||
CMakeProject *pro = static_cast<CMakeProject *>(m_buildStep->project());
|
|
||||||
QStringList targetList = pro->buildTargetTitles();
|
QStringList targetList = pro->buildTargetTitles();
|
||||||
targetList.sort();
|
targetList.sort();
|
||||||
|
|
||||||
|
|||||||
@@ -180,15 +180,6 @@ bool CMakeCbpParser::parseCbpFile(CMakeTool::PathMapper mapper, const FileName &
|
|||||||
|
|
||||||
fi.close();
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ CMakeProject::CMakeProject(const FileName &fileName) : Project(Constants::CMAKEM
|
|||||||
setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID);
|
setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID);
|
||||||
setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT));
|
setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT));
|
||||||
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
|
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
|
||||||
|
setDisplayName(projectDirectory().fileName());
|
||||||
|
|
||||||
connect(this, &CMakeProject::activeTargetChanged, this, &CMakeProject::handleActiveTargetChanged);
|
connect(this, &CMakeProject::activeTargetChanged, this, &CMakeProject::handleActiveTargetChanged);
|
||||||
connect(&m_treeScanner, &TreeScanner::finished, this, &CMakeProject::handleTreeScanningFinished);
|
connect(&m_treeScanner, &TreeScanner::finished, this, &CMakeProject::handleTreeScanningFinished);
|
||||||
@@ -145,8 +146,10 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc)
|
|||||||
Kit *k = t->kit();
|
Kit *k = t->kit();
|
||||||
|
|
||||||
auto newRoot = bc->generateProjectTree(m_allFiles);
|
auto newRoot = bc->generateProjectTree(m_allFiles);
|
||||||
if (newRoot)
|
if (newRoot) {
|
||||||
setRootProjectNode(newRoot);
|
setRootProjectNode(newRoot);
|
||||||
|
setDisplayName(newRoot->displayName());
|
||||||
|
}
|
||||||
|
|
||||||
updateApplicationAndDeploymentTargets();
|
updateApplicationAndDeploymentTargets();
|
||||||
updateTargetRunConfigurations(t);
|
updateTargetRunConfigurations(t);
|
||||||
@@ -296,12 +299,6 @@ bool CMakeProject::hasBuildTarget(const QString &title) const
|
|||||||
return anyOf(buildTargets(), [title](const CMakeBuildTarget &ct) { return ct.title == title; });
|
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)
|
Project::RestoreResult CMakeProject::fromMap(const QVariantMap &map, QString *errorMessage)
|
||||||
{
|
{
|
||||||
RestoreResult result = Project::fromMap(map, errorMessage);
|
RestoreResult result = Project::fromMap(map, errorMessage);
|
||||||
|
|||||||
@@ -86,8 +86,6 @@ public:
|
|||||||
explicit CMakeProject(const Utils::FileName &filename);
|
explicit CMakeProject(const Utils::FileName &filename);
|
||||||
~CMakeProject() final;
|
~CMakeProject() final;
|
||||||
|
|
||||||
QString displayName() const final;
|
|
||||||
|
|
||||||
QStringList buildTargetTitles(bool runnable = false) const;
|
QStringList buildTargetTitles(bool runnable = false) const;
|
||||||
bool hasBuildTarget(const QString &title) const;
|
bool hasBuildTarget(const QString &title) const;
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ CMakeManager::CMakeManager() :
|
|||||||
m_runCMakeAction(new QAction(QIcon(), tr("Run CMake"), this)),
|
m_runCMakeAction(new QAction(QIcon(), tr("Run CMake"), this)),
|
||||||
m_clearCMakeCacheAction(new QAction(QIcon(), tr("Clear CMake Configuration"), this)),
|
m_clearCMakeCacheAction(new QAction(QIcon(), tr("Clear CMake Configuration"), this)),
|
||||||
m_runCMakeActionContextMenu(new QAction(QIcon(), tr("Run CMake"), 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::ActionContainer *mbuild =
|
||||||
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
|
Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT);
|
||||||
|
|||||||
@@ -56,12 +56,6 @@ bool CMakeInputsNode::showInSimpleTree() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ProjectExplorer::ProjectAction> CMakeInputsNode::supportedActions(ProjectExplorer::Node *node) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(node);
|
|
||||||
return QList<ProjectExplorer::ProjectAction>();
|
|
||||||
}
|
|
||||||
|
|
||||||
CMakeListsNode::CMakeListsNode(const Utils::FileName &cmakeListPath) :
|
CMakeListsNode::CMakeListsNode(const Utils::FileName &cmakeListPath) :
|
||||||
ProjectExplorer::ProjectNode(cmakeListPath)
|
ProjectExplorer::ProjectNode(cmakeListPath)
|
||||||
{
|
{
|
||||||
@@ -80,12 +74,6 @@ bool CMakeListsNode::showInSimpleTree() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ProjectExplorer::ProjectAction> CMakeListsNode::supportedActions(ProjectExplorer::Node *node) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(node);
|
|
||||||
return QList<ProjectExplorer::ProjectAction>();
|
|
||||||
}
|
|
||||||
|
|
||||||
CMakeProjectNode::CMakeProjectNode(const Utils::FileName &directory) :
|
CMakeProjectNode::CMakeProjectNode(const Utils::FileName &directory) :
|
||||||
ProjectExplorer::ProjectNode(directory)
|
ProjectExplorer::ProjectNode(directory)
|
||||||
{
|
{
|
||||||
@@ -103,12 +91,6 @@ QString CMakeProjectNode::tooltip() const
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ProjectExplorer::ProjectAction> CMakeProjectNode::supportedActions(ProjectExplorer::Node *node) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(node);
|
|
||||||
return QList<ProjectExplorer::ProjectAction>();
|
|
||||||
}
|
|
||||||
|
|
||||||
CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory) :
|
CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory) :
|
||||||
ProjectExplorer::ProjectNode(directory)
|
ProjectExplorer::ProjectNode(directory)
|
||||||
{
|
{
|
||||||
@@ -126,12 +108,6 @@ QString CMakeTargetNode::tooltip() const
|
|||||||
return m_tooltip;
|
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,
|
void CMakeTargetNode::setTargetInformation(const QList<Utils::FileName> &artifacts,
|
||||||
const QString &type)
|
const QString &type)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,8 +28,6 @@
|
|||||||
#include <projectexplorer/projectnodes.h>
|
#include <projectexplorer/projectnodes.h>
|
||||||
|
|
||||||
namespace CMakeProjectManager {
|
namespace CMakeProjectManager {
|
||||||
class CMakeProject;
|
|
||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class CMakeInputsNode : public ProjectExplorer::ProjectNode
|
class CMakeInputsNode : public ProjectExplorer::ProjectNode
|
||||||
@@ -40,7 +38,6 @@ public:
|
|||||||
static Utils::FileName inputsPathFromCMakeListsPath(const Utils::FileName &cmakeLists);
|
static Utils::FileName inputsPathFromCMakeListsPath(const Utils::FileName &cmakeLists);
|
||||||
|
|
||||||
bool showInSimpleTree() const final;
|
bool showInSimpleTree() const final;
|
||||||
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CMakeListsNode : public ProjectExplorer::ProjectNode
|
class CMakeListsNode : public ProjectExplorer::ProjectNode
|
||||||
@@ -49,7 +46,6 @@ public:
|
|||||||
CMakeListsNode(const Utils::FileName &cmakeListPath);
|
CMakeListsNode(const Utils::FileName &cmakeListPath);
|
||||||
|
|
||||||
bool showInSimpleTree() const final;
|
bool showInSimpleTree() const final;
|
||||||
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CMakeProjectNode : public ProjectExplorer::ProjectNode
|
class CMakeProjectNode : public ProjectExplorer::ProjectNode
|
||||||
@@ -59,7 +55,6 @@ public:
|
|||||||
|
|
||||||
bool showInSimpleTree() const final;
|
bool showInSimpleTree() const final;
|
||||||
QString tooltip() const final;
|
QString tooltip() const final;
|
||||||
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CMakeTargetNode : public ProjectExplorer::ProjectNode
|
class CMakeTargetNode : public ProjectExplorer::ProjectNode
|
||||||
@@ -71,7 +66,6 @@ public:
|
|||||||
|
|
||||||
bool showInSimpleTree() const final;
|
bool showInSimpleTree() const final;
|
||||||
QString tooltip() const final;
|
QString tooltip() const final;
|
||||||
QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_tooltip;
|
QString m_tooltip;
|
||||||
|
|||||||
@@ -414,14 +414,16 @@ void ServerModeReader::extractConfigurationData(const QVariantMap &data)
|
|||||||
{
|
{
|
||||||
const QString name = data.value(NAME_KEY).toString();
|
const QString name = data.value(NAME_KEY).toString();
|
||||||
Q_UNUSED(name);
|
Q_UNUSED(name);
|
||||||
|
QSet<QString> knownTargets; // To filter duplicate target names:-/
|
||||||
const QVariantList projects = data.value("projects").toList();
|
const QVariantList projects = data.value("projects").toList();
|
||||||
for (const QVariant &p : projects) {
|
for (const QVariant &p : projects) {
|
||||||
const QVariantMap pData = p.toMap();
|
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;
|
auto project = new Project;
|
||||||
project->name = data.value(NAME_KEY).toString();
|
project->name = data.value(NAME_KEY).toString();
|
||||||
@@ -430,16 +432,28 @@ ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMa
|
|||||||
const QVariantList targets = data.value("targets").toList();
|
const QVariantList targets = data.value("targets").toList();
|
||||||
for (const QVariant &t : targets) {
|
for (const QVariant &t : targets) {
|
||||||
const QVariantMap tData = t.toMap();
|
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;
|
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;
|
auto target = new Target;
|
||||||
target->project = p;
|
target->project = p;
|
||||||
target->name = data.value(NAME_KEY).toString();
|
target->name = targetName;
|
||||||
target->sourceDirectory = FileName::fromString(data.value(SOURCE_DIRECTORY_KEY).toString());
|
target->sourceDirectory = FileName::fromString(data.value(SOURCE_DIRECTORY_KEY).toString());
|
||||||
target->buildDirectory = FileName::fromString(data.value("buildDirectory").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);
|
auto headerNode = new VirtualFolderNode(root->filePath(), Node::DefaultPriority - 5);
|
||||||
headerNode->setDisplayName(tr("<Headers>"));
|
headerNode->setDisplayName(tr("<Headers>"));
|
||||||
root->addNode(headerNode);
|
|
||||||
|
|
||||||
// knownHeaders are already listed in their targets:
|
// knownHeaders are already listed in their targets:
|
||||||
QSet<Utils::FileName> seenHeaders = Utils::transform<QSet>(knownHeaders, &FileNode::filePath);
|
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);
|
headerNode->addNestedNode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (headerNode->nodes().isEmpty())
|
||||||
|
delete headerNode; // No Headers, do not show this Folder.
|
||||||
|
else
|
||||||
|
root->addNode(headerNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user