Merge remote-tracking branch 'origin/master' into 4.2
Change-Id: Ibc8b4de34e6790854d23b829c96a1a128ea0e6a4
@@ -18,6 +18,7 @@ imagedirs = $SRCDIR/images \
|
||||
$SRCDIR/../src/plugins/projectexplorer/images \
|
||||
$SRCDIR/../src/plugins/qmldesigner/components/formeditor \
|
||||
$SRCDIR/../src/plugins/qmldesigner/components/navigator \
|
||||
$SRCDIR/../src/plugins/scxmleditor/common/images \
|
||||
$SRCDIR/../src/plugins/texteditor/images
|
||||
|
||||
outputdir = $OUTDIR
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 72 KiB |
BIN
doc/images/qtcreator-scxml-editor.png
Normal file
After Width: | Height: | Size: 35 KiB |
@@ -137,8 +137,8 @@
|
||||
|
||||
\list 1
|
||||
|
||||
\li Select \uicontrol Tools > \uicontrol Options > \uicontrol Android to add paths to the
|
||||
Android NDK and SDK.
|
||||
\li Select \uicontrol Tools > \uicontrol Options > \uicontrol Devices >
|
||||
\uicontrol Android to add paths to the Android NDK and SDK.
|
||||
|
||||
You can use the \inlineimage download.png
|
||||
(\uicontrol Download) buttons to go to the sites where you can download
|
||||
@@ -219,7 +219,8 @@
|
||||
\section1 Using the Android Emulator
|
||||
|
||||
To run your application on the Android Emulator, you must create Android
|
||||
virtual devices (AVD). Select \uicontrol Tools > \uicontrol Options > \uicontrol Android >
|
||||
virtual devices (AVD). Select \uicontrol Tools > \uicontrol Options >
|
||||
\uicontrol Devices > \uicontrol Android >
|
||||
\uicontrol Add. If you run an application without a device connected to the
|
||||
development PC and without an AVD specified, \QC asks you to add an AVD.
|
||||
|
||||
|
@@ -51,7 +51,7 @@
|
||||
|
||||
\li Restart \QC to be able to use the plugin.
|
||||
|
||||
\li Select \uicontrol Tools > \uicontrol Options >
|
||||
\li Select \uicontrol Tools > \uicontrol Options > \uicontrol Devices >
|
||||
\uicontrol {Bare Metal} > \uicontrol Add > \uicontrol Default,
|
||||
\uicontrol OpenCD, or \uicontrol {ST-LINK Utility} to specify
|
||||
connections to GDB servers or hardware debuggers:
|
||||
@@ -113,10 +113,9 @@
|
||||
\li Open a project for an application you want to develop for the
|
||||
device.
|
||||
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run} >
|
||||
\uicontrol {Add Kit}, and then select the kit for building and
|
||||
running the application on the bare metal device specified in the
|
||||
kit.
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run}, and then
|
||||
select the kit for building and running the application on the bare
|
||||
metal device specified in the kit.
|
||||
|
||||
\image creator-baremetal-kit-for-project.png "Adding a bare metal kit for a project"
|
||||
|
||||
|
@@ -902,100 +902,125 @@
|
||||
structures, but it does usually not give enough insight into more complex
|
||||
structures, such as \c QObjects or associative containers. These items are
|
||||
internally represented by a complex arrangement of pointers, often highly
|
||||
optimized, with part of the data not directly accessible through either
|
||||
sub-structures or pointers.
|
||||
|
||||
To give the user simple access also to these items, \QC employs so-called
|
||||
\e{debugging helpers}. Debugging helpers come in two varieties, a compiled
|
||||
one, for use with the CDB backend, and a set of Python scripts for use with
|
||||
the GDB and LLDB backends.
|
||||
optimized, with part of the data not directly accessible through neither
|
||||
sub-structures nor pointers.
|
||||
|
||||
To give the user simple access also to these items, \QC employs Python
|
||||
scripts that are called \e {debugging helpers}.
|
||||
Debugging helpers are always automatically used. To force a plain C-like
|
||||
display of structures, select \uicontrol Tools > \uicontrol Options >
|
||||
\uicontrol Debugger > \uicontrol {Locals & Expressions}, and then deselect
|
||||
the \uicontrol {Use Debugging Helper} check box. For GDB and LLDB this will
|
||||
still use the Python scripts, but generate more basic output. To force plain
|
||||
display for a single object or for all objects of a given type, select the
|
||||
the \uicontrol {Use Debugging Helper} check box. This will still use the
|
||||
Python scripts, but generate more basic output. To force the plain display
|
||||
for a single object or for all objects of a given type, select the
|
||||
corresponding option from the context menu.
|
||||
|
||||
\QC ships with debugging helpers for more than 130 of the most popular Qt
|
||||
classes, Standard C++ containers and smart pointers, covering the usual
|
||||
\QC ships with debugging helpers for more than 200 of the most popular Qt
|
||||
classes, standard C++ containers, and smart pointers, covering the usual
|
||||
needs of a C++ application developer out-of-the-box.
|
||||
|
||||
\section1 Extending GDB and LLDB Debugging Helpers
|
||||
\section1 Extending Debugging Helpers
|
||||
|
||||
When using either GDB or LLDB as the debugging backend, \QC uses Python
|
||||
scripts to display information in the \uicontrol {Locals and Expressions}
|
||||
\QC uses Python scripts to translate raw memory contents and type information
|
||||
data from native debugger backends (GDB, LLDB, and CDB are currently supported)
|
||||
into the form presented to the user in the \uicontrol {Locals and Expressions}
|
||||
view.
|
||||
|
||||
You can easily extend these scripts to cover your own types, using the same
|
||||
code for both the GDB and the LLDB backend. No compilation is required, just
|
||||
adding a few lines of Python. The scripts can address multiple versions of
|
||||
Qt, or of your own library, at the same time.
|
||||
Unlike GDB's
|
||||
\l{https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html#Pretty-Printing}
|
||||
{pretty printers} and LLDB's \l{http://lldb.llvm.org/varformats.html}
|
||||
{data formatters}, Qt Creator's debugging helpers are independent of the
|
||||
native debugging backend. That is, the same code can be used with GDB on
|
||||
Linux, LLDB on macOS, and CDB on Windows, or any other platform on which at
|
||||
least one of the three supported backends is available.
|
||||
|
||||
To extend the shipped Python based debugging helpers for custom types, add
|
||||
debugging helper implementations to the GDB startup file \c{~/.gdbinit}, or
|
||||
specify them directly in the \uicontrol {Additional Startup Commands} in
|
||||
\uicontrol Tools > \uicontrol Options > \uicontrol Debugger >
|
||||
\uicontrol GDB.
|
||||
To add debugging helpers for your own types, no compilation is required,
|
||||
just adding a few lines of Python. The scripts can address multiple versions
|
||||
of Qt, or of your own library, at the same time.
|
||||
|
||||
To add debugging helpers for custom types, add debugging helper
|
||||
implementations to the startup file of the native debuggers (for example,
|
||||
\c{~/.gdbinit} or \c{~/.lldbinit}) or specify them directly in the
|
||||
\uicontrol {Additional Startup Commands} in \uicontrol Tools >
|
||||
\uicontrol Options > \uicontrol Debugger > \uicontrol GDB.
|
||||
|
||||
\section2 Debugging Helper Overview
|
||||
|
||||
The implementation of a debugging helper typically consists of a single
|
||||
Python function, which needs to be named \c{qdump__NS__Foo}, where
|
||||
\c{NS::Foo} is the class or class template to be examined. Note that the
|
||||
\c{::} scope resolution operator is replaced by double underscores \c{__}.
|
||||
\c{::} scope resolution operator is replaced by double underscores: \c{__}.
|
||||
Nested namespaces are possible.
|
||||
|
||||
The debugger plugin calls this function whenever you want to display an
|
||||
\QC's debugger plugin calls this function whenever you want to display an
|
||||
object of this type. The function is passed the following parameters:
|
||||
|
||||
\list
|
||||
|
||||
\li \c d of type \c Dumper, an object containing current settings and
|
||||
providing facilities to build up an object representing part of the
|
||||
Locals and Expressions view,
|
||||
\li \c d of type \c Dumper, an object containing the current settings and
|
||||
providing facilities to build up an object representing a part of
|
||||
the \uicontrol {Locals and Expressions} view.
|
||||
|
||||
\li \c value of type \c Value, wrapping either a
|
||||
\l{https://sourceware.org/gdb/onlinedocs/gdb/Values-From-Inferior.html}{gdb.Value}
|
||||
or an
|
||||
\l{http://lldb.llvm.org/cpp_reference/html/classlldb_1_1SBValue.html}{lldb.SBValue}.
|
||||
\li \c value of type \c Value, wrapping either a
|
||||
\l{https://sourceware.org/gdb/onlinedocs/gdb/Values-From-Inferior.html}
|
||||
{gdb.Value} or an
|
||||
\l{http://lldb.llvm.org/cpp_reference/html/classlldb_1_1SBValue.html}
|
||||
{lldb.SBValue}.
|
||||
|
||||
\endlist
|
||||
|
||||
The \c{qdump__*} function has to feed the Dumper object with certain
|
||||
information which is used to build up the object and its children's display
|
||||
information that is used to build up the object and its children's display
|
||||
in the \uicontrol {Locals and Expressions} view.
|
||||
|
||||
Example:
|
||||
|
||||
\code
|
||||
def qdump__QFiniteStack(d, value):
|
||||
alloc = int(value["_alloc"])
|
||||
size = int(value["_size"])
|
||||
alloc = value["_alloc"].integer()
|
||||
size = value["_size"].integer()
|
||||
d.putItemCount(size)
|
||||
d.putNumChild(size)
|
||||
if d.isExpanded():
|
||||
innerType = d.templateArgument(value.type, 0)
|
||||
d.putArrayData(innerType, value["_array"], size)
|
||||
d.putArrayData(value.type[0], value["_array"], size)
|
||||
\endcode
|
||||
|
||||
\note To create dumper functions usable with both LLDB and GDB backends,
|
||||
avoid direct access to the \c gdb.* and \c lldb.* namespaces and use
|
||||
avoid direct access to the \c gdb.* and \c lldb.* namespaces and use the
|
||||
functions of the \c Dumper class instead.
|
||||
|
||||
\section2 Dumper Class
|
||||
Debugging helpers can be set up to be called whenever a type name matches
|
||||
a regular expression. To do so, the debugging helper's function name must
|
||||
begin with \c{qdump__} (with two underscore characters). In addition,
|
||||
the function needs to have a third parameter called \c regex with a default
|
||||
value that specifies the regular expression that the type name should match.
|
||||
|
||||
For example, the Nim 0.12 compiler assigns artificial names, such as \c{TY1}
|
||||
and \c{TY2}, to all generic sequences it compiles. To visualize these in
|
||||
\QC, the following debugging helper may be used:
|
||||
|
||||
\code
|
||||
def qdump__NimGenericSequence__(d, value, regex = "^TY.*$"):
|
||||
size = value["Sup"]["len"]
|
||||
base = value["data"].dereference()
|
||||
typeobj = base.dereference().type
|
||||
d.putArrayData(base, size, typeobj)
|
||||
\endcode
|
||||
|
||||
\section2 Debugging Helper Implementation
|
||||
|
||||
A debugging helper creates a description of the displayed data item
|
||||
in a format that is similar to GDB/MI and JSON.
|
||||
|
||||
For each line in the \uicontrol {Locals and Expressions} view, a string like
|
||||
the following needs to be created and channeled to the debugger plugin.
|
||||
|
||||
\code
|
||||
{iname='some internal name', # optional
|
||||
addr='object address in memory', # optional
|
||||
{ iname='some internal name', # optional
|
||||
address='object address in memory', # optional
|
||||
name='contents of the name column', # optional
|
||||
value='contents of the value column',
|
||||
type='contents of the type column',
|
||||
numchild='number of children', # zero/nonzero is sufficient
|
||||
childtype='default type of children', # optional
|
||||
childnumchild='default number of grandchildren', # optional
|
||||
children=[ # only needed if item is expanded in view
|
||||
{iname='internal name of first child',
|
||||
},
|
||||
@@ -1006,99 +1031,42 @@
|
||||
\endcode
|
||||
|
||||
The value of the \c iname field is the internal name of the object,
|
||||
constituting a dot-separated list of identifiers, corresponding to the
|
||||
which consists of a dot-separated list of identifiers, corresponding to the
|
||||
position of the object's representation in the view. If it is not present,
|
||||
is it generated by concatenating the parent object's iname, a dot, and a
|
||||
it is generated by concatenating the parent object's \c iname, a dot, and a
|
||||
sequential number.
|
||||
|
||||
The value of the \c name field is displayed in the \uicontrol Name column
|
||||
of the view. If it is not specified, a simple number in brackets is used
|
||||
instead.
|
||||
|
||||
While in theory you can build up the entire string above manually, it is
|
||||
easier to employ the Dumper Python class for that purpose. The Dumper Python
|
||||
class contains a complete framework to take care of the \c iname and \c addr
|
||||
fields, to handle children of simple types, references, pointers, enums,
|
||||
known and unknown structs as well as some convenience functions to handle
|
||||
As the format is not guaranteed to be stable, it is strongly recommended
|
||||
not to generate the wire format directly, but to use the abstraction
|
||||
layer provided by the Python Dumper classes, specifically the \c{Dumper}
|
||||
class itself, and the \c{Dumper:Value} and \c{Dumper:Type} abstractions.
|
||||
|
||||
These provide a complete framework to take care of the \c iname and \c addr
|
||||
fields, to handle children of simple types, references, pointers, enums, and
|
||||
known and unknown structs, as well as some convenience functions to handle
|
||||
common situations.
|
||||
|
||||
\section3 Dumper Class
|
||||
|
||||
The \c Dumper class contains generic bookkeeping, low-level, and convenience
|
||||
functions.
|
||||
|
||||
The member functions of the \c Dumper class are the following:
|
||||
|
||||
\list
|
||||
|
||||
\li \c{__init__(self)} - Initializes the output to an empty string and
|
||||
empties the child stack. This should not be used in user code.
|
||||
|
||||
\li \c{put(self, value)} - Low level function to directly append to the
|
||||
output string. That is also the fastest way to append output.
|
||||
|
||||
\li \c{putField(self, name, value)} - Appends a \c{name='value'} field.
|
||||
|
||||
\li \c{childRange(self)} - Returns the range of children specified in
|
||||
the current \c Children scope.
|
||||
|
||||
\li \c{putItemCount(self, count)} - Appends a field
|
||||
\c {value='<%d items'} to the output.
|
||||
|
||||
\li \c{putName(self, name)} - Appends a \c {name=''} field.
|
||||
|
||||
\li \c{putType(self, type, priority=0)} - Appends a field \c {type=''}
|
||||
unless the \a type coincides with the parent's default child type or
|
||||
\c putType was already called for the current item with a higher
|
||||
value of \c priority.
|
||||
|
||||
\li \c{putBetterType(self, type)} - Overrides the last recorded
|
||||
\c type.
|
||||
|
||||
\li \c{putNumChild(self, numchild)} - Appends a field \c {numchild=''}
|
||||
unless the \c numchild coincides with the parent's default child
|
||||
numchild value.
|
||||
|
||||
\li \c{putValue(self, value, encoding = None)} - Append a file
|
||||
\c {value=''}, optionally followed by a field \c {valueencoding=''}.
|
||||
The \c value needs to be convertible to a string entirely consisting
|
||||
of alphanumerical values. The \c encoding parameter can be used to
|
||||
specify the encoding in case the real value had to be encoded in
|
||||
some way to meet the alphanumerical-only requirement. Currently the
|
||||
following encodings are supported:
|
||||
|
||||
\list
|
||||
|
||||
\li 0: unencoded 8 bit data, interpreted as Latin1.
|
||||
|
||||
\li 1: base64 encoded 8 bit data, used for QByteArray, double quotes
|
||||
are added.
|
||||
|
||||
\li 2: base64 encoded 16 bit data, used for QString, double quotes
|
||||
are added.
|
||||
|
||||
\li 3: base64 encoded 32 bit data, double quotes are added.
|
||||
|
||||
\li 4: base64 encoded 16 bit data, without quotes (see 2)
|
||||
|
||||
\li 5: base64 encoded 8 bit data, without quotes (see 1)
|
||||
|
||||
\li 6: %02x encoded 8 bit data (as with \c QByteArray::toHex),
|
||||
double quotes are added.
|
||||
|
||||
\li 7: %04x encoded 16 bit data (as with \c QByteArray::toHex),
|
||||
double quotes are added.
|
||||
\endlist
|
||||
|
||||
\li \c{putStringValue(self, value)} - Encodes a QString and calls
|
||||
\c putValue with the correct \c encoding setting.
|
||||
|
||||
\li \c{putByteArrayValue(self, value)} - Encodes a QByteArray and calls
|
||||
\c putValue with the correct \c encoding setting.
|
||||
|
||||
\li \c{isExpanded()} - Checks whether the current item is expanded in
|
||||
the view.
|
||||
\li \c{putItem(self, value)} - The \e {master function} that handles
|
||||
basic types, references, pointers, and enums directly, iterates over
|
||||
base classes and class members of compound types, and calls
|
||||
\c qdump__* functions when appropriate.
|
||||
|
||||
\li \c{putIntItem(self, name, value)} - Equivalent to:
|
||||
\code
|
||||
with SubItem(self, name):
|
||||
self.putValue(value)
|
||||
self.putAddress(value.address)
|
||||
self.putType("int")
|
||||
self.putNumChild(0)
|
||||
\endcode
|
||||
@@ -1111,21 +1079,32 @@
|
||||
self.putNumChild(0)
|
||||
\endcode
|
||||
|
||||
\li \c{putCallItem(self, name, value, func, *args)} - Uses GDB to call
|
||||
the function \c func on the value specified by \a {value} and output
|
||||
the resulting item. Use \c{putCallItem} only if there is no other
|
||||
way to access the data. Calls cannot be executed when inspecting a
|
||||
core file, they are expensive to execute and have the potential to
|
||||
change the state of the debugged program.
|
||||
\li \c{putCallItem(self, name, value, func, *args)} - Uses the native
|
||||
debugger backend to place the function call \c func on the value
|
||||
specified by \a {value} and output the resulting item.
|
||||
|
||||
\li \c{putArrayData(self, type, address, size)} - Creates \c size
|
||||
children of type \c type of an array-like object located at
|
||||
\c address.
|
||||
Native calls are extremely powerful and can leverage existing
|
||||
debugging or logging facilities in the debugged process, for
|
||||
example. However, they should only be used in a controlled
|
||||
environment, and only if there is no other way to access the data,
|
||||
for the following reasons:
|
||||
|
||||
\li \c{putItem(self, value)} - The "master function", handling basic
|
||||
types, references, pointers and enums directly, iterates over base
|
||||
classes and class members of compound types and calls \c qdump__*
|
||||
functions whenever appropriate.
|
||||
\list
|
||||
|
||||
\li Direct execution of code is dangerous. It runs native code
|
||||
with the privileges of the debugged process, with the
|
||||
potential to not only corrupt the debugged process, but also
|
||||
to access the disk and network.
|
||||
|
||||
\li Calls cannot be executed when inspecting a core file.
|
||||
|
||||
\li Calls are expensive to set up and execute in the debugger.
|
||||
|
||||
\endlist
|
||||
|
||||
\li \c{putArrayData(self, type, address, itemCount)} - Creates the
|
||||
number of children specified by \c itemCount of the type \c type of
|
||||
an array-like object located at \c address.
|
||||
|
||||
\li \c{putSubItem(self, component, value)} - Equivalent to:
|
||||
\code
|
||||
@@ -1141,9 +1120,247 @@
|
||||
d.put('value="<invalid>",type="<unknown>",numchild="0",')
|
||||
\endcode
|
||||
|
||||
\li \c{put(self, value)} - A low-level function to directly append to
|
||||
the output string. That is also the fastest way to append output.
|
||||
|
||||
\li \c{putField(self, name, value)} - Appends a \c{name='value'} field.
|
||||
|
||||
\li \c{childRange(self)} - Returns the range of children specified in
|
||||
the current \c Children scope.
|
||||
|
||||
\li \c{putItemCount(self, count)} - Appends the field
|
||||
\c {value='<%d items>'} to the output.
|
||||
|
||||
\li \c{putName(self, name)} - Appends the \c {name=''} field.
|
||||
|
||||
\li \c{putType(self, type, priority=0)} - Appends the field \c {type=''},
|
||||
unless the \a type coincides with the parent's default child type or
|
||||
\c putType was already called for the current item with a higher
|
||||
value of \c priority.
|
||||
|
||||
\li \c{putBetterType(self, type)} - Overrides the last recorded \c type.
|
||||
|
||||
\li \c{putNumChild(self, numchild)} - Announces the existence
|
||||
(\c numchild > 0) or non-existence of child items for the current
|
||||
value. If \c putNumChild is not explicitly called, the
|
||||
existence of child items is implied.
|
||||
|
||||
\li \c{putValue(self, value, encoding = None)} - Appends the file
|
||||
\c {value=''}, optionally followed by the field \c {valueencoding=''}.
|
||||
The \c value needs to be convertible to a string entirely consisting
|
||||
of alphanumerical values. The \c encoding parameter can be used to
|
||||
specify the encoding in case the real value had to be encoded in
|
||||
some way to meet the alphanumerical-only requirement. The parameter
|
||||
\c{encoding} is either a string of the form \c codec:itemsize:quote
|
||||
where \c{codec} is any of \c{latin1}, \c{utf8}, \c{utf16}, \c{ucs4},
|
||||
\c{int}, or \c{float}. \c{itemsize} gives the size of the basic
|
||||
component of the object if it is not implied by \c codec and
|
||||
\c quote specifies whether or not the value should be surrounded by
|
||||
quotes in the display.
|
||||
|
||||
Example:
|
||||
|
||||
\code
|
||||
# Safe transport of quirky data. Put quotes around the result.
|
||||
d.putValue(d.hexencode("ABC\"DEF"), "utf8:1:1")
|
||||
\endcode
|
||||
|
||||
\li \c{putStringValue(self, value)} - Encodes a QString and calls
|
||||
\c putValue with the correct \c encoding setting.
|
||||
|
||||
\li \c{putByteArrayValue(self, value)} - Encodes a QByteArray and calls
|
||||
\c putValue with the correct \c encoding setting.
|
||||
|
||||
\li \c{isExpanded(self)} - Checks whether the current item is expanded in
|
||||
the view.
|
||||
|
||||
\li \c{createType(self, pattern, size = None)} - Creates a \c{Dumper.Type}
|
||||
object. The exact operation depends on \c pattern.
|
||||
|
||||
\list
|
||||
|
||||
\li If \c pattern matches the name of a well-known type, a
|
||||
\c{Dumper.Type} object describing this type is returned.
|
||||
|
||||
\li If \c pattern is the name of a type known to the native backend,
|
||||
the returned type describes the native type.
|
||||
|
||||
\li Otherwise, \c pattern is used to construct a type description by
|
||||
interpreting a sequence of items describing the field of a
|
||||
structure as follows. Field descriptions consist of one or
|
||||
more characters as follows:
|
||||
|
||||
\list
|
||||
|
||||
\li \c q - Signed 8-byte integral value
|
||||
\li \c Q - Unsigned 8-byte integral value
|
||||
\li \c i - Signed 4-byte integral value
|
||||
\li \c I - Unsigned 4-byte integral value
|
||||
\li \c h - Signed 2-byte integral value
|
||||
\li \c H - Unsigned 2-byte integral value
|
||||
\li \c b - Signed 1-byte integral value
|
||||
\li \c B - Unsigned 1-byte integral value
|
||||
\li \c d - 8-byte IEEE 754 floating point value
|
||||
\li \c f - 4-byte IEEE 754 floating point value
|
||||
\li \c p - A pointer, that is, an unsigned integral value of
|
||||
suitable size according to the target architecture
|
||||
\li \c @ - Suitable padding. The size is determined by the
|
||||
preceding and following field and the target architecture
|
||||
\li \c <n>s - A blob of <n> bytes, with implied alignment of 1
|
||||
\li \c {<typename>} - A blob of suitable size and suitable
|
||||
alignment determined by a \c{Dumper.Type} with the name
|
||||
\c typename
|
||||
|
||||
\endlist
|
||||
|
||||
\endlist
|
||||
|
||||
\endlist
|
||||
|
||||
\section2 Children and SubItem Class
|
||||
\section3 Dumper.Type Class
|
||||
|
||||
The \c{Dumper.Type} class describes the type of a piece of data, typically
|
||||
a C++ class or struct, a pointer to a struct, or a primitive type, such as
|
||||
an integral or floating point type.
|
||||
|
||||
Type objects, that is, instances of the \c{Dumper.Type} class, can be
|
||||
created by native debugger backends, usually by evaluating Debug Information
|
||||
built into or shipped alongside the debugged binary, or created on-the-fly
|
||||
by the debugging helper.
|
||||
|
||||
\QC uses the possibility to provide type information on-the-fly for most Qt
|
||||
classes, obliterating the need to use \e Debug builds of Qt for the purpose
|
||||
of object introspection.
|
||||
|
||||
The member functions of the \c{Dumper.Type} class are the following:
|
||||
|
||||
\list
|
||||
|
||||
\li \c{name} - The name of this type as a string, or \c None if the type
|
||||
is anonymous.
|
||||
|
||||
\li \c{size(self)} - Returns the size of an object of this type in
|
||||
bytes.
|
||||
|
||||
\li \c{bitsize(self)} - Returns the size of an object of this type in
|
||||
bits.
|
||||
|
||||
\li \c{(alignment(self)} - Returns the required alignment for objects of
|
||||
this type in bytes.
|
||||
|
||||
\li \c{deference(self)} - Returns the dereferences type for pointer
|
||||
type, \c None otherwise.
|
||||
|
||||
\li \c{pointer(self)} - Returns a pointer type that can be dereferenced
|
||||
to this type.
|
||||
|
||||
\li \c{target(self)} - A convenience function that returns the item type
|
||||
for array types and the dereferenced type for pointers and
|
||||
references.
|
||||
|
||||
\li \c{stripTypedefs(self)} - Returns the underlying type if this type
|
||||
is an alias.
|
||||
|
||||
\li \c{templateArgument(self, position, numeric = False)} - Returns the
|
||||
template parameter located at \c{position} if this is a templated
|
||||
type. If \c numeric is \c True, returns the parameter as an integral
|
||||
value.
|
||||
|
||||
\li \c{fields(self)} - Returns a list of \c{Dumper:Fields} describing
|
||||
the base classes and data members of this type.
|
||||
|
||||
\endlist
|
||||
|
||||
|
||||
\section3 Dumper.Field Class
|
||||
|
||||
The \c{Dumper.Field} class describes a base class or a data member of a type
|
||||
object.
|
||||
|
||||
The member function and properties of the \c{Dumper.Field} class are the
|
||||
following:
|
||||
|
||||
\list
|
||||
|
||||
\li \c{isBaseClass} - Distinguishes between base classes and data
|
||||
members.
|
||||
|
||||
\li \c{fieldType(self)} - Returns the type of this base class or data
|
||||
member.
|
||||
|
||||
\li \c{parentType(self)} - Returns the owning type.
|
||||
|
||||
\li \c{bitsize(self)} - Returns the size of this field in bits.
|
||||
|
||||
\li \c{bitpos(self)} - Returns the offset of this field in the owning
|
||||
type in bits.
|
||||
|
||||
\endlist
|
||||
|
||||
|
||||
\section3 Dumper.Value Class
|
||||
|
||||
The \c{Dumper.Value} class describes a piece of data, such as instances of
|
||||
C++ classes or primitive data types. It can also be used to describe
|
||||
artificial items that have no direct representation in memory, such as
|
||||
file contents, non-contiguous objects, or collections.
|
||||
|
||||
A \c{Dumper.Value} has always an associated \c{Dumper.Type}. The two
|
||||
main representations of the value's actual data are:
|
||||
|
||||
\list
|
||||
|
||||
\li Python object following the Python buffer protocol, such as a Python
|
||||
\c memoryview, or a \c bytes object. The \c size() should match the
|
||||
size of this value's type.
|
||||
|
||||
\li An integral value representing a pointer to the begin of the object
|
||||
in the current address space. The size of the object is given by its
|
||||
type's \c{size()}.
|
||||
|
||||
\endlist
|
||||
|
||||
Knowledge of the internal representation of a \c{Dumper.Value} is typically
|
||||
not required when creating a debugger helper for it.
|
||||
|
||||
The member function and properties of the \c{Dumper.Value} class are the
|
||||
following:
|
||||
|
||||
\list
|
||||
|
||||
\li \c{integer(self)} - Returns an interpretation of this value as a
|
||||
signed integral value of a suitable size.
|
||||
|
||||
\li \c{pointer(self)} - Returns an interpretation of this value as a
|
||||
pointer in the current address space.
|
||||
|
||||
\li \c{members(self)} - Returns a list of \c{Dumper.Value} objects
|
||||
representing the base objects and data members of this value.
|
||||
|
||||
\li \c{dereference(self)} - For values describing pointers, returns the
|
||||
dereferenced value, and \c None otherwise.
|
||||
|
||||
\li \c{cast(self, type)} - Returns a value that has the same data as
|
||||
this value, but the type \c type.
|
||||
|
||||
\li \c{address(self)} - Returns the address of this value if it consists
|
||||
of a contiguous region in the current address space, and \c None
|
||||
otherwise.
|
||||
|
||||
\li \c{data(self)} - Returns the data of this value as a Python \c bytes
|
||||
object.
|
||||
|
||||
\li \c{split(self, pattern)} - Returns a list of values created
|
||||
according to \c pattern from this value's data. Acceptable patterns
|
||||
are the same as for \c{Dumper.createType}.
|
||||
|
||||
\li \c{dynamicTypeName(self)} - Tries to retrieve the name of the
|
||||
dynamic type of this value if this is a base class object. Returns
|
||||
\c None if that is not possible.
|
||||
|
||||
\endlist
|
||||
|
||||
\section3 Children and SubItem Class
|
||||
|
||||
The attempt to create child items might lead to errors if data is
|
||||
uninitialized or corrupted. To gracefully recover in such situations, use
|
||||
@@ -1164,7 +1381,6 @@
|
||||
|
||||
Example:
|
||||
\code
|
||||
d.putNumChild(2) # Annouce children to make the item expandable in the view.
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
with SubItem(d):
|
||||
|
@@ -92,6 +92,15 @@
|
||||
Modeling Language (UML) style models with structured diagrams and
|
||||
store them in XML format.
|
||||
|
||||
\li \l{Editing State Charts}
|
||||
|
||||
You can use \QC to create applications that embed state machines. A
|
||||
project wizard creates \l{https://www.w3.org/TR/scxml/}
|
||||
{State Chart XML (SCXML)} files with boilerplate code that you can
|
||||
edit using an experimental SCXML editor. You can use the classes in
|
||||
the Qt SCXML module to embed state machines created from the files
|
||||
in Qt applications.
|
||||
|
||||
\endlist
|
||||
|
||||
*/
|
||||
|
@@ -33,7 +33,7 @@
|
||||
\contentspage {Qt Creator Manual}
|
||||
\previouspage creator-mime-types.html
|
||||
\page creator-modeling.html
|
||||
\nextpage creator-building-running.html
|
||||
\nextpage creator-scxml.html
|
||||
|
||||
\title Modeling
|
||||
|
||||
|
297
doc/src/editors/creator-scxml.qdoc
Normal file
@@ -0,0 +1,297 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the Qt Creator documentation.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** 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 Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** 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-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
// **********************************************************************
|
||||
// NOTE: the sections are not ordered by their logical order to avoid
|
||||
// reshuffling the file each time the index order changes (i.e., often).
|
||||
// Run the fixnavi.pl script to adjust the links to the index order.
|
||||
// **********************************************************************
|
||||
|
||||
/*!
|
||||
\contentspage {Qt Creator Manual}
|
||||
\previouspage creator-modeling.html
|
||||
\page creator-scxml.html
|
||||
\nextpage creator-building-running.html
|
||||
|
||||
\title Editing State Charts
|
||||
|
||||
State charts provide a graphical way of modeling how a system reacts to
|
||||
stimuli. This is done by defining the possible \e states that the system can
|
||||
be in, and how the system can move from one state to another (\e transitions
|
||||
between states). A key characteristic of event-driven systems (such as Qt
|
||||
applications) is that behavior often depends not only on the last or current
|
||||
\e event, but also the events that preceded it. With state charts, this
|
||||
information is easy to express.
|
||||
|
||||
\QC provides a project wizard for adding \l{https://www.w3.org/TR/scxml/}
|
||||
{State Chart XML (SCXML)} files with boilerplate code to projects and an
|
||||
experimental SCXML editor for editing the state charts. You can use the
|
||||
SCXML editor to add \e states and \e transitions to the files. You can then
|
||||
use the classes in the Qt SCXML module to embed the state machines created
|
||||
from the files in Qt applications.
|
||||
|
||||
\image qtcreator-scxml-editor.png SXCML Editor
|
||||
|
||||
You can drag and drop states from the \uicontrol {Common States} view (1) to
|
||||
the state editor (2). Select a state in the state editor and use the tool
|
||||
buttons (3) to create a transition (4) and its \e {target state}.
|
||||
|
||||
You can view the state chart structure in the \uicontrol Structure view (5)
|
||||
and specify attributes for the selected state or transition in the
|
||||
\uicontrol Attributes view (6).
|
||||
|
||||
You can use the toolbar buttons (7) to execute functions such as editing,
|
||||
zooming, magnifying, navigating, and panning state charts, as well as
|
||||
taking screenshots and viewing statistics.
|
||||
|
||||
To zoom into and out of the whole state chart in the state editor, select
|
||||
\inlineimage icon-zoom-in.png
|
||||
(\uicontrol {Zoom In}) or \inlineimage icon-zoom-out.png
|
||||
(\uicontrol {Zoom Out}) or press \key Ctrl and use the mouse wheel. To make
|
||||
the whole state chart visible in the state editor at a time, select
|
||||
\inlineimage icon-fit-screen.png
|
||||
(\uicontrol {Fit to View}).
|
||||
|
||||
To view a particular part of a large state chart in the state editor, select
|
||||
\inlineimage navigator.png
|
||||
(\uicontrol {Navigator}) and move the navigator frame on the part you want
|
||||
to view.
|
||||
|
||||
To use the magnifier to zoom into a part of the state chart, select
|
||||
\inlineimage zoom.png
|
||||
(\uicontrol Magnifier). To move the magnifier faster, press down the
|
||||
\key Alt key.
|
||||
|
||||
To pan the state chart, select \inlineimage icon-pan.png
|
||||
(\uicontrol Panning). To increase the pace of panning, press down the
|
||||
\key Shift key.
|
||||
|
||||
To view statistics about the numbers of states and transitions in the state
|
||||
chart, select \inlineimage statistics.png
|
||||
(\uicontrol {View Statistics}).
|
||||
|
||||
To search from the state chart, use the \uicontrol Search pane. The search
|
||||
checks the whole SCXML tree for attributes that match the search criteria.
|
||||
|
||||
To save the currently visible part of the state chart as an image, select
|
||||
\inlineimage screenshot.png
|
||||
(\uicontrol {Save Screenshot}). To save the whole state chart as an image,
|
||||
select \inlineimage icon-export-canvas.png
|
||||
(\uicontrol {Export Canvas to Image}).
|
||||
|
||||
\section1 Creating State Charts
|
||||
|
||||
To create a state chart:
|
||||
|
||||
\list 1
|
||||
|
||||
\li Select \uicontrol Help > \uicontrol {About Plugins} >
|
||||
\uicontrol Modeling > \uicontrol ScxmlEditor and restart \QC to
|
||||
enable the plugin.
|
||||
|
||||
\li Select \uicontrol File > \uicontrol {New File or Project} >
|
||||
\uicontrol {Files and Classes} > \uicontrol Modeling >
|
||||
\uicontrol {State Chart} > \uicontrol Choose to create an empty
|
||||
state chart and to open it in the SCXML editor.
|
||||
|
||||
\li Drag and drop a state from the \uicontrol {Common States} view to
|
||||
the state editor.
|
||||
|
||||
\li Drag and drop child states to the initial state to create a
|
||||
\e {compound state} or use the tool buttons to create a transition
|
||||
from the selected state and its target state.
|
||||
|
||||
\li Select a state to edit its attributes in the \uicontrol Attributes
|
||||
view.
|
||||
|
||||
\li Select the transition line to add edge points to it.
|
||||
|
||||
\li To raise or send events, for example, use the context menu commands
|
||||
to add executable content to the \c <onentry> and \c <onexit>
|
||||
elements of states or to transitions.
|
||||
|
||||
\endlist
|
||||
|
||||
The following sections describe how to manage states, transitions, and
|
||||
executable content.
|
||||
|
||||
\section1 Managing States
|
||||
|
||||
When the state machine enters a state in response to an event, the state
|
||||
that it entered becomes the \e {active state}.
|
||||
|
||||
State charts are hierarchical, and therefore states can be nested inside
|
||||
other states, to create compound states.
|
||||
|
||||
In addition to basic states, you can create the following types of states:
|
||||
|
||||
\list
|
||||
|
||||
\li \e Initial state is the state the state machine enters when it
|
||||
starts.
|
||||
|
||||
\li \e {Parallel state} contains child states that execute in parallel
|
||||
and are all active simultaneously. Events are processed
|
||||
independently by each child state and may trigger different
|
||||
transitions for each child.
|
||||
|
||||
\li \e {Final state} enables a state machine to finish. When the state
|
||||
machine enters a top-level final state, it emits the finished signal
|
||||
and halts. You can create final states in compound states to hide
|
||||
the internal details of a compound state. The outside world can only
|
||||
enter the state and get a notification when the state has finished.
|
||||
A parallel state finishes when all its child states reach final
|
||||
states.
|
||||
|
||||
\li \e {History state} is a pseudo-state that represents the child state
|
||||
the parent state was in the last time the parent state was exited.
|
||||
|
||||
Create a history state as a child of the state for which you want to
|
||||
record the current child state. When the state machine detects the
|
||||
presence of such a state at runtime, it automatically records the
|
||||
current (real) child state when the parent state is exited. A
|
||||
transition to the history state is in fact a transition to the child
|
||||
state that the state machine previously saved. The state machine
|
||||
automatically forwards the transition to the real child state.
|
||||
|
||||
\endlist
|
||||
|
||||
You can add new states to the state chart in the following ways:
|
||||
|
||||
\list
|
||||
|
||||
\li Drag and drop states from the \uicontrol {Common States} view to the
|
||||
state editor.
|
||||
|
||||
\li Select a state in the state editor, and then select the
|
||||
\uicontrol State tool button to create a transition and its target
|
||||
state.
|
||||
|
||||
\li Copy and paste states within the SCXML editor or between the SCXML
|
||||
editor and the \uicontrol Edit mode.
|
||||
|
||||
\endlist
|
||||
|
||||
You can drag states on top of other states to create compound states, or
|
||||
you can drag child states out of their parent state. To move child states
|
||||
within their parent, press down the \key Ctrl key while moving them.
|
||||
|
||||
You can use toolbar buttons to align states in the state editor, to adjust
|
||||
their size, and to change the default color scheme. Overlapping states are
|
||||
marked in red color.
|
||||
|
||||
To expand or collapse the state tree structure in the \uicontrol Structure
|
||||
view, double-click a state.
|
||||
|
||||
To view a child state of a nested state in more detail in the state editor,
|
||||
select \uicontrol {Zoom to State}.
|
||||
|
||||
To ensure that the state ids are unique within a compound state machine,
|
||||
select \inlineimage fullnamespace.png
|
||||
(\uicontrol {Toggle Full Namespace}). The name of the parent state is
|
||||
added to the names of the child states, separated by two colons (::).
|
||||
For example:
|
||||
|
||||
\badcode
|
||||
<state id="broken">
|
||||
...
|
||||
<state id="broken::blinking">
|
||||
...
|
||||
</state>
|
||||
<state id="broken::unblinking">
|
||||
...
|
||||
</state>
|
||||
</state>
|
||||
\endcode
|
||||
|
||||
\section1 Managing Transitions
|
||||
|
||||
Transitions define how a state reacts to \e events that are generated either
|
||||
by the state machine or external entities. When events occur, the state
|
||||
machine checks for a matching transition defined in the active state and
|
||||
moves to its target state.
|
||||
|
||||
To create a transition from the selected state to a new state, drag and
|
||||
release the mouse at the location where you want to add the target state.
|
||||
When you draw a transition to the center of another state, it points to the
|
||||
center of the state, but you can also draw a transition to the edge of the
|
||||
state.
|
||||
|
||||
To add edge points to transitions, select a transition line. Only two edge
|
||||
points are permitted for each line, and unnecessary edge points are removed
|
||||
automatically. To remove the selected edge point, select
|
||||
\uicontrol {Remove Point} in the context menu.
|
||||
|
||||
To add new edge points with a mouse click, select the \uicontrol Transition
|
||||
tool button.
|
||||
|
||||
A transition label is automatically center-aligned, but you can drag it to
|
||||
another position.
|
||||
|
||||
To remove the selected transition, select \uicontrol Remove in the context
|
||||
menu.
|
||||
|
||||
\section1 Adding Executable Content
|
||||
|
||||
You can add \e {executable content} to a state chart to enable the state
|
||||
machine to modify its data model and to interact with external entities.
|
||||
|
||||
Use the context menu commands to add executable content to the \c <onentry>
|
||||
and \c <onexit> elements or to transitions:
|
||||
|
||||
\list
|
||||
\li \c <raise> to raise events
|
||||
\li \c <send> to communicate with external entities
|
||||
\li \c <script> to run scripts
|
||||
\li \c <assign> to modify the data model
|
||||
\li \c <cancel> to cancel action execution
|
||||
\li \c <log> to record information in a log
|
||||
\li \c <if> to execute actions conditionally
|
||||
\li \c <foreach> to iterate over the items in a collection and execute
|
||||
an action for each of them
|
||||
\endlist
|
||||
|
||||
During a transition, the state machine executes the content that you specify
|
||||
for the \c <onexit> element in the state it is leaving, then the content in
|
||||
the transition, and then the content for the \c <onentry> element in the
|
||||
state it is entering.
|
||||
|
||||
You can add attributes for the selected executable content in the
|
||||
\uicontrol Attributes view.
|
||||
*/
|
@@ -84,9 +84,8 @@
|
||||
\li Open a project for an application you want to develop for the
|
||||
device.
|
||||
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run} >
|
||||
\uicontrol {Add Kit} to add a kit for building and running
|
||||
applications on iOS.
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run} to select
|
||||
the kit for building and running applications on iOS.
|
||||
|
||||
\image qtcreator-ios-add-kit.png "Build & Run Settings"
|
||||
|
||||
|
@@ -119,8 +119,8 @@
|
||||
\li Open a project for an application you want to develop for the
|
||||
device.
|
||||
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run} > \uicontrol {Add Kit} to
|
||||
add the kit that you specified above.
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run} to enable
|
||||
the kit that you specified above.
|
||||
|
||||
\endlist
|
||||
|
||||
|
@@ -31,7 +31,7 @@
|
||||
|
||||
/*!
|
||||
\contentspage {Qt Creator Manual}
|
||||
\previouspage creator-modeling.html
|
||||
\previouspage creator-scxml.html
|
||||
\page creator-building-running.html
|
||||
\nextpage creator-building-targets.html
|
||||
|
||||
|
@@ -52,30 +52,30 @@
|
||||
|
||||
\image qtcreator-project-kits.png
|
||||
|
||||
To add kits for the project, select \uicontrol {Add Kit}. The list displays kits
|
||||
that are configured in \uicontrol Tools > \uicontrol Options > \uicontrol {Build & Run} >
|
||||
\uicontrol Kits. To add kits to the list, select \uicontrol {Manage Kits}.
|
||||
For more information about adding kits, see \l{Adding Kits}.
|
||||
To enable kits for the project, click them in \uicontrol {Build & Run}. The
|
||||
list displays kits that are configured in \uicontrol Tools >
|
||||
\uicontrol Options > \uicontrol {Build & Run} > \uicontrol Kits. To modify
|
||||
kit configuration or to add kits to the list, select
|
||||
\uicontrol {Manage Kits} in the context menu. For more information about
|
||||
managing kits, see \l{Adding Kits}.
|
||||
|
||||
Each kit consists of a set of values that define one environment, such as a
|
||||
device, compiler, and Qt version. For more information, see
|
||||
\l{Adding Qt Versions}, \l{Adding Compilers}, and \l{Adding Debuggers}.
|
||||
|
||||
To copy the build and run settings for a kit to another kit, select
|
||||
\uicontrol {Copy to Kit} in the kit menu.
|
||||
\uicontrol {Copy Steps from Other Kit} in the context menu.
|
||||
|
||||
To change the kit, while preserving the build and run settings, select
|
||||
\uicontrol {Change Kit}.
|
||||
|
||||
To remove a kit, select \uicontrol {Remove Kit}.
|
||||
To disable a kit for the project, select \uicontrol {Disable Kit for Project}
|
||||
in the context menu.
|
||||
|
||||
\section1 Specifying Settings
|
||||
|
||||
The project pane consists of the following tabs:
|
||||
The \uicontrol Projects mode displays one of the following views:
|
||||
|
||||
\list
|
||||
|
||||
\li Build & Run
|
||||
\li \uicontrol {Build & Run} settings for each configured kit:
|
||||
|
||||
\list
|
||||
|
||||
@@ -86,25 +86,27 @@
|
||||
\endlist
|
||||
|
||||
\note If you have not configured the project for building, the
|
||||
\uicontrol {Build & Run} tab is replaced by the \l{Opening Projects}
|
||||
{Configure Projects} tab.
|
||||
\uicontrol {Build & Run} view is replaced by the
|
||||
\l{Opening Projects}{Configure Projects} view.
|
||||
|
||||
\li \l{Specifying Editor Settings}{Editor}
|
||||
\li \uicontrol {Project Settings} for each project:
|
||||
|
||||
\li \l{Specifying Code Style Settings}{Code Style}
|
||||
\list
|
||||
|
||||
\li \l{Specifying Dependencies}{Dependencies}
|
||||
\li \l{Specifying Editor Settings}{Editor}
|
||||
|
||||
\li \l{Using Clang Static Analyzer}{Clang Static Analyzer}
|
||||
\li \l{Specifying Code Style Settings}{Code Style}
|
||||
|
||||
\li \l{To-Do Entries}{To-Do} (experimental)
|
||||
\li \l{Specifying Dependencies}{Dependencies}
|
||||
|
||||
\li \l{Using Clang Static Analyzer}{Clang Static Analyzer}
|
||||
|
||||
\li \l{To-Do Entries}{To-Do} (experimental)
|
||||
|
||||
\endlist
|
||||
\endlist
|
||||
|
||||
Use the \uicontrol Build and \uicontrol Run buttons to switch between the build and
|
||||
run settings for the active project.
|
||||
|
||||
If you have multiple projects open in \QC, use the tabs at the top of the
|
||||
window to navigate between their settings.
|
||||
If you have multiple projects open in \QC, select the project to configure
|
||||
in the list of projects.
|
||||
|
||||
*/
|
||||
|
@@ -44,4 +44,11 @@
|
||||
Adding a QNX Neutrino device is very similar to \l{Connecting Embedded
|
||||
Linux Devices}, except you need to select \uicontrol {QNX Device} in the
|
||||
\uicontrol {Device Configuration} wizard.
|
||||
|
||||
\section1 Adding Kits for QNX Devices
|
||||
|
||||
To view QNX device settings, select \uicontrol Tools > \uicontrol Options >
|
||||
\uicontrol Devices > \uicontrol QNX. Select the \uicontrol {Generate kits}
|
||||
check box to allow \QC to generate kits for QNX development. For more
|
||||
information about how to create the kits manually, see \l {Adding Kits}.
|
||||
*/
|
||||
|
@@ -84,6 +84,7 @@
|
||||
\li \l{Refactoring}
|
||||
\li \l{Configuring the Editor}
|
||||
\li \l{Modeling}
|
||||
\li \l{Editing State Charts}
|
||||
\endlist
|
||||
\row
|
||||
\li \inlineimage creator_buildingrunning.png
|
||||
@@ -227,6 +228,8 @@
|
||||
\endlist
|
||||
\li \l{Editing MIME Types}
|
||||
\li \l{Modeling}
|
||||
\li \l{Editing State Charts}
|
||||
|
||||
\endlist
|
||||
\li \l{Building and Running}
|
||||
\list
|
||||
|
@@ -60,8 +60,8 @@
|
||||
\li Open a project for an application you want to develop for the
|
||||
device.
|
||||
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run} > \uicontrol {Add Kit} to
|
||||
add a kit for building and running applications on Windows
|
||||
\li Select \uicontrol Projects > \uicontrol {Build & Run} to
|
||||
enable a kit for building and running applications on Windows
|
||||
Runtime devices (the local computer, for now), Windows Phones,
|
||||
or the Windows Phone emulator.
|
||||
|
||||
|
@@ -80,6 +80,8 @@ class Value:
|
||||
dereference() -> Value # Dereference if value is pointer,
|
||||
# remove reference if value is reference.
|
||||
hasChildren() -> bool # Whether this object has subobjects.
|
||||
expand() -> bool # Make sure that children are accessible.
|
||||
nativeDebuggerValue() -> string # Dumper value returned from the debugger
|
||||
|
||||
childFromName(string name) -> Value # (optional)
|
||||
childFromField(Field field) -> Value # (optional)
|
||||
@@ -122,6 +124,7 @@ class Field:
|
||||
parseAndEvaluate(string: expr) -> Value # or None if not possible.
|
||||
lookupType(string: name) -> Type # or None if not possible.
|
||||
listOfLocals() -> [ Value ] # List of items currently in scope.
|
||||
readRawMemory(ULONG64 address, ULONG size) -> bytes # Read a block of data from the virtual address space
|
||||
|
||||
|
||||
|
||||
|
198
share/qtcreator/debugger/cdbbridge.py
Normal file
@@ -0,0 +1,198 @@
|
||||
############################################################################
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import cdbext
|
||||
|
||||
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
|
||||
|
||||
from dumper import *
|
||||
|
||||
class Dumper(DumperBase):
|
||||
def __init__(self):
|
||||
DumperBase.__init__(self)
|
||||
self.outputLock = threading.Lock()
|
||||
self.isCdb = True
|
||||
|
||||
def fromNativeValue(self, nativeValue):
|
||||
val = self.Value(self)
|
||||
val.nativeValue = nativeValue
|
||||
val.type = self.fromNativeType(nativeValue.type())
|
||||
val.lIsInScope = True
|
||||
val.laddress = nativeValue.address()
|
||||
return val
|
||||
|
||||
def fromNativeType(self, nativeType):
|
||||
typeobj = self.Type(self)
|
||||
typeobj.nativeType = nativeType
|
||||
typeobj.name = nativeType.name()
|
||||
typeobj.lbitsize = nativeType.bitsize()
|
||||
typeobj.code = nativeType.code()
|
||||
return typeobj
|
||||
|
||||
def nativeTypeFields(self, nativeType):
|
||||
fields = []
|
||||
for nativeField in nativeType.fields():
|
||||
field = self.Field(self)
|
||||
field.name = nativeField.name()
|
||||
field.parentType = self.fromNativeType(nativeType)
|
||||
field.ltype = self.fromNativeType(nativeField.type())
|
||||
field.lbitsize = nativeField.bitsize()
|
||||
field.lbitpos = nativeField.bitpos()
|
||||
fields.append(field)
|
||||
return fields
|
||||
|
||||
def nativeTypeUnqualified(self, nativeType):
|
||||
return self.fromNativeType(nativeType.unqualified())
|
||||
|
||||
def nativeTypePointer(self, nativeType):
|
||||
return self.fromNativeType(nativeType.target())
|
||||
|
||||
def nativeTypeStripTypedefs(self, typeobj):
|
||||
return self.fromNativeType(nativeType.stripTypedef())
|
||||
|
||||
def nativeTypeFirstBase(self, nativeType):
|
||||
return None
|
||||
|
||||
def nativeTypeEnumDisplay(self, nativeType, intval):
|
||||
# TODO: generate fake value
|
||||
return None
|
||||
|
||||
def enumExpression(self, enumType, enumValue):
|
||||
ns = self.qtNamespace()
|
||||
return ns + "Qt::" + enumType + "(" \
|
||||
+ ns + "Qt::" + enumType + "::" + enumValue + ")"
|
||||
|
||||
def pokeValue(self, typeName, *args):
|
||||
return None
|
||||
|
||||
def parseAndEvaluate(self, exp):
|
||||
return cdbext.parseAndEvaluate(exp)
|
||||
|
||||
def nativeTypeTemplateArgument(self, nativeType, position, numeric = False):
|
||||
return self.fromNativeType(nativeType.templateArgument(position, numeric))
|
||||
|
||||
def nativeTypeDereference(self, nativeType):
|
||||
return self.fromNativeType(nativeType.target())
|
||||
|
||||
def nativeTypeTarget(self, nativeType):
|
||||
return self.fromNativeType(nativeType.target())
|
||||
|
||||
def isWindowsTarget(self):
|
||||
return True
|
||||
|
||||
def isQnxTarget(self):
|
||||
return False
|
||||
|
||||
def isArmArchitecture(self):
|
||||
return False
|
||||
|
||||
def isMsvcTarget(self):
|
||||
return True
|
||||
|
||||
def qtVersionAndNamespace(self):
|
||||
return ('', 0x50700) #FIXME: use a general approach in dumper or qttypes
|
||||
|
||||
def qtNamespace(self):
|
||||
return self.qtVersionAndNamespace()[0]
|
||||
|
||||
def qtVersion(self):
|
||||
self.qtVersionAndNamespace()
|
||||
return self.qtVersionAndNamespace()[1]
|
||||
|
||||
def qtTypeInfoVersion(self):
|
||||
return None
|
||||
|
||||
def ptrSize(self):
|
||||
return cdbext.pointerSize()
|
||||
|
||||
def put(self, stuff):
|
||||
self.output += stuff
|
||||
|
||||
def lookupNativeType(self, name):
|
||||
return cdbext.lookupType(name)
|
||||
|
||||
def currentThread(self):
|
||||
return None
|
||||
|
||||
def currentFrame(self):
|
||||
return None
|
||||
|
||||
def reportResult(self, result, args):
|
||||
self.report('result={%s}' % (result))
|
||||
|
||||
def readRawMemory(self, address, size):
|
||||
return cdbext.readRawMemory(address, size)
|
||||
|
||||
def findStaticMetaObject(self, typeName):
|
||||
symbolName = self.mangleName(typeName + '::staticMetaObject')
|
||||
symbol = self.target.FindFirstGlobalVariable(symbolName)
|
||||
return symbol.AddressOf().GetValueAsUnsigned() if symbol.IsValid() else 0
|
||||
|
||||
def warn(self, msg):
|
||||
self.put('{name="%s",value="",type="",numchild="0"},' % msg)
|
||||
|
||||
def fetchVariables(self, args):
|
||||
(ok, res) = self.tryFetchInterpreterVariables(args)
|
||||
if ok:
|
||||
self.reportResult(res, args)
|
||||
return
|
||||
|
||||
self.setVariableFetchingOptions(args)
|
||||
|
||||
self.output = ''
|
||||
|
||||
self.currentIName = 'local'
|
||||
self.put('data=[')
|
||||
self.anonNumber = 0
|
||||
|
||||
variables = []
|
||||
for val in cdbext.listOfLocals():
|
||||
self.currentContextValue = val
|
||||
name = val.name()
|
||||
value = self.fromNativeValue(val)
|
||||
value.name = name
|
||||
variables.append(value)
|
||||
|
||||
self.handleLocals(variables)
|
||||
self.handleWatches(args)
|
||||
|
||||
self.put('],partial="%d"' % (len(self.partialVariable) > 0))
|
||||
self.reportResult(self.output, args)
|
||||
|
||||
def report(self, stuff):
|
||||
sys.stdout.write(stuff + "\n")
|
||||
|
||||
def loadDumpers(self, args):
|
||||
msg = self.setupDumpers()
|
||||
self.reportResult(msg, args)
|
||||
|
||||
def findValueByExpression(self, exp):
|
||||
return cdbext.parseAndEvaluate(exp)
|
||||
|
||||
def nativeDynamicTypeName(self, address, baseType):
|
||||
return None # FIXME: Seems sufficient, no idea why.
|
@@ -32,6 +32,7 @@ import re
|
||||
import time
|
||||
import json
|
||||
import inspect
|
||||
import threading
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
xrange = range
|
||||
@@ -94,7 +95,7 @@ BreakpointAtJavaScriptThrow, \
|
||||
= range(0, 14)
|
||||
|
||||
|
||||
# Internal codes for types
|
||||
# Internal codes for types keep in sync with cdbextensions pytype.cpp
|
||||
TypeCodeTypedef, \
|
||||
TypeCodeStruct, \
|
||||
TypeCodeVoid, \
|
||||
@@ -301,6 +302,44 @@ class DumperBase:
|
||||
"personaltypes",
|
||||
]
|
||||
|
||||
# These values are never used, but the variables need to have
|
||||
# some value base for the swapping logic in Children.__enter__()
|
||||
# and Children.__exit__().
|
||||
self.currentIName = None
|
||||
self.currentValue = None
|
||||
self.currentType = None
|
||||
self.currentNumChild = None
|
||||
self.currentMaxNumChild = None
|
||||
self.currentPrintsAddress = True
|
||||
self.currentChildType = None
|
||||
self.currentChildNumChild = None
|
||||
|
||||
def setVariableFetchingOptions(self, args):
|
||||
self.resultVarName = args.get('resultvarname', '')
|
||||
self.expandedINames = set(args.get('expanded', []))
|
||||
self.stringCutOff = int(args.get('stringcutoff', 10000))
|
||||
self.displayStringLimit = int(args.get('displaystringlimit', 100))
|
||||
self.typeformats = args.get('typeformats', {})
|
||||
self.formats = args.get('formats', {})
|
||||
self.watchers = args.get('watchers', {})
|
||||
self.useDynamicType = int(args.get('dyntype', '0'))
|
||||
self.useFancy = int(args.get('fancy', '0'))
|
||||
self.forceQtNamespace = int(args.get('forcens', '0'))
|
||||
self.passExceptions = int(args.get('passexceptions', '0'))
|
||||
self.showQObjectNames = int(args.get('qobjectnames', '0'))
|
||||
self.nativeMixed = int(args.get('nativemixed', '0'))
|
||||
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
||||
self.partialVariable = args.get('partialvar', '')
|
||||
self.partialUpdate = int(args.get('partial', '0'))
|
||||
self.fallbackQtVersion = 0x50200
|
||||
#warn('NAMESPACE: "%s"' % self.qtNamespace())
|
||||
#warn('EXPANDED INAMES: %s' % self.expandedINames)
|
||||
#warn('WATCHERS: %s' % self.watchers)
|
||||
|
||||
# The guess does not need to be updated during a fetchVariables()
|
||||
# as the result is fixed during that time (ignoring "active"
|
||||
# dumpers causing loading of shared objects etc).
|
||||
self.currentQtNamespaceGuess = None
|
||||
|
||||
def resetCaches(self):
|
||||
# This is a cache mapping from 'type name' to 'display alternatives'.
|
||||
@@ -772,7 +811,7 @@ class DumperBase:
|
||||
continue
|
||||
|
||||
with SubItem(self, field.name):
|
||||
self.putItem(value[field])
|
||||
self.putItem(value.extractField(field))
|
||||
|
||||
|
||||
def putMembersItem(self, value, sortorder = 10):
|
||||
@@ -1015,7 +1054,8 @@ class DumperBase:
|
||||
if arrayByteSize == 0:
|
||||
# This should not happen. But it does, see QTCREATORBUG-14755.
|
||||
# GDB/GCC produce sizeof == 0 for QProcess arr[3]
|
||||
s = str(value.type)
|
||||
# And in the Nim string dumper.
|
||||
s = value.type.name
|
||||
itemCount = s[s.find('[')+1:s.find(']')]
|
||||
if not itemCount:
|
||||
itemCount = '100'
|
||||
@@ -2449,6 +2489,8 @@ class DumperBase:
|
||||
if typeobj.code == TypeCodeTypedef:
|
||||
strippedType = typeobj.stripTypedefs()
|
||||
self.putItem(value.cast(strippedType))
|
||||
if value.lbitsize is not None and value.lbitsize != value.type.size() * 8:
|
||||
typeName += " : %s" % value.lbitsize
|
||||
self.putBetterType(typeName)
|
||||
return
|
||||
|
||||
@@ -2478,7 +2520,9 @@ class DumperBase:
|
||||
#warn("INTEGER: %s %s" % (value.name, value))
|
||||
self.putValue(value.value())
|
||||
self.putNumChild(0)
|
||||
self.putType(typeobj.name)
|
||||
if value.lbitsize is not None and value.lbitsize != value.type.size() * 8:
|
||||
typeName += " : %s" % value.lbitsize
|
||||
self.putType(typeName)
|
||||
return
|
||||
|
||||
if typeobj.code == TypeCodeFloat:
|
||||
@@ -2579,6 +2623,7 @@ class DumperBase:
|
||||
self.laddress = None
|
||||
self.lIsInScope = True
|
||||
self.ldisplay = None
|
||||
self.lbitsize = None
|
||||
|
||||
def check(self):
|
||||
if self.laddress is not None and not self.dumper.isInt(self.laddress):
|
||||
@@ -2595,8 +2640,9 @@ class DumperBase:
|
||||
|
||||
def stringify(self):
|
||||
addr = "None" if self.laddress is None else ("0x%x" % self.laddress)
|
||||
return "Value(name='%s',type=%s,data=%s,address=%s)" \
|
||||
% (self.name, self.type.stringify(), self.dumper.hexencode(self.ldata), addr)
|
||||
return "Value(name='%s',type=%s,bsize=%s,data=%s,address=%s)" \
|
||||
% (self.name, self.type.name, self.lbitsize,
|
||||
self.dumper.hexencode(self.ldata), addr)
|
||||
|
||||
def display(self):
|
||||
if self.type.code == TypeCodeEnum:
|
||||
@@ -2686,6 +2732,7 @@ class DumperBase:
|
||||
fieldBitpos = field.bitpos()
|
||||
fieldOffset = fieldBitpos >> 3
|
||||
fieldBitpos -= fieldOffset * 8
|
||||
fieldType = field.fieldType()
|
||||
|
||||
val = self.dumper.Value(self.dumper)
|
||||
val.name = field.name
|
||||
@@ -2697,7 +2744,7 @@ class DumperBase:
|
||||
else:
|
||||
self.dumper.check(False)
|
||||
|
||||
if fieldBitsize is not None and fieldBitsize % 8 != 0:
|
||||
if fieldBitsize is not None and fieldBitsize != fieldType.size() * 8:
|
||||
data = val.extractInteger(fieldBitsize, True)
|
||||
data = data >> fieldBitpos
|
||||
data = data & ((1 << fieldBitsize) - 1)
|
||||
@@ -2705,7 +2752,6 @@ class DumperBase:
|
||||
val.ldata = bytes(struct.pack('Q', data))
|
||||
|
||||
val.type = None
|
||||
fieldType = field.fieldType()
|
||||
if val.laddress is not None and fieldType is not None:
|
||||
if fieldType.code in (TypeCodePointer, TypeCodeReference):
|
||||
baseType = fieldType.dereference()
|
||||
@@ -2771,6 +2817,7 @@ class DumperBase:
|
||||
self.check()
|
||||
val = self.dumper.Value(self.dumper)
|
||||
val.laddress = self.laddress
|
||||
val.lbitsize = self.lbitsize
|
||||
val.ldata = self.ldata
|
||||
val.type = self.dumper.createType(typish)
|
||||
return val
|
||||
@@ -2889,8 +2936,8 @@ class DumperBase:
|
||||
#error("Not implemented")
|
||||
|
||||
def stringify(self):
|
||||
return "Type(name='%s',bsize=%s,bpos=%s,code=%s,native=%s)" \
|
||||
% (self.name, self.lbitsize, self.lbitpos, self.code, self.nativeType is not None)
|
||||
return "Type(name='%s',bsize=%s,bpos=%s,code=%s,ntype=%s)" \
|
||||
% (self.name, self.lbitsize, self.lbitpos, self.code, self.nativeType)
|
||||
|
||||
def __getitem__(self, index):
|
||||
if self.dumper.isInt(index):
|
||||
@@ -2994,7 +3041,7 @@ class DumperBase:
|
||||
innerType.code = TypeCodeArray
|
||||
return innerType
|
||||
|
||||
strippedType = self.stripTypdefs()
|
||||
strippedType = self.stripTypedefs()
|
||||
if strippedType.name != self.name:
|
||||
return strippedType.target()
|
||||
error("DONT KNOW TARGET FOR: %s" % self)
|
||||
@@ -3334,8 +3381,20 @@ class DumperBase:
|
||||
elif c == '{':
|
||||
readingTypeName = True
|
||||
typeName = ""
|
||||
elif c == '@': # Automatic padding.
|
||||
builder.autoPadNext = True
|
||||
elif c == '@':
|
||||
if n is None:
|
||||
# Automatic padding depending on next item
|
||||
builder.autoPadNext = True
|
||||
else:
|
||||
# Explicit padding.
|
||||
builder.currentBitsize = 8 * ((builder.currentBitsize + 7) >> 3)
|
||||
padding = (int(n) - (builder.currentBitsize >> 3)) % int(n)
|
||||
field = self.Field(self)
|
||||
field.code = None
|
||||
builder.pattern += "%ds" % padding
|
||||
builder.currentBitsize += padding * 8
|
||||
builder.fields.append(field)
|
||||
n = None
|
||||
else:
|
||||
error("UNKNOWN STRUCT CODE: %s" % c)
|
||||
pp = builder.pattern
|
||||
|
@@ -203,41 +203,7 @@ class Dumper(DumperBase):
|
||||
|
||||
def prepare(self, args):
|
||||
self.output = []
|
||||
self.currentIName = ""
|
||||
self.currentPrintsAddress = True
|
||||
self.currentChildType = ""
|
||||
self.currentChildNumChild = -1
|
||||
self.currentMaxNumChild = -1
|
||||
self.currentNumChild = -1
|
||||
self.currentValue = ReportItem()
|
||||
self.currentType = ReportItem()
|
||||
self.currentAddress = None
|
||||
|
||||
self.resultVarName = args.get("resultvarname", "")
|
||||
self.expandedINames = set(args.get("expanded", []))
|
||||
self.stringCutOff = int(args.get("stringcutoff", 10000))
|
||||
self.displayStringLimit = int(args.get("displaystringlimit", 100))
|
||||
self.typeformats = args.get("typeformats", {})
|
||||
self.formats = args.get("formats", {})
|
||||
self.watchers = args.get("watchers", {})
|
||||
self.useDynamicType = int(args.get("dyntype", "0"))
|
||||
self.useFancy = int(args.get("fancy", "0"))
|
||||
self.forceQtNamespace = int(args.get("forcens", "0"))
|
||||
self.passExceptions = int(args.get("passexceptions", "0"))
|
||||
self.showQObjectNames = int(args.get("qobjectnames", "0"))
|
||||
self.nativeMixed = int(args.get("nativemixed", "0"))
|
||||
self.autoDerefPointers = int(args.get("autoderef", "0"))
|
||||
self.partialUpdate = int(args.get("partial", "0"))
|
||||
self.fallbackQtVersion = 0x50200
|
||||
|
||||
#warn("NAMESPACE: '%s'" % self.qtNamespace())
|
||||
#warn("EXPANDED INAMES: %s" % self.expandedINames)
|
||||
#warn("WATCHERS: %s" % self.watchers)
|
||||
|
||||
# The guess does not need to be updated during a fetchVariables()
|
||||
# as the result is fixed during that time (ignoring "active"
|
||||
# dumpers causing loading of shared objects etc).
|
||||
self.currentQtNamespaceGuess = None
|
||||
self.setVariableFetchingOptions(args)
|
||||
|
||||
def fromNativeDowncastableValue(self, nativeValue):
|
||||
if self.useDynamicType:
|
||||
@@ -680,6 +646,9 @@ class Dumper(DumperBase):
|
||||
# We get i686-w64-mingw32
|
||||
return 'mingw' in gdb.TARGET_CONFIG.lower()
|
||||
|
||||
def isMsvcTarget(self):
|
||||
return False
|
||||
|
||||
def qtVersionString(self):
|
||||
try:
|
||||
return str(gdb.lookup_symbol("qVersion")[0].value()())
|
||||
|
@@ -61,7 +61,7 @@ class Dumper(DumperBase):
|
||||
def __init__(self):
|
||||
DumperBase.__init__(self)
|
||||
lldb.theDumper = self
|
||||
|
||||
self.isLldb = True
|
||||
self.outputLock = threading.Lock()
|
||||
self.debugger = lldb.SBDebugger.Create()
|
||||
#self.debugger.SetLoggingCallback(loggingCallback)
|
||||
@@ -91,30 +91,10 @@ class Dumper(DumperBase):
|
||||
#for i in range(self.debugger.GetNumCategories()):
|
||||
# self.debugger.GetCategoryAtIndex(i).SetEnabled(False)
|
||||
|
||||
self.isLldb = True
|
||||
self.process = None
|
||||
self.target = None
|
||||
self.eventState = lldb.eStateInvalid
|
||||
self.expandedINames = {}
|
||||
self.passExceptions = False
|
||||
self.showQObjectNames = False
|
||||
self.useLldbDumpers = False
|
||||
self.autoDerefPointers = True
|
||||
self.useDynamicType = True
|
||||
self.useFancy = True
|
||||
self.formats = {}
|
||||
self.typeformats = {}
|
||||
self.currentContextValue = None
|
||||
|
||||
self.currentIName = None
|
||||
self.currentValue = ReportItem()
|
||||
self.currentType = ReportItem()
|
||||
self.currentNumChild = None
|
||||
self.currentMaxNumChild = None
|
||||
self.currentPrintsAddress = True
|
||||
self.currentChildType = None
|
||||
self.currentChildNumChild = -1
|
||||
self.currentWatchers = {}
|
||||
self.runEngineAttempted = False
|
||||
|
||||
self.executable_ = None
|
||||
self.startMode_ = None
|
||||
@@ -478,6 +458,9 @@ class Dumper(DumperBase):
|
||||
def isArmArchitecture(self):
|
||||
return False
|
||||
|
||||
def isMsvcTarget(self):
|
||||
return False
|
||||
|
||||
def qtVersionAndNamespace(self):
|
||||
for func in self.target.FindFunctions('qVersion'):
|
||||
name = func.GetSymbol().GetName()
|
||||
@@ -630,9 +613,13 @@ class Dumper(DumperBase):
|
||||
self.target.BreakpointCreateByName("qt_qmlDebugMessageAvailable")
|
||||
|
||||
state = 1 if self.target.IsValid() else 0
|
||||
self.reportResult('success="%s",msg="%s",exe="%s"' % (state, error, self.executable_), args)
|
||||
self.reportResult('success="%s",msg="%s",exe="%s"'
|
||||
% (state, error, self.executable_), args)
|
||||
|
||||
def runEngine(self, args):
|
||||
if self.runEngineAttempted:
|
||||
return
|
||||
self.runEngineAttempted = True
|
||||
self.prepare(args)
|
||||
s = threading.Thread(target=self.loop, args=[])
|
||||
s.start()
|
||||
@@ -877,15 +864,7 @@ class Dumper(DumperBase):
|
||||
self.reportResult(res, args)
|
||||
return
|
||||
|
||||
self.expandedINames = set(args.get('expanded', []))
|
||||
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
||||
self.useDynamicType = int(args.get('dyntype', '0'))
|
||||
self.useFancy = int(args.get('fancy', '0'))
|
||||
self.passExceptions = int(args.get('passexceptions', '0'))
|
||||
self.showQObjectNames = int(args.get('qobjectnames', '0'))
|
||||
self.currentWatchers = args.get('watchers', {})
|
||||
self.typeformats = args.get('typeformats', {})
|
||||
self.formats = args.get('formats', {})
|
||||
self.setVariableFetchingOptions(args)
|
||||
|
||||
frame = self.currentFrame()
|
||||
if frame is None:
|
||||
@@ -893,9 +872,7 @@ class Dumper(DumperBase):
|
||||
return
|
||||
|
||||
self.output = ''
|
||||
self.currentAddress = None
|
||||
partialVariable = args.get('partialvar', "")
|
||||
isPartial = len(partialVariable) > 0
|
||||
isPartial = len(self.partialVariable) > 0
|
||||
|
||||
self.currentIName = 'local'
|
||||
self.put('data=[')
|
||||
|
@@ -33,73 +33,64 @@ from dumper import *
|
||||
|
||||
def qdump____m128(d, value):
|
||||
d.putEmptyValue()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
d.putArrayData(value.address(), 4, d.lookupType("float"))
|
||||
d.putArrayData(value.address(), 4, d.lookupType('float'))
|
||||
|
||||
def qdump____m256(d, value):
|
||||
d.putEmptyValue()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
d.putArrayData(value.address(), 8, d.lookupType("float"))
|
||||
d.putArrayData(value.address(), 8, d.lookupType('float'))
|
||||
|
||||
def qdump____m512(d, value):
|
||||
d.putEmptyValue()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
d.putArrayData(value.address(), 16, d.lookupType("float"))
|
||||
d.putArrayData(value.address(), 16, d.lookupType('float'))
|
||||
|
||||
def qdump____m128d(d, value):
|
||||
d.putEmptyValue()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
d.putArrayData(value.address(), 2, d.lookupType("double"))
|
||||
d.putArrayData(value.address(), 2, d.lookupType('double'))
|
||||
|
||||
def qdump____m256d(d, value):
|
||||
d.putEmptyValue()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
d.putArrayData(value.address(), 4, d.lookupType("double"))
|
||||
d.putArrayData(value.address(), 4, d.lookupType('double'))
|
||||
|
||||
def qdump____m512d(d, value):
|
||||
d.putEmptyValue()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
d.putArrayData(value.address(), 8, d.lookupType("double"))
|
||||
d.putArrayData(value.address(), 8, d.lookupType('double'))
|
||||
|
||||
def qdump____m128i(d, value):
|
||||
data = d.hexencode(value.data())
|
||||
d.putValue(':'.join("%04x" % int(data[i:i+4], 16) for i in xrange(0, 32, 4)))
|
||||
d.putNumChild(4)
|
||||
d.putValue(':'.join('%04x' % int(data[i:i+4], 16) for i in xrange(0, 32, 4)))
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
addr = value.address()
|
||||
d.putArrayItem("uint8x16", addr, 16, "unsigned char")
|
||||
d.putArrayItem("uint16x8", addr, 8, "unsigned short")
|
||||
d.putArrayItem("uint32x4", addr, 4, "unsigned int")
|
||||
d.putArrayItem("uint64x2", addr, 2, "unsigned long long")
|
||||
d.putArrayItem('uint8x16', addr, 16, 'unsigned char')
|
||||
d.putArrayItem('uint16x8', addr, 8, 'unsigned short')
|
||||
d.putArrayItem('uint32x4', addr, 4, 'unsigned int')
|
||||
d.putArrayItem('uint64x2', addr, 2, 'unsigned long long')
|
||||
|
||||
def qdump____m256i(d, value):
|
||||
data = d.hexencode(value.data())
|
||||
d.putValue(':'.join("%04x" % int(data[i:i+4], 16) for i in xrange(0, 64, 4)))
|
||||
d.putNumChild(4)
|
||||
d.putValue(':'.join('%04x' % int(data[i:i+4], 16) for i in xrange(0, 64, 4)))
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
addr = value.address()
|
||||
d.putArrayItem("uint8x32", addr, 32, "unsigned char")
|
||||
d.putArrayItem("uint16x16", addr, 16, "unsigned short")
|
||||
d.putArrayItem("uint32x8", addr, 8, "unsigned int")
|
||||
d.putArrayItem("uint64x4", addr, 4, "unsigned long long")
|
||||
d.putArrayItem('uint8x32', addr, 32, 'unsigned char')
|
||||
d.putArrayItem('uint16x16', addr, 16, 'unsigned short')
|
||||
d.putArrayItem('uint32x8', addr, 8, 'unsigned int')
|
||||
d.putArrayItem('uint64x4', addr, 4, 'unsigned long long')
|
||||
|
||||
def qdump____m512i(d, value):
|
||||
data = d.hexencode(value.data())
|
||||
d.putValue(':'.join("%04x" % int(data[i:i+4], 16) for i in xrange(0, 64, 4))
|
||||
+ ', ' + ':'.join("%04x" % int(data[i:i+4], 16) for i in xrange(64, 128, 4)))
|
||||
d.putNumChild(2)
|
||||
d.putValue(':'.join('%04x' % int(data[i:i+4], 16) for i in xrange(0, 64, 4))
|
||||
+ ', ' + ':'.join('%04x' % int(data[i:i+4], 16) for i in xrange(64, 128, 4)))
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
d.putArrayItem("uint32x16", value.address(), 16, "unsigned int")
|
||||
d.putArrayItem("uint64x8", value.address(), 8, "unsigned long long")
|
||||
d.putArrayItem('uint32x16', value.address(), 16, 'unsigned int')
|
||||
d.putArrayItem('uint64x8', value.address(), 8, 'unsigned long long')
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
@@ -108,7 +99,7 @@ def qdump____m512i(d, value):
|
||||
#######################################################################
|
||||
|
||||
#def qform__Eigen__Matrix():
|
||||
# return "Transposed"
|
||||
# return 'Transposed'
|
||||
|
||||
def qdump__Eigen__Matrix(d, value):
|
||||
innerType = value.type.templateArgument(0, False)
|
||||
@@ -118,7 +109,7 @@ def qdump__Eigen__Matrix(d, value):
|
||||
rowMajor = (int(options) & 0x1)
|
||||
# The magic dimension value is -1 in Eigen3, but 10000 in Eigen2.
|
||||
# 10000 x 10000 matrices are rare, vectors of dim 10000 less so.
|
||||
# So "fix" only the matrix case:
|
||||
# So 'fix' only the matrix case:
|
||||
if argCol == 10000 and argRow == 10000:
|
||||
argCol = -1
|
||||
argRow = -1
|
||||
@@ -127,20 +118,20 @@ def qdump__Eigen__Matrix(d, value):
|
||||
ncols = argCol
|
||||
p = value.address()
|
||||
else:
|
||||
storage = value["m_storage"]
|
||||
nrows = storage["m_rows"].integer() if argRow == -1 else argRow
|
||||
ncols = storage["m_cols"].integer() if argCol == -1 else argCol
|
||||
p = storage["m_data"].integer()
|
||||
storage = value['m_storage']
|
||||
nrows = storage['m_rows'].integer() if argRow == -1 else argRow
|
||||
ncols = storage['m_cols'].integer() if argCol == -1 else argCol
|
||||
p = storage['m_data'].integer()
|
||||
innerSize = innerType.size()
|
||||
d.putValue("(%s x %s), %s" % (nrows, ncols, ["ColumnMajor", "RowMajor"][rowMajor]))
|
||||
d.putField("keeporder", "1")
|
||||
d.putValue('(%s x %s), %s' % (nrows, ncols, ['ColumnMajor', 'RowMajor'][rowMajor]))
|
||||
d.putField('keeporder', '1')
|
||||
d.putNumChild(nrows * ncols)
|
||||
|
||||
limit = 10000
|
||||
nncols = min(ncols, limit)
|
||||
nnrows = min(nrows, limit * limit / nncols)
|
||||
if d.isExpanded():
|
||||
#format = d.currentItemFormat() # format == 1 is "Transposed"
|
||||
#format = d.currentItemFormat() # format == 1 is 'Transposed'
|
||||
with Children(d, nrows * ncols, childType=innerType):
|
||||
if ncols == 1 or nrows == 1:
|
||||
for i in range(0, min(nrows * ncols, 10000)):
|
||||
@@ -150,17 +141,41 @@ def qdump__Eigen__Matrix(d, value):
|
||||
for i in range(0, nnrows):
|
||||
for j in range(0, nncols):
|
||||
v = d.createValue(p + (i * ncols + j) * innerSize, innerType)
|
||||
d.putNamedSubItem(s, v, "[%d,%d]" % (i, j))
|
||||
d.putNamedSubItem(s, v, '[%d,%d]' % (i, j))
|
||||
s = s + 1
|
||||
else:
|
||||
s = 0
|
||||
for j in range(0, nncols):
|
||||
for i in range(0, nnrows):
|
||||
v = d.createValue(p + (i + j * nrows) * innerSize, innerType)
|
||||
d.putNamedSubItem(s, v, "[%d,%d]" % (i, j))
|
||||
d.putNamedSubItem(s, v, '[%d,%d]' % (i, j))
|
||||
s = s + 1
|
||||
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# Nim
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
def qdump__NimStringDesc(d, value):
|
||||
size, reserved = value.split('pp')
|
||||
data = value.address() + 2 * d.ptrSize()
|
||||
d.putCharArrayHelper(data, size, d.createType('char'), 'utf8')
|
||||
|
||||
def qdump__NimGenericSequence__(d, value, regex = '^TY[\d]+$'):
|
||||
code = value.type.stripTypedefs().code
|
||||
if code == TypeCodeStruct:
|
||||
size, reserved = d.split('pp', value)
|
||||
data = value.address() + 2 * d.ptrSize()
|
||||
typeobj = value['data'].type.dereference()
|
||||
d.putItemCount(size)
|
||||
d.putArrayData(data, size, typeobj)
|
||||
d.putBetterType('%s (%s[%s])' % (value.type.name, typeobj.name, size))
|
||||
else:
|
||||
d.putEmptyValue()
|
||||
d.putPlainChildren(value)
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# D
|
||||
@@ -168,15 +183,15 @@ def qdump__Eigen__Matrix(d, value):
|
||||
#######################################################################
|
||||
|
||||
def cleanDType(type):
|
||||
return str(type).replace("uns long long", "string")
|
||||
return str(type).replace('uns long long', 'string')
|
||||
|
||||
def qdump_Array(d, value):
|
||||
n = value["length"]
|
||||
p = value["ptr"]
|
||||
n = value['length']
|
||||
p = value['ptr']
|
||||
t = cleanDType(value.type)[7:]
|
||||
d.putType("%s[%d]" % (t, n))
|
||||
if t == "char":
|
||||
d.putValue(encodeCharArray(p, 100), "local8bit")
|
||||
d.putType('%s[%d]' % (t, n))
|
||||
if t == 'char':
|
||||
d.putValue(encodeCharArray(p, 100), 'local8bit')
|
||||
d.putNumChild(0)
|
||||
else:
|
||||
d.putEmptyValue()
|
||||
@@ -190,91 +205,64 @@ def qdump_Array(d, value):
|
||||
|
||||
|
||||
def qdump_AArray(d, value):
|
||||
#n = value["length"]
|
||||
#n = value['length']
|
||||
# This ends up as _AArray_<key>_<value> with a single .ptr
|
||||
# member of type void *. Not much that can be done here.
|
||||
p = value["ptr"]
|
||||
p = value['ptr']
|
||||
t = cleanDType(value.type)[8:]
|
||||
d.putType("%s]" % t.replace("_", "["))
|
||||
d.putType('%s]' % t.replace('_', '['))
|
||||
d.putEmptyValue()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
with Children(d, 1):
|
||||
d.putSubItem("ptr", p)
|
||||
d.putSubItem('ptr', p)
|
||||
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# Display Test
|
||||
# MPI
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
if False:
|
||||
|
||||
# FIXME: Make that work
|
||||
def qdump__Color(d, value):
|
||||
v = value
|
||||
d.putValue("(%s, %s, %s; %s)" % (v["r"], v["g"], v["b"], v["a"]))
|
||||
d.putPlainChildren(value)
|
||||
|
||||
def qdump__Color_(d, value):
|
||||
v = value
|
||||
d.putValue("(%s, %s, %s; %s)" % (v["r"], v["g"], v["b"], v["a"]))
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
with SubItem(d, "0"):
|
||||
d.putItem(v["r"])
|
||||
with SubItem(d, "1"):
|
||||
d.putItem(v["g"])
|
||||
with SubItem(d, "2"):
|
||||
d.putItem(v["b"])
|
||||
with SubItem(d, "3"):
|
||||
d.putItem(v["a"])
|
||||
|
||||
|
||||
if False:
|
||||
|
||||
def qdump__tree_entry(d, value):
|
||||
d.putValue("len: %s, offset: %s, type: %s" %
|
||||
(value["blocklength"], value["offset"], value["type"]))
|
||||
d.putValue('len: %s, offset: %s, type: %s' %
|
||||
(value['blocklength'], value['offset'], value['type']))
|
||||
d.putNumChild(0)
|
||||
|
||||
def qdump__tree(d, value):
|
||||
count = value["count"]
|
||||
entries = value["entries"]
|
||||
base = value["base"].pointer()
|
||||
count = value['count']
|
||||
entries = value['entries']
|
||||
base = value['base'].pointer()
|
||||
d.putItemCount(count)
|
||||
d.putNumChild(count)
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
with SubItem(d, "tree"):
|
||||
with SubItem(d, 'tree'):
|
||||
d.putEmptyValue()
|
||||
d.putNoType()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
for i in xrange(count):
|
||||
d.putSubItem(Item(entries[i], iname))
|
||||
with SubItem(d, "data"):
|
||||
with SubItem(d, 'data'):
|
||||
d.putEmptyValue()
|
||||
d.putNoType()
|
||||
d.putNumChild(1)
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
for i in xrange(count):
|
||||
with SubItem(d, i):
|
||||
entry = entries[i]
|
||||
mpitype = str(entry["type"])
|
||||
mpitype = str(entry['type'])
|
||||
d.putType(mpitype)
|
||||
length = int(entry["blocklength"])
|
||||
offset = int(entry["offset"])
|
||||
d.putValue("%s items at %s" % (length, offset))
|
||||
if mpitype == "MPI_INT":
|
||||
innerType = "int"
|
||||
elif mpitype == "MPI_CHAR":
|
||||
innerType = "char"
|
||||
elif mpitype == "MPI_DOUBLE":
|
||||
innerType = "double"
|
||||
length = int(entry['blocklength'])
|
||||
offset = int(entry['offset'])
|
||||
d.putValue('%s items at %s' % (length, offset))
|
||||
if mpitype == 'MPI_INT':
|
||||
innerType = 'int'
|
||||
elif mpitype == 'MPI_CHAR':
|
||||
innerType = 'char'
|
||||
elif mpitype == 'MPI_DOUBLE':
|
||||
innerType = 'double'
|
||||
else:
|
||||
length = 0
|
||||
d.putNumChild(length)
|
||||
@@ -285,70 +273,23 @@ if False:
|
||||
for j in range(length):
|
||||
d.putSubItem(j, p.dereference())
|
||||
|
||||
#struct KRBase
|
||||
#{
|
||||
# enum Type { TYPE_A, TYPE_B } type;
|
||||
# KRBase(Type _type) : type(_type) {}
|
||||
#};
|
||||
#
|
||||
#struct KRA : KRBase { int x; int y; KRA():KRBase(TYPE_A),x(1),y(32) {} };
|
||||
#struct KRB : KRBase { KRB():KRBase(TYPE_B) {} };
|
||||
#
|
||||
#void testKR()
|
||||
#{
|
||||
# KRBase *ptr1 = new KRA;
|
||||
# KRBase *ptr2 = new KRB;
|
||||
# ptr2 = new KRB;
|
||||
#}
|
||||
|
||||
def qdump__KRBase(d, value):
|
||||
if getattr(value, "__nested__", None) is None:
|
||||
base = ["KRA", "KRB"][int(value["type"])]
|
||||
nest = value.cast(d.lookupType(base))
|
||||
nest.__nested__ = True
|
||||
warn("NEST %s " % dir(nest))
|
||||
d.putItem(nest)
|
||||
else:
|
||||
d.putName("type")
|
||||
d.putValue(value["type"])
|
||||
d.putNoType()
|
||||
|
||||
|
||||
|
||||
if False:
|
||||
def qdump__bug5106__A5106(d, value):
|
||||
d.putName("a")
|
||||
d.putValue("This is the value: %s" % value["m_a"])
|
||||
d.putNoType()
|
||||
d.putNumChild(0)
|
||||
|
||||
|
||||
if False:
|
||||
def qdump__bug6933__Base(d, value):
|
||||
d.putValue("foo")
|
||||
d.putPlainChildren(value)
|
||||
|
||||
if False:
|
||||
def qdump__gdb13393__Base(d, value):
|
||||
d.putValue("Base (%s)" % value["a"])
|
||||
d.putType(value.type)
|
||||
d.putPlainChildren(value)
|
||||
|
||||
def qdump__gdb13393__Derived(d, value):
|
||||
d.putValue("Derived (%s, %s)" % (value["a"], value["b"]))
|
||||
d.putType(value.type)
|
||||
d.putPlainChildren(value)
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
# KDSoap
|
||||
#
|
||||
#######################################################################
|
||||
|
||||
def qdump__KDSoapValue1(d, value):
|
||||
inner = value["d"]["d"].dereference()
|
||||
d.putStringValue(inner["m_name"])
|
||||
inner = value['d']['d'].dereference()
|
||||
d.putStringValue(inner['m_name'])
|
||||
d.putPlainChildren(inner)
|
||||
|
||||
def qdump__KDSoapValue(d, value):
|
||||
p = (value.cast(lookupType("char*")) + 4).dereference().cast(lookupType("QString"))
|
||||
p = (value.cast(lookupType('char*')) + 4).dereference().cast(lookupType('QString'))
|
||||
d.putStringValue(p)
|
||||
d.putPlainChildren(value["d"]["d"].dereference())
|
||||
d.putPlainChildren(value['d']['d'].dereference())
|
||||
|
||||
|
||||
#######################################################################
|
||||
#
|
||||
|
@@ -2063,11 +2063,63 @@ def qdump__QV4_Object(d, value):
|
||||
d.putValue("PTR: 0x%x" % objectPtr)
|
||||
|
||||
def qdump__QV4__Value(d, value):
|
||||
if d.ptrSize() == 4:
|
||||
qdump_32__QV4__Value(d, value)
|
||||
else:
|
||||
qdump_64__QV4__Value(d, value)
|
||||
|
||||
def qdump_32__QV4__Value(d, value):
|
||||
# QV4_Masks_SilentNaNBit = 0x00040000
|
||||
# QV4_Masks_NaN_Mask = 0x7ff80000
|
||||
# QV4_Masks_NotDouble_Mask = 0x7ffa0000
|
||||
# QV4_Masks_Type_Mask = 0xffffc000
|
||||
ns = d.qtNamespace()
|
||||
v = value.split('Q')[0]
|
||||
tag = v >> 32
|
||||
val = v & 0xffffffff
|
||||
if (tag & 0x7fff2000) == 0x7fff2000: # Int
|
||||
d.putValue(val)
|
||||
d.putBetterType("%sQV4::Value (int32)" % ns)
|
||||
elif (tag & 0x7fff4000) == 0x7fff4000: # Bool
|
||||
d.putValue(val)
|
||||
d.putBetterType("%sQV4::Value (bool)" % ns)
|
||||
elif (tag & 0x7fff0000) == 0x7fff0000: # Null
|
||||
d.putValue(val)
|
||||
d.putBetterType("%sQV4::Value (null)" % ns)
|
||||
elif (tag & 0x7ffa0000) != 0x7ffa0000: # Double
|
||||
d.putValue(value.split('d')[0])
|
||||
d.putBetterType("%sQV4::Value (double)" % ns)
|
||||
elif tag == 0x7ffa0000:
|
||||
if val == 0:
|
||||
d.putValue("(undefined)")
|
||||
d.putBetterType("%sQV4::Value (undefined)" % ns)
|
||||
else:
|
||||
managed = d.createValue(val, ns + "QV4::Heap::Base")
|
||||
qdump__QV4__Heap__Base(d, managed)
|
||||
#d.putValue("[0x%x]" % v)
|
||||
#d.putPlainChildren(value)
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
with SubItem(d, "[raw]"):
|
||||
d.putValue("[0x%x]" % v)
|
||||
d.putType(" ");
|
||||
d.putNumChild(0)
|
||||
with SubItem(d, "[val]"):
|
||||
d.putValue("[0x%x]" % val)
|
||||
d.putType(" ");
|
||||
d.putNumChild(0)
|
||||
with SubItem(d, "[tag]"):
|
||||
d.putValue("[0x%x]" % tag)
|
||||
d.putType(" ");
|
||||
d.putNumChild(0)
|
||||
#with SubItem(d, "[vtable]"):
|
||||
# d.putItem(d.createValue(vtable, ns + "QV4::VTable"))
|
||||
# d.putType(" ");
|
||||
# d.putNumChild(0)
|
||||
d.putFields(value)
|
||||
|
||||
def qdump_64__QV4__Value(d, value):
|
||||
v = value.split('Q')[0]
|
||||
if d.ptrSize() == 4:
|
||||
d.putValue("[0x%x]" % v)
|
||||
d.putPlainChildren(value)
|
||||
return
|
||||
tag = v >> QV4_Masks_Tag_Shift
|
||||
vtable = v & QV4_PointerMask
|
||||
ns = d.qtNamespace()
|
||||
@@ -2158,15 +2210,45 @@ def qdump__QV4__ScopedString(d, value):
|
||||
|
||||
|
||||
def qdump__QJSValue(d, value):
|
||||
if d.ptrSize() == 4:
|
||||
qdump_32__QJSValue(d, value)
|
||||
else:
|
||||
qdump_64__QJSValue(d, value)
|
||||
|
||||
def qdump_32__QJSValue(d, value):
|
||||
ns = d.qtNamespace()
|
||||
dd = value.split('Q')[0]
|
||||
if dd & 1:
|
||||
dd = value.split('I')[0]
|
||||
d.putValue("[0x%x]" % dd)
|
||||
if dd == 0:
|
||||
d.putValue("(null)")
|
||||
d.putType(value.type.name + " (null)")
|
||||
elif dd & 1:
|
||||
variant = d.createValue(dd & ~3, ns + "QVariant")
|
||||
qdump__QVariant(d, variant)
|
||||
d.putBetterType(d.currentType.value.replace('QVariant', 'QJSValue', 1))
|
||||
elif dd == 0:
|
||||
elif dd & 3 == 0:
|
||||
v4value = d.createValue(dd, ns + "QV4::Value")
|
||||
qdump_32__QV4__Value(d, v4value)
|
||||
d.putBetterType(d.currentType.value.replace('QV4::Value', 'QJSValue', 1))
|
||||
return
|
||||
if d.isExpanded():
|
||||
with Children(d):
|
||||
with SubItem(d, "[raw]"):
|
||||
d.putValue("[0x%x]" % dd)
|
||||
d.putType(" ");
|
||||
d.putNumChild(0)
|
||||
d.putFields(value)
|
||||
|
||||
def qdump_64__QJSValue(d, value):
|
||||
ns = d.qtNamespace()
|
||||
dd = value.split('Q')[0]
|
||||
if dd == 0:
|
||||
d.putValue("(null)")
|
||||
d.putType(value.type.name + " (null)")
|
||||
elif dd & 1:
|
||||
variant = d.createValue(dd & ~3, ns + "QVariant")
|
||||
qdump__QVariant(d, variant)
|
||||
d.putBetterType(d.currentType.value.replace('QVariant', 'QJSValue', 1))
|
||||
else:
|
||||
d.putEmptyValue()
|
||||
#qdump__QV4__Value(d, d.createValue(dd, ns + 'QV4::Value'))
|
||||
|
@@ -529,6 +529,9 @@ def qdumpHelper_std__string(d, value, charType, format):
|
||||
if d.isQnxTarget():
|
||||
qdumpHelper__std__string__QNX(d, value, charType, format)
|
||||
return
|
||||
if d.isMsvcTarget():
|
||||
qdumpHelper__std__string__MSVC(d, value, charType, format)
|
||||
return
|
||||
|
||||
data = value.extractPointer()
|
||||
# We can't lookup the std::string::_Rep type without crashing LLDB,
|
||||
@@ -554,6 +557,23 @@ def qdumpHelper__std__string__QNX(d, value, charType, format):
|
||||
d.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
|
||||
d.putCharArrayHelper(sizePtr, size, charType, format)
|
||||
|
||||
def qdumpHelper__std__string__MSVC(d, value, charType, format):
|
||||
val = value
|
||||
try:
|
||||
size = value['_Mysize'].integer()
|
||||
alloc = value['_Myres'].integer()
|
||||
except:
|
||||
val = value['_Mypair']['_Myval2']
|
||||
size = d.extractUInt64(val['_Mysize'])
|
||||
alloc = d.extractUInt64(val['_Myres'])
|
||||
|
||||
_BUF_SIZE = int(16 / charType.size());
|
||||
d.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
|
||||
if _BUF_SIZE <= alloc:
|
||||
data = val['_Bx']['_Ptr'].pointer()
|
||||
else:
|
||||
data = val['_Bx']['_Buf'].address()
|
||||
d.putCharArrayHelper(data, size, charType, format)
|
||||
|
||||
def qdump__std____1__string(d, value):
|
||||
firstByte = value.split('b')[0]
|
||||
|
@@ -85,28 +85,65 @@
|
||||
|
||||
#include <designersupportdelegate.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
bool testImportStatements(const QStringList &importStatementList, const QUrl &url, QString *errorMessage = 0) {
|
||||
QQmlEngine engine;
|
||||
QQmlComponent testImportComponent(&engine);
|
||||
bool testImportStatements(const QStringList &importStatementList, const QUrl &url, QString *errorMessage = 0) {
|
||||
// ToDo: move engine outside of this function, this makes it expensive
|
||||
QQmlEngine engine;
|
||||
QQmlComponent testImportComponent(&engine);
|
||||
|
||||
QByteArray testComponentCode = QStringList(importStatementList).join("\n").toUtf8();
|
||||
QByteArray testComponentCode = QStringList(importStatementList).join("\n").toUtf8();
|
||||
|
||||
testImportComponent.setData(testComponentCode.append("\nItem {}\n"), url);
|
||||
testImportComponent.create();
|
||||
testImportComponent.setData(testComponentCode.append("\nItem {}\n"), url);
|
||||
testImportComponent.create();
|
||||
|
||||
if (testImportComponent.errors().isEmpty()) {
|
||||
return true;
|
||||
} else {
|
||||
if (errorMessage) {
|
||||
errorMessage->append("found not working imports: ");
|
||||
errorMessage->append(testImportComponent.errorString());
|
||||
if (testImportComponent.isError()) {
|
||||
if (errorMessage) {
|
||||
errorMessage->append("found not working imports: ");
|
||||
errorMessage->append(testImportComponent.errorString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void sortFilterImports(const QStringList &imports, QStringList *workingImports, QStringList *failedImports, const QUrl &url, QString *errorMessage)
|
||||
{
|
||||
for (const QString &import : imports) {
|
||||
const QStringList alreadyTestedImports = *workingImports + *failedImports;
|
||||
if (!alreadyTestedImports.contains(import)) {
|
||||
QStringList readyForTestImports = *workingImports;
|
||||
readyForTestImports.append(import);
|
||||
|
||||
QString lastErrorMessage;
|
||||
if (testImportStatements(readyForTestImports, url, &lastErrorMessage)) {
|
||||
Q_ASSERT(!workingImports->contains(import));
|
||||
workingImports->append(import);
|
||||
} else {
|
||||
if (imports.endsWith(import) == false) {
|
||||
// the not working import is not the last import, so there could be some
|
||||
// import dependency which we try with the reorderd remaining imports
|
||||
QStringList reorderedImports;
|
||||
std::copy_if(imports.cbegin(), imports.cend(), std::back_inserter(reorderedImports),
|
||||
[&import, &alreadyTestedImports] (const QString &checkForResortingImport){
|
||||
if (checkForResortingImport == import)
|
||||
return false;
|
||||
return !alreadyTestedImports.contains(checkForResortingImport);
|
||||
});
|
||||
reorderedImports.append(import);
|
||||
sortFilterImports(reorderedImports, workingImports, failedImports, url, errorMessage);
|
||||
} else {
|
||||
Q_ASSERT(!failedImports->contains(import));
|
||||
failedImports->append(import);
|
||||
if (errorMessage)
|
||||
errorMessage->append(lastErrorMessage);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
namespace QmlDesigner {
|
||||
|
||||
@@ -415,32 +452,17 @@ void NodeInstanceServer::setupImports(const QVector<AddImportContainer> &contain
|
||||
QStringList workingImportStatementList;
|
||||
|
||||
// check possible import statements combinations
|
||||
QString errorMessage;
|
||||
// maybe it just works
|
||||
// but first try the current order -> maybe it just works
|
||||
if (testImportStatements(importStatementList, fileUrl())) {
|
||||
workingImportStatementList = importStatementList;
|
||||
} else {
|
||||
QString firstWorkingImportStatement; //usually this will be "import QtQuick x.x"
|
||||
QStringList otherImportStatements;
|
||||
foreach (const QString &importStatement, importStatementList) {
|
||||
if (testImportStatements(QStringList(importStatement), fileUrl()))
|
||||
firstWorkingImportStatement = importStatement;
|
||||
else
|
||||
otherImportStatements.append(importStatement);
|
||||
}
|
||||
|
||||
// find the bad imports from otherImportStatements
|
||||
foreach (const QString &importStatement, otherImportStatements) {
|
||||
if (testImportStatements(QStringList(firstWorkingImportStatement) <<
|
||||
importStatement, fileUrl(), &errorMessage)) {
|
||||
workingImportStatementList.append(importStatement);
|
||||
}
|
||||
}
|
||||
workingImportStatementList.prepend(firstWorkingImportStatement);
|
||||
QString errorMessage;
|
||||
QStringList failedImportList;
|
||||
sortFilterImports(importStatementList, &workingImportStatementList, &failedImportList, fileUrl(), &errorMessage);
|
||||
if (!errorMessage.isEmpty())
|
||||
sendDebugOutput(DebugOutputCommand::WarningType, errorMessage);
|
||||
}
|
||||
|
||||
if (!errorMessage.isEmpty())
|
||||
sendDebugOutput(DebugOutputCommand::WarningType, errorMessage);
|
||||
setupOnlyWorkingImports(workingImportStatementList);
|
||||
}
|
||||
|
||||
|
@@ -165,8 +165,10 @@ void QuickItemNodeInstance::initialize(const ObjectNodeInstance::Pointer &object
|
||||
quickItem()->setParentItem(qobject_cast<QQuickItem*>(nodeInstanceServer()->quickView()->rootObject()));
|
||||
}
|
||||
|
||||
if (s_createEffectItem || instanceId() == 0)
|
||||
designerSupport()->refFromEffectItem(quickItem());
|
||||
if (quickItem()->window()) {
|
||||
if (s_createEffectItem || instanceId() == 0)
|
||||
designerSupport()->refFromEffectItem(quickItem());
|
||||
}
|
||||
|
||||
ObjectNodeInstance::initialize(objectNodeInstance);
|
||||
quickItem()->update();
|
||||
@@ -582,8 +584,7 @@ void QuickItemNodeInstance::reparent(const ObjectNodeInstance::Pointer &oldParen
|
||||
|
||||
if (quickItem()->parentItem()) {
|
||||
refresh();
|
||||
if (quickItem()->window())
|
||||
DesignerSupport::updateDirtyNode(quickItem());
|
||||
DesignerSupport::updateDirtyNode(quickItem());
|
||||
|
||||
if (instanceIsValidLayoutable(oldParentInstance, oldParentProperty))
|
||||
oldParentInstance->refreshLayoutable();
|
||||
|
@@ -51,6 +51,7 @@ ConnectionClient::ConnectionClient()
|
||||
|
||||
connectLocalSocketError();
|
||||
connectLocalSocketConnected();
|
||||
connectLocalSocketDisconnected();
|
||||
}
|
||||
|
||||
void ConnectionClient::startProcessAndConnectToServerAsynchronously()
|
||||
@@ -230,6 +231,14 @@ void ConnectionClient::connectLocalSocketConnected()
|
||||
&ConnectionClient::resetProcessIsStarting);
|
||||
}
|
||||
|
||||
void ConnectionClient::connectLocalSocketDisconnected()
|
||||
{
|
||||
connect(&localSocket,
|
||||
&QLocalSocket::disconnected,
|
||||
this,
|
||||
&ConnectionClient::disconnectedFromLocalSocket);
|
||||
}
|
||||
|
||||
void ConnectionClient::finishProcess()
|
||||
{
|
||||
finishProcess(std::move(process_));
|
||||
|
@@ -76,6 +76,7 @@ public:
|
||||
|
||||
signals:
|
||||
void connectedToLocalSocket();
|
||||
void disconnectedFromLocalSocket();
|
||||
void processFinished();
|
||||
|
||||
protected:
|
||||
@@ -102,6 +103,7 @@ private:
|
||||
void printStandardError();
|
||||
|
||||
void connectLocalSocketConnected();
|
||||
void connectLocalSocketDisconnected();
|
||||
void connectProcessFinished(QProcess *process) const;
|
||||
void connectProcessStarted(QProcess *process) const;
|
||||
void disconnectProcessFinished(QProcess *process) const;
|
||||
|
@@ -35,6 +35,7 @@
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
#include <Python.h>
|
||||
#include "pycdbextmodule.h"
|
||||
#endif
|
||||
|
||||
// wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;'
|
||||
@@ -160,7 +161,10 @@ HRESULT ExtensionContext::initialize(PULONG Version, PULONG Flags)
|
||||
*Flags = 0;
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
initCdbextPythonModule();
|
||||
Py_Initialize();
|
||||
PyRun_SimpleString("import cdbext");
|
||||
PyRun_SimpleString("import sys");
|
||||
#endif
|
||||
|
||||
IInterfacePointer<CIDebugClient> client;
|
||||
|
187
src/libs/qtcreatorcdbext/pycdbextmodule.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "pycdbextmodule.h"
|
||||
|
||||
#include "extensioncontext.h"
|
||||
#include "symbolgroup.h"
|
||||
|
||||
#include "pyfield.h"
|
||||
#include "pystdoutredirect.h"
|
||||
#include "pytype.h"
|
||||
#include "pyvalue.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
|
||||
// cdbext python module
|
||||
static PyObject *cdbext_parseAndEvaluate(PyObject *, PyObject *args) // -> Value
|
||||
{
|
||||
char *expr;
|
||||
if (!PyArg_ParseTuple(args, "s", &expr))
|
||||
return NULL;
|
||||
CIDebugControl *control = ExtensionCommandContext::instance()->control();
|
||||
control->SetExpressionSyntax(DEBUG_EXPR_CPLUSPLUS);
|
||||
DEBUG_VALUE value;
|
||||
if (FAILED(control->Evaluate(expr, DEBUG_VALUE_INT64, &value, NULL)))
|
||||
Py_RETURN_NONE;
|
||||
return Py_BuildValue("K", value.I64);
|
||||
}
|
||||
|
||||
static PyObject *cdbext_lookupType(PyObject *, PyObject *args) // -> Type
|
||||
{
|
||||
char *type;
|
||||
if (!PyArg_ParseTuple(args, "s", &type))
|
||||
return NULL;
|
||||
return lookupType(type);
|
||||
}
|
||||
|
||||
static PyObject *cdbext_listOfLocals(PyObject *, PyObject *) // -> [ Value ]
|
||||
{
|
||||
ExtensionCommandContext *extCmdCtx = ExtensionCommandContext::instance();
|
||||
ULONG frame;
|
||||
if (FAILED(extCmdCtx->symbols()->GetCurrentScopeFrameIndex(&frame)))
|
||||
return NULL;
|
||||
|
||||
std::string errorMessage;
|
||||
LocalsSymbolGroup *sg = ExtensionContext::instance().symbolGroup(
|
||||
extCmdCtx->symbols(), extCmdCtx->threadId(), int(frame), &errorMessage);
|
||||
if (!sg)
|
||||
return NULL;
|
||||
|
||||
const auto children = sg->root()->children();
|
||||
auto locals = PyList_New(0);
|
||||
for (AbstractSymbolGroupNode *abstractChild : children) {
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
if (SymbolGroupNode* child = abstractChild->asSymbolGroupNode()) {
|
||||
childValue->m_index = child->index();
|
||||
childValue->m_symbolGroup = sg->debugSymbolGroup();
|
||||
}
|
||||
}
|
||||
PyList_Append(locals, reinterpret_cast<PyObject*>(childValue));
|
||||
}
|
||||
|
||||
return locals;
|
||||
}
|
||||
|
||||
static PyObject *cdbext_pointerSize(PyObject *, PyObject *)
|
||||
{
|
||||
HRESULT isPointer64Bit = ExtensionCommandContext::instance()->control()->IsPointer64Bit();
|
||||
return Py_BuildValue("i", isPointer64Bit == S_OK ? 8 : 4);
|
||||
}
|
||||
|
||||
static PyObject *cdbext_readRawMemory(PyObject *, PyObject *args)
|
||||
{
|
||||
ULONG64 address = 0;
|
||||
ULONG size = 0;
|
||||
if (!PyArg_ParseTuple(args, "Kk", &address, &size))
|
||||
return NULL;
|
||||
|
||||
char *buffer = new char[size];
|
||||
|
||||
CIDebugDataSpaces *data = ExtensionCommandContext::instance()->dataSpaces();
|
||||
ULONG bytesWritten = 0;
|
||||
HRESULT hr = data->ReadVirtual(address, buffer, size, &bytesWritten);
|
||||
if (FAILED(hr))
|
||||
bytesWritten = 0;
|
||||
PyObject *ret = Py_BuildValue("y#", buffer, bytesWritten);
|
||||
delete[] buffer;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyMethodDef cdbextMethods[] = {
|
||||
{"parseAndEvaluate", cdbext_parseAndEvaluate, METH_VARARGS,
|
||||
"Returns value of expression or None if the expression can not be resolved"},
|
||||
{"lookupType", cdbext_lookupType, METH_VARARGS,
|
||||
"Returns type object or None if the type can not be resolved"},
|
||||
{"listOfLocals", cdbext_listOfLocals, METH_NOARGS,
|
||||
"Returns list of values that are currently in scope"},
|
||||
{"pointerSize", cdbext_pointerSize, METH_NOARGS,
|
||||
"Returns the size of a pointer"},
|
||||
{"readRawMemory", cdbext_readRawMemory, METH_VARARGS,
|
||||
"Read a block of data from the virtual address space"},
|
||||
{NULL, NULL, 0,
|
||||
NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static struct PyModuleDef cdbextModule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"cdbext", /* name of module */
|
||||
"bridge to the creator cdb extension", /* module documentation */
|
||||
-1, /* size of per-interpreter state of the module,
|
||||
or -1 if the module keeps state in global variables. */
|
||||
cdbextMethods
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_cdbext(void)
|
||||
{
|
||||
if (PyType_Ready(field_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
if (PyType_Ready(type_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
if (PyType_Ready(value_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
stdoutRedirect_pytype()->tp_new = PyType_GenericNew;
|
||||
if (PyType_Ready(stdoutRedirect_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
PyObject *module = PyModule_Create(&cdbextModule);
|
||||
if (module == NULL)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(field_pytype());
|
||||
Py_INCREF(stdoutRedirect_pytype());
|
||||
Py_INCREF(type_pytype());
|
||||
Py_INCREF(value_pytype());
|
||||
|
||||
PyModule_AddObject(module, "Field",
|
||||
reinterpret_cast<PyObject *>(field_pytype()));
|
||||
PyModule_AddObject(module, "StdoutRedirect",
|
||||
reinterpret_cast<PyObject *>(stdoutRedirect_pytype()));
|
||||
PyModule_AddObject(module, "Type",
|
||||
reinterpret_cast<PyObject *>(type_pytype()));
|
||||
PyModule_AddObject(module, "Value",
|
||||
reinterpret_cast<PyObject *>(value_pytype()));
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
void initCdbextPythonModule()
|
||||
{
|
||||
PyImport_AppendInittab("cdbext", PyInit_cdbext);
|
||||
}
|
||||
|
||||
PyObject *pyBool(bool b)
|
||||
{
|
||||
if (b)
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
}
|
40
src/libs/qtcreatorcdbext/pycdbextmodule.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <Python.h>
|
||||
|
||||
void initCdbextPythonModule();
|
||||
|
||||
PyObject *pyBool(bool);
|
||||
|
||||
/* TODO's
|
||||
class Field:
|
||||
isBaseClass() -> bool # Whether this is a base class or normal member
|
||||
|
||||
parseAndEvaluate(string: expr) -> Value # or None if not possible.
|
||||
|
||||
*/
|
167
src/libs/qtcreatorcdbext/pyfield.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "pyfield.h"
|
||||
|
||||
#include "pytype.h"
|
||||
#include "extensioncontext.h"
|
||||
|
||||
PyObject *field_Name(Field *self)
|
||||
{
|
||||
return Py_BuildValue("s", self->m_name);
|
||||
}
|
||||
|
||||
PyObject *field_isBaseClass(Field *)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool initTypeAndOffset(Field *field)
|
||||
{
|
||||
auto extcmd = ExtensionCommandContext::instance();
|
||||
field->m_initialized = SUCCEEDED(extcmd->symbols()->GetFieldTypeAndOffset(
|
||||
field->m_module, field->m_parentTypeId,
|
||||
field->m_name, &field->m_typeId, &field->m_offset));
|
||||
return field->m_initialized;
|
||||
}
|
||||
|
||||
PyObject *field_Type(Field *self)
|
||||
{
|
||||
if (!self->m_initialized)
|
||||
if (!initTypeAndOffset(self))
|
||||
return NULL;
|
||||
return createType(self->m_module, self->m_typeId);
|
||||
}
|
||||
|
||||
PyObject *field_ParentType(Field *self)
|
||||
{
|
||||
return createType(self->m_module, self->m_parentTypeId);
|
||||
}
|
||||
|
||||
PyObject *field_Bitsize(Field *self)
|
||||
{
|
||||
if (!self->m_initialized)
|
||||
if (!initTypeAndOffset(self))
|
||||
return NULL;
|
||||
ULONG byteSize;
|
||||
auto extcmd = ExtensionCommandContext::instance();
|
||||
if (FAILED(extcmd->symbols()->GetTypeSize(self->m_module, self->m_typeId, &byteSize)))
|
||||
return NULL;
|
||||
return Py_BuildValue("k", byteSize * 8);
|
||||
}
|
||||
|
||||
PyObject *field_Bitpos(Field *self)
|
||||
{
|
||||
if (!self->m_initialized)
|
||||
if (!initTypeAndOffset(self))
|
||||
return NULL;
|
||||
return Py_BuildValue("k", self->m_offset * 8);
|
||||
}
|
||||
|
||||
PyObject *field_New(PyTypeObject *type, PyObject *, PyObject *)
|
||||
{
|
||||
Field *self = reinterpret_cast<Field *>(type->tp_alloc(type, 0));
|
||||
if (self != NULL)
|
||||
initField(self);
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void field_Dealloc(Field *self)
|
||||
{
|
||||
delete[] self->m_name;
|
||||
}
|
||||
|
||||
void initField(Field *field)
|
||||
{
|
||||
field->m_name = 0;
|
||||
field->m_initialized = false;
|
||||
field->m_typeId = 0;
|
||||
field->m_offset = 0;
|
||||
field->m_module = 0;
|
||||
field->m_parentTypeId = 0;
|
||||
}
|
||||
|
||||
static PyMethodDef fieldMethods[] = {
|
||||
{"name", PyCFunction(field_Name), METH_NOARGS,
|
||||
"Return the name of this field or None for anonymous fields"},
|
||||
{"isBaseClass", PyCFunction(field_isBaseClass), METH_NOARGS,
|
||||
"Whether this is a base class or normal member"},
|
||||
{"type", PyCFunction(field_Type), METH_NOARGS,
|
||||
"Type of this member"},
|
||||
{"parentType", PyCFunction(field_ParentType), METH_NOARGS,
|
||||
"Type of class this member belongs to"},
|
||||
{"bitsize", PyCFunction(field_Bitsize), METH_NOARGS,
|
||||
"Size of member in bits"},
|
||||
{"bitpos", PyCFunction(field_Bitpos), METH_NOARGS,
|
||||
"Offset of member in parent type in bits"},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject *field_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_FieldType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.Field", /* tp_name */
|
||||
sizeof(Field), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)field_Dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_as_async */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"Field objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
fieldMethods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
field_New, /* tp_new */
|
||||
};
|
||||
|
||||
return &cdbext_FieldType;
|
||||
}
|
46
src/libs/qtcreatorcdbext/pyfield.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <Python.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Field
|
||||
{
|
||||
PyObject_HEAD
|
||||
const char *m_name; // owned
|
||||
bool m_initialized;
|
||||
unsigned long m_typeId;
|
||||
unsigned long m_offset;
|
||||
unsigned long m_parentTypeId;
|
||||
ULONG64 m_module;
|
||||
};
|
||||
|
||||
PyTypeObject *field_pytype();
|
||||
|
||||
void initField(Field *field);
|
||||
bool initTypeAndOffset(Field *field);
|
116
src/libs/qtcreatorcdbext/pystdoutredirect.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "pystdoutredirect.h"
|
||||
|
||||
static std::string output;
|
||||
static PyObject *stdoutRedirect = nullptr;
|
||||
|
||||
struct StdoutRedirect
|
||||
{
|
||||
PyObject_HEAD
|
||||
};
|
||||
|
||||
PyObject *stdoutRedirect_write(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char *string;
|
||||
PyArg_ParseTuple(args, "s", &string);
|
||||
output += string;
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
PyObject *stdoutRedirect_flush(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
static PyMethodDef StdoutRedirectMethods[] =
|
||||
{
|
||||
{"write", stdoutRedirect_write, METH_VARARGS, "sys.stdout.write"},
|
||||
{"flush", stdoutRedirect_flush, METH_VARARGS, "sys.stdout.flush"},
|
||||
{0, 0, 0, 0} // sentinel
|
||||
};
|
||||
|
||||
PyTypeObject *stdoutRedirect_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_StdoutRedirectType =
|
||||
{
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.StdoutRedirect", /* tp_name */
|
||||
sizeof(StdoutRedirect), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
0, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"stdout redirector", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
StdoutRedirectMethods, /* tp_methods */
|
||||
};
|
||||
return &cdbext_StdoutRedirectType;
|
||||
}
|
||||
|
||||
void startCapturePyStdout()
|
||||
{
|
||||
if (stdoutRedirect != nullptr)
|
||||
endCapturePyStdout();
|
||||
stdoutRedirect = _PyObject_New(stdoutRedirect_pytype());
|
||||
if (PySys_SetObject("stdout", stdoutRedirect) != 0)
|
||||
PySys_SetObject("stdout", PySys_GetObject("__stdout__"));
|
||||
if (PySys_SetObject("stderr", stdoutRedirect) != 0)
|
||||
PySys_SetObject("stderr", PySys_GetObject("__stderr__"));
|
||||
PyObject_CallMethod(stdoutRedirect, "write", "s", "text");
|
||||
output.clear();
|
||||
}
|
||||
|
||||
std::string getPyStdout()
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
void endCapturePyStdout()
|
||||
{
|
||||
PySys_SetObject("stdout", PySys_GetObject("__stdout__"));
|
||||
PySys_SetObject("stderr", PySys_GetObject("__stderr__"));
|
||||
Py_DecRef(stdoutRedirect);
|
||||
stdoutRedirect = nullptr;
|
||||
}
|
35
src/libs/qtcreatorcdbext/pystdoutredirect.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <Python.h>
|
||||
#include <string>
|
||||
|
||||
PyTypeObject *stdoutRedirect_pytype();
|
||||
|
||||
void startCapturePyStdout();
|
||||
std::string getPyStdout();
|
||||
void endCapturePyStdout();
|
356
src/libs/qtcreatorcdbext/pytype.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "pytype.h"
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "extensioncontext.h"
|
||||
#include "pycdbextmodule.h"
|
||||
#include "pyfield.h"
|
||||
#include "stringutils.h"
|
||||
#include "symbolgroupvalue.h"
|
||||
|
||||
enum TypeCodes {
|
||||
TypeCodeTypedef,
|
||||
TypeCodeStruct,
|
||||
TypeCodeVoid,
|
||||
TypeCodeIntegral,
|
||||
TypeCodeFloat,
|
||||
TypeCodeEnum,
|
||||
TypeCodePointer,
|
||||
TypeCodeArray,
|
||||
TypeCodeComplex,
|
||||
TypeCodeReference,
|
||||
TypeCodeFunction,
|
||||
TypeCodeMemberPointer,
|
||||
TypeCodeFortranString
|
||||
};
|
||||
|
||||
PyObject *lookupType(const std::string &typeNameIn)
|
||||
{
|
||||
std::string typeName = typeNameIn;
|
||||
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG64 module;
|
||||
ULONG typeId;
|
||||
if (FAILED(symbols->GetSymbolTypeId(typeName.c_str(), &typeId, &module)))
|
||||
Py_RETURN_NONE;
|
||||
return createType(module, typeId);
|
||||
}
|
||||
|
||||
char *getTypeName(ULONG64 module, ULONG typeId)
|
||||
{
|
||||
char *typeName = 0;
|
||||
auto symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG size = 0;
|
||||
symbols->GetTypeName(module, typeId, NULL, 0, &size);
|
||||
if (size > 0) {
|
||||
typeName = new char[size];
|
||||
if (FAILED(symbols->GetTypeName(module, typeId, typeName, size, &size))) {
|
||||
delete[] typeName;
|
||||
typeName = new char[1];
|
||||
typeName[0] = 0;
|
||||
}
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
const char *getTypeName(Type *type)
|
||||
{
|
||||
if (type->m_name == 0)
|
||||
type->m_name = getTypeName(type->m_module, type->m_typeId);
|
||||
return type->m_name;
|
||||
}
|
||||
|
||||
PyObject *type_Name(Type *self)
|
||||
{
|
||||
return Py_BuildValue("s", getTypeName(self));
|
||||
}
|
||||
|
||||
PyObject *type_bitSize(Type *self)
|
||||
{
|
||||
ULONG size;
|
||||
auto extcmd = ExtensionCommandContext::instance();
|
||||
if (FAILED(extcmd->symbols()->GetTypeSize(self->m_module, self->m_typeId, &size)))
|
||||
return NULL;
|
||||
return Py_BuildValue("k", size * 8);
|
||||
}
|
||||
|
||||
bool isType(const std::string &typeName, const std::vector<std::string> &types)
|
||||
{
|
||||
return std::find(types.begin(), types.end(), typeName) != types.end();
|
||||
}
|
||||
|
||||
PyObject *type_Code(Type *self)
|
||||
{
|
||||
static const std::vector<std::string> integralTypes({"bool",
|
||||
"char", "unsigned char", "char16_t", "char32_t", "wchar_t",
|
||||
"short", "unsigned short", "int", "unsigned int",
|
||||
"long", "unsigned long", "int64", "unsigned int64"});
|
||||
static const std::vector<std::string> floatTypes({"float", "double"});
|
||||
|
||||
TypeCodes code = TypeCodeStruct;
|
||||
const char *typeNameCstr = getTypeName(self);
|
||||
if (typeNameCstr == 0)
|
||||
Py_RETURN_NONE;
|
||||
const std::string typeName(typeNameCstr);
|
||||
if (SymbolGroupValue::isArrayType(typeName))
|
||||
code = TypeCodeArray;
|
||||
else if (endsWith(typeName, "*"))
|
||||
code = TypeCodePointer;
|
||||
else if (typeName.find("<function>") != std::string::npos)
|
||||
code = TypeCodeFunction;
|
||||
else if (isType(typeName, integralTypes))
|
||||
code = TypeCodeIntegral;
|
||||
else if (isType(typeName, floatTypes))
|
||||
code = TypeCodeFloat;
|
||||
|
||||
return Py_BuildValue("k", code);
|
||||
}
|
||||
|
||||
PyObject *type_Unqualified(Type *self)
|
||||
{
|
||||
Py_XINCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
PyObject *type_Target(Type *self)
|
||||
{
|
||||
std::string typeName(getTypeName(self));
|
||||
if (!endsWith(typeName, "*")) {
|
||||
Py_XINCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
typeName = typeName.substr(0, typeName.length() - 1);
|
||||
|
||||
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG typeId;
|
||||
if (FAILED(symbols->GetTypeId(self->m_module, typeName.c_str(), &typeId)))
|
||||
return NULL;
|
||||
return createType(self->m_module, typeId);
|
||||
}
|
||||
|
||||
PyObject *type_StripTypedef(Type *self)
|
||||
{
|
||||
Py_XINCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
PyObject *type_Fields(Type *self)
|
||||
{
|
||||
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
|
||||
auto fields = PyList_New(0);
|
||||
for (ULONG fieldIndex = 0;; ++fieldIndex) {
|
||||
ULONG fieldNameSize = 0;
|
||||
symbols->GetFieldName(self->m_module, self->m_typeId, fieldIndex, NULL, 0, &fieldNameSize);
|
||||
if (fieldNameSize == 0)
|
||||
break;
|
||||
char *name = new char[fieldNameSize];
|
||||
if (FAILED(symbols->GetFieldName(self->m_module, self->m_typeId, fieldIndex, name,
|
||||
fieldNameSize, NULL))) {
|
||||
delete[] name;
|
||||
break;
|
||||
}
|
||||
|
||||
Field *field = PyObject_New(Field, field_pytype());
|
||||
if (field == NULL)
|
||||
return fields;
|
||||
initField(field);
|
||||
field->m_name = name;
|
||||
field->m_parentTypeId = self->m_typeId;
|
||||
field->m_module = self->m_module;
|
||||
PyList_Append(fields, reinterpret_cast<PyObject*>(field));
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
std::vector<std::string> innerTypesOf(const std::string &t)
|
||||
{
|
||||
std::vector<std::string> rc;
|
||||
|
||||
std::string::size_type pos = t.find('<');
|
||||
if (pos == std::string::npos)
|
||||
return rc;
|
||||
|
||||
rc.reserve(5);
|
||||
const std::string::size_type size = t.size();
|
||||
// Record all elements of level 1 to work correctly for
|
||||
// 'std::map<std::basic_string< .. > >'
|
||||
unsigned level = 0;
|
||||
std::string::size_type start = 0;
|
||||
for ( ; pos < size ; pos++) {
|
||||
const char c = t.at(pos);
|
||||
switch (c) {
|
||||
case '<':
|
||||
if (++level == 1)
|
||||
start = pos + 1;
|
||||
break;
|
||||
case '>':
|
||||
if (--level == 0) { // last element
|
||||
std::string innerType = t.substr(start, pos - start);
|
||||
trimFront(innerType);
|
||||
trimBack(innerType);
|
||||
rc.push_back(innerType);
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if (level == 1) { // std::map<a, b>: start anew at ','.
|
||||
std::string innerType = t.substr(start, pos - start);
|
||||
trimFront(innerType);
|
||||
trimBack(innerType);
|
||||
rc.push_back(innerType);
|
||||
start = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
PyObject *type_TemplateArgument(Type *self, PyObject *args)
|
||||
{
|
||||
unsigned int index;
|
||||
bool numeric;
|
||||
if (!PyArg_ParseTuple(args, "Ib", &index, &numeric))
|
||||
return NULL;
|
||||
|
||||
std::vector<std::string> innerTypes = innerTypesOf(getTypeName(self));
|
||||
if (innerTypes.size() <= index)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
const std::string &innerType = innerTypes.at(index);
|
||||
if (numeric) {
|
||||
try {
|
||||
return Py_BuildValue("i", std::stoi(innerType));
|
||||
}
|
||||
catch (std::invalid_argument) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return lookupType(innerType);
|
||||
}
|
||||
|
||||
PyObject *type_New(PyTypeObject *type, PyObject *, PyObject *)
|
||||
{
|
||||
Type *self = reinterpret_cast<Type *>(type->tp_alloc(type, 0));
|
||||
if (self != NULL) {
|
||||
self->m_name = nullptr;
|
||||
self->m_typeId = 0;
|
||||
self->m_module = 0;
|
||||
}
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void type_Dealloc(Type *self)
|
||||
{
|
||||
delete[] self->m_name;
|
||||
}
|
||||
|
||||
PyObject *createType(ULONG64 module, ULONG typeId)
|
||||
{
|
||||
Type *type = PyObject_New(Type, type_pytype());
|
||||
type->m_module = module;
|
||||
type->m_typeId = typeId;
|
||||
type->m_name = nullptr;
|
||||
return reinterpret_cast<PyObject *>(type);
|
||||
}
|
||||
|
||||
static PyMethodDef typeMethods[] = {
|
||||
{"name", PyCFunction(type_Name), METH_NOARGS,
|
||||
"Return the type name"},
|
||||
{"bitsize", PyCFunction(type_bitSize), METH_NOARGS,
|
||||
"Return the size of the type in bits"},
|
||||
{"code", PyCFunction(type_Code), METH_NOARGS,
|
||||
"Return type code"},
|
||||
{"unqualified", PyCFunction(type_Unqualified), METH_NOARGS,
|
||||
"Type without const/volatile"},
|
||||
{"target", PyCFunction(type_Target), METH_NOARGS,
|
||||
"Type dereferenced if it is a pointer type, element if array etc"},
|
||||
{"stripTypedef", PyCFunction(type_StripTypedef), METH_NOARGS,
|
||||
"Type with typedefs removed"},
|
||||
{"fields", PyCFunction(type_Fields), METH_NOARGS,
|
||||
"List of fields (member and base classes) of this type"},
|
||||
|
||||
{"templateArgument", PyCFunction(type_TemplateArgument), METH_VARARGS,
|
||||
"Returns template argument at position"},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef typeMembers[] = {
|
||||
{const_cast<char *>("id"), T_ULONG, offsetof(Type, m_typeId), 0,
|
||||
const_cast<char *>("type id")},
|
||||
{const_cast<char *>("moduleBase"), T_ULONGLONG, offsetof(Type, m_module), 0,
|
||||
const_cast<char *>("module base address")},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject *type_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_TypeType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.Type", /* tp_name */
|
||||
sizeof(Type), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)type_Dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_as_async */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"Type objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
typeMethods, /* tp_methods */
|
||||
typeMembers, /* tp_members (just for debugging)*/
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
type_New, /* tp_new */
|
||||
};
|
||||
|
||||
return &cdbext_TypeType;
|
||||
}
|
47
src/libs/qtcreatorcdbext/pytype.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "symbolgroup.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include "structmember.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Type
|
||||
{
|
||||
PyObject_HEAD
|
||||
ULONG m_typeId;
|
||||
ULONG64 m_module;
|
||||
char *m_name; // owned
|
||||
};
|
||||
|
||||
PyTypeObject *type_pytype();
|
||||
char *getTypeName(ULONG64 module, ULONG typeId);
|
||||
|
||||
PyObject *lookupType(const std::string &typeName);
|
||||
PyObject *createType(ULONG64 module, ULONG typeId);
|
372
src/libs/qtcreatorcdbext/pyvalue.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "pyvalue.h"
|
||||
|
||||
#include "extensioncontext.h"
|
||||
#include "pycdbextmodule.h"
|
||||
#include "pytype.h"
|
||||
#include "pyfield.h"
|
||||
#include "stringutils.h"
|
||||
|
||||
std::string getSymbolName(CIDebugSymbolGroup *sg, ULONG index)
|
||||
{
|
||||
ULONG size = 0;
|
||||
sg->GetSymbolName(index, NULL, 0, &size);
|
||||
if (size == 0)
|
||||
return std::string();
|
||||
std::string name(size, '\0');
|
||||
sg->GetSymbolName(index, &name[0], size, &size);
|
||||
return name;
|
||||
}
|
||||
|
||||
PyObject *value_Name(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
const std::string &symbolName = getSymbolName(self->m_symbolGroup, self->m_index);
|
||||
if (symbolName.empty())
|
||||
Py_RETURN_NONE;
|
||||
return Py_BuildValue("s", symbolName.c_str());
|
||||
}
|
||||
|
||||
PyObject *value_Type(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
const HRESULT hr = self->m_symbolGroup->GetSymbolParameters(self->m_index, 1, ¶ms);
|
||||
if (FAILED(hr))
|
||||
return NULL;
|
||||
return createType(params.Module, params.TypeId);
|
||||
}
|
||||
|
||||
PyObject *value_AsBytes(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
ULONG64 address = 0;
|
||||
if (FAILED(self->m_symbolGroup->GetSymbolOffset(self->m_index, &address)))
|
||||
return NULL;
|
||||
ULONG size;
|
||||
if (FAILED(self->m_symbolGroup->GetSymbolSize(self->m_index, &size)))
|
||||
return NULL;
|
||||
|
||||
char *buffer = new char[size];
|
||||
auto data = ExtensionCommandContext::instance()->dataSpaces();
|
||||
ULONG received = 0;
|
||||
if (FAILED(data->ReadVirtual(address, buffer, size, &received)))
|
||||
return NULL;
|
||||
|
||||
return PyByteArray_FromStringAndSize(buffer, received);
|
||||
}
|
||||
|
||||
ULONG64 valueAddress(Value *value)
|
||||
{
|
||||
ULONG64 address = 0;
|
||||
if (value->m_symbolGroup)
|
||||
value->m_symbolGroup->GetSymbolOffset(value->m_index, &address);
|
||||
return address;
|
||||
}
|
||||
|
||||
PyObject *value_Address(Value *self)
|
||||
{
|
||||
const ULONG64 address = valueAddress(self);
|
||||
return address == 0 ? NULL : Py_BuildValue("K", address);
|
||||
}
|
||||
|
||||
bool expandValue(Value *v)
|
||||
{
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
if (FAILED(v->m_symbolGroup->GetSymbolParameters(v->m_index, 1, ¶ms)))
|
||||
return false;
|
||||
if (params.Flags & DEBUG_SYMBOL_EXPANDED)
|
||||
return true;
|
||||
return SUCCEEDED(v->m_symbolGroup->ExpandSymbol(v->m_index, TRUE));
|
||||
}
|
||||
|
||||
ULONG numberOfChildren(Value *v)
|
||||
{
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
HRESULT hr = v->m_symbolGroup->GetSymbolParameters(v->m_index, 1, ¶ms);
|
||||
return SUCCEEDED(hr) ? params.SubElements : 0;
|
||||
}
|
||||
|
||||
PyObject *value_Dereference(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
const HRESULT hr = self->m_symbolGroup->GetSymbolParameters(self->m_index, 1, ¶ms);
|
||||
if (FAILED(hr))
|
||||
return NULL;
|
||||
|
||||
char *name = getTypeName(params.Module, params.TypeId);
|
||||
|
||||
Value *ret = self;
|
||||
if (endsWith(std::string(name), "*")) {
|
||||
if (numberOfChildren(self) > 0 && expandValue(self)) {
|
||||
ULONG symbolCount = 0;
|
||||
self->m_symbolGroup->GetNumberSymbols(&symbolCount);
|
||||
if (symbolCount > self->m_index + 1) {
|
||||
ret = PyObject_New(Value, value_pytype());
|
||||
if (ret != NULL) {
|
||||
ret->m_index = self->m_index + 1;
|
||||
ret->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] name;
|
||||
return reinterpret_cast<PyObject*>(ret);
|
||||
}
|
||||
|
||||
PyObject *value_HasChildren(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
return pyBool(numberOfChildren(self) != 0);
|
||||
}
|
||||
|
||||
PyObject *value_Expand(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
return pyBool(expandValue(self));
|
||||
}
|
||||
|
||||
PyObject *value_NativeDebuggerValue(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
ULONG size = 0;
|
||||
self->m_symbolGroup->GetSymbolValueText(self->m_index, NULL, 0, &size);
|
||||
char *name = new char[size];
|
||||
if (FAILED(self->m_symbolGroup->GetSymbolValueText(self->m_index, name, size, &size))) {
|
||||
delete[] name;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
PyObject *ret = Py_BuildValue("s", name);
|
||||
delete[] name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *value_ChildFromName(Value *self, PyObject *args)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
char *name;
|
||||
if (!PyArg_ParseTuple(args, "s", &name))
|
||||
return NULL;
|
||||
|
||||
const ULONG childCount = numberOfChildren(self);
|
||||
if (childCount == 0 || !expandValue(self))
|
||||
Py_RETURN_NONE;
|
||||
|
||||
for (ULONG childIndex = self->m_index + 1 ; childIndex <= self->m_index + childCount; ++childIndex) {
|
||||
if (getSymbolName(self->m_symbolGroup, childIndex) == name) {
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
childValue->m_index = childIndex;
|
||||
childValue->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
return reinterpret_cast<PyObject*>(childValue);
|
||||
}
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
std::string pointedToSymbolName(ULONG64 address, const std::string &type)
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << "*(" << type;
|
||||
if (!type.empty() && type.at(type.size() - 1) == '*')
|
||||
str << ' ';
|
||||
str << "*)" << std::showbase << std::hex << address;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
PyObject *value_ChildFromField(Value *self, PyObject *args)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
Field *field;
|
||||
if (!PyArg_ParseTuple(args, "O", &field))
|
||||
return NULL;
|
||||
|
||||
if (!field->m_initialized && !initTypeAndOffset(field))
|
||||
return NULL;
|
||||
|
||||
ULONG64 address = valueAddress(self);
|
||||
if (address == 0)
|
||||
return NULL;
|
||||
address += field->m_offset;
|
||||
|
||||
auto symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG childTypeNameSize = 0;
|
||||
symbols->GetTypeName(field->m_module, field->m_typeId, NULL, 0, &childTypeNameSize);
|
||||
std::string childTypeName(childTypeNameSize, '\0');
|
||||
symbols->GetTypeName(field->m_module, field->m_typeId, &childTypeName[0],
|
||||
childTypeNameSize, &childTypeNameSize);
|
||||
|
||||
if (childTypeName.empty())
|
||||
return NULL;
|
||||
|
||||
std::string name = pointedToSymbolName(address, childTypeName);
|
||||
ULONG index = DEBUG_ANY_ID;
|
||||
if (FAILED(self->m_symbolGroup->AddSymbol(name.c_str(), &index)))
|
||||
return NULL;
|
||||
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
childValue->m_index = index;
|
||||
childValue->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
return reinterpret_cast<PyObject*>(childValue);
|
||||
}
|
||||
|
||||
PyObject *value_ChildFromIndex(Value *self, PyObject *args)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
unsigned int index;
|
||||
if (!PyArg_ParseTuple(args, "I", &index))
|
||||
return NULL;
|
||||
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
const ULONG childCount = numberOfChildren(self);
|
||||
if (childCount <= index || !expandValue(self))
|
||||
Py_RETURN_NONE;
|
||||
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
childValue->m_index = self->m_index + index + 1;
|
||||
childValue->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject*>(childValue);
|
||||
}
|
||||
|
||||
void value_Dealloc(Value *)
|
||||
{ }
|
||||
|
||||
PyObject *value_New(PyTypeObject *type, PyObject *, PyObject *)
|
||||
{
|
||||
Value *self = reinterpret_cast<Value *>(type->tp_alloc(type, 0));
|
||||
if (self != NULL)
|
||||
initValue(self);
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void initValue(Value *value)
|
||||
{
|
||||
value->m_index = 0;
|
||||
value->m_symbolGroup = nullptr;
|
||||
}
|
||||
|
||||
static PyMethodDef valueMethods[] = {
|
||||
{"name", PyCFunction(value_Name), METH_NOARGS,
|
||||
"Name of this thing or None"},
|
||||
{"type", PyCFunction(value_Type), METH_NOARGS,
|
||||
"Type of this value"},
|
||||
{"asBytes", PyCFunction(value_AsBytes), METH_NOARGS,
|
||||
"Memory contents of this object, or None"},
|
||||
{"address", PyCFunction(value_Address), METH_NOARGS,
|
||||
"Address of this object, or None"},
|
||||
{"dereference", PyCFunction(value_Dereference), METH_NOARGS,
|
||||
"Dereference if value is pointer"},
|
||||
{"hasChildren", PyCFunction(value_HasChildren), METH_NOARGS,
|
||||
"Whether this object has subobjects"},
|
||||
{"expand", PyCFunction(value_Expand), METH_NOARGS,
|
||||
"Make sure that children are accessible."},
|
||||
{"nativeDebuggerValue", PyCFunction(value_NativeDebuggerValue), METH_NOARGS,
|
||||
"Value string returned by the debugger"},
|
||||
|
||||
{"childFromName", PyCFunction(value_ChildFromName), METH_VARARGS,
|
||||
"Return the name of this value"},
|
||||
{"childFromField", PyCFunction(value_ChildFromField), METH_VARARGS,
|
||||
"Return the name of this value"},
|
||||
{"childFromIndex", PyCFunction(value_ChildFromIndex), METH_VARARGS,
|
||||
"Return the name of this value"},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef valueMembers[] = {
|
||||
{const_cast<char *>("index"), T_ULONG, offsetof(Value, m_index), 0,
|
||||
const_cast<char *>("value index in symbolgroup")},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject *value_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_ValueType =
|
||||
{
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.Value", /* tp_name */
|
||||
sizeof(Value), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)value_Dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"Value objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
valueMethods, /* tp_methods */
|
||||
valueMembers, /* tp_members (just for debugging)*/
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
value_New, /* tp_new */
|
||||
};
|
||||
|
||||
return &cdbext_ValueType;
|
||||
}
|
42
src/libs/qtcreatorcdbext/pyvalue.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "symbolgroupnode.h"
|
||||
#include "symbolgroup.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include "structmember.h"
|
||||
|
||||
struct Value
|
||||
{
|
||||
PyObject_HEAD
|
||||
ULONG m_index;
|
||||
CIDebugSymbolGroup *m_symbolGroup; // not owned
|
||||
};
|
||||
|
||||
PyTypeObject *value_pytype();
|
||||
void initValue(Value *value);
|
@@ -103,6 +103,20 @@ exists($$PYTHON_INSTALL_DIR) {
|
||||
INCLUDEPATH += $$PYTHON_INSTALL_DIR/include
|
||||
DEPENDPATH += $$PYTHON_INSTALL_DIR/include
|
||||
|
||||
SOURCES += \
|
||||
pycdbextmodule.cpp \
|
||||
pyfield.cpp \
|
||||
pystdoutredirect.cpp \
|
||||
pytype.cpp \
|
||||
pyvalue.cpp
|
||||
|
||||
HEADERS += \
|
||||
pycdbextmodule.h \
|
||||
pyfield.h \
|
||||
pystdoutredirect.h \
|
||||
pytype.h \
|
||||
pyvalue.h
|
||||
|
||||
#TODO: parse version number for a generic approach
|
||||
CONFIG(release, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35
|
||||
else:CONFIG(debug, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35_d
|
||||
|
@@ -33,6 +33,7 @@
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
#include <Python.h>
|
||||
#include "pystdoutredirect.h"
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
@@ -579,26 +580,17 @@ extern "C" HRESULT CALLBACK script(CIDebugClient *client, PCSTR argsIn)
|
||||
for (std::string arg : commandTokens<StringList>(argsIn, &token))
|
||||
command << arg << ' ';
|
||||
|
||||
if (PyRun_SimpleString(command.str().c_str()) == 0) {
|
||||
ExtensionContext::instance().reportLong('R', token, "script", "");
|
||||
} else {
|
||||
ExtensionContext::instance().report('N', token, 0, "script",
|
||||
"Error while executing Python code.");
|
||||
}
|
||||
|
||||
_Py_IDENTIFIER(stdout);
|
||||
_Py_IDENTIFIER(flush);
|
||||
|
||||
PyObject *fout = _PySys_GetObjectId(&PyId_stdout);
|
||||
PyObject *tmp;
|
||||
|
||||
if (fout != NULL && fout != Py_None) {
|
||||
tmp = _PyObject_CallMethodId(fout, &PyId_flush, "");
|
||||
if (tmp == NULL)
|
||||
PyErr_WriteUnraisable(fout);
|
||||
else
|
||||
Py_DECREF(tmp);
|
||||
}
|
||||
PyObject *ptype = NULL;
|
||||
PyObject *pvalue = NULL;
|
||||
PyObject *ptraceback = NULL;
|
||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||
startCapturePyStdout();
|
||||
const char result = (PyRun_SimpleString(command.str().c_str()) == 0) ? 'R' : 'N';
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Print();
|
||||
ExtensionContext::instance().reportLong(result, token, "script", getPyStdout().c_str());
|
||||
endCapturePyStdout();
|
||||
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||
#else
|
||||
commandTokens<StringList>(argsIn, &token);
|
||||
ExtensionContext::instance().report('N', token, 0, "script",
|
||||
|
@@ -318,6 +318,17 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
TimelineRulers {
|
||||
contentX: buttonsBar.width - content.x - content.flickableItem.x + content.contentX
|
||||
anchors.left: buttonsBar.right
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
height: content.flickableItem.height + buttonsBar.height
|
||||
windowStart: zoomControl.windowStart
|
||||
viewTimePerPixel: selectionRange.viewTimePerPixel
|
||||
scaleHeight: buttonsBar.height
|
||||
}
|
||||
|
||||
SelectionRangeDetails {
|
||||
z: 3
|
||||
x: 200
|
||||
|
128
src/libs/timeline/qml/TimelineRulers.qml
Normal file
@@ -0,0 +1,128 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
id: rulersParent
|
||||
property int scaleHeight
|
||||
property double viewTimePerPixel: 1
|
||||
property double contentX
|
||||
property double windowStart
|
||||
clip: true
|
||||
|
||||
ListModel {
|
||||
id: rulersModel
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: scaleHeight
|
||||
|
||||
onClicked: {
|
||||
rulersModel.append({
|
||||
timestamp: (mouse.x + contentX) * viewTimePerPixel + windowStart
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: dragDummy
|
||||
property int index: -1
|
||||
onXChanged: {
|
||||
if (index >= 0) {
|
||||
rulersModel.setProperty(
|
||||
index, "timestamp",
|
||||
(x + contentX) * viewTimePerPixel + windowStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: rulersModel
|
||||
|
||||
Item {
|
||||
id: ruler
|
||||
x: (timestamp - windowStart) / viewTimePerPixel - 1 - contentX
|
||||
y: 0
|
||||
width: 2
|
||||
height: rulersParent.height
|
||||
Rectangle {
|
||||
id: arrow
|
||||
height: scaleHeight
|
||||
width: scaleHeight
|
||||
rotation: 45
|
||||
anchors.verticalCenter: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: creatorTheme.Timeline_HandleColor
|
||||
MouseArea {
|
||||
cursorShape: pressed ? Qt.DragMoveCursor : Qt.OpenHandCursor
|
||||
anchors.fill: parent
|
||||
drag.target: dragDummy
|
||||
drag.axis: Drag.XAxis
|
||||
drag.smoothed: false
|
||||
onPressedChanged: {
|
||||
if (!pressed) {
|
||||
dragDummy.index = -1
|
||||
} else {
|
||||
dragDummy.x = ruler.x + 1
|
||||
dragDummy.index = index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: arrow.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
width: 2
|
||||
color: creatorTheme.Timeline_HandleColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: arrow.bottom
|
||||
anchors.horizontalCenter: ruler.horizontalCenter
|
||||
width: scaleHeight / 4
|
||||
height: width
|
||||
color: creatorTheme.Timeline_PanelBackgroundColor
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 2
|
||||
height: 1
|
||||
color: creatorTheme.Timeline_TextColor
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: rulersModel.remove(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -30,5 +30,6 @@
|
||||
<file>SynchronousReloader.qml</file>
|
||||
<file>TimelineText.qml</file>
|
||||
<file>ImageToolButton.qml</file>
|
||||
<file>TimelineRulers.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@@ -471,6 +471,22 @@ bool FileSaverBase::setResult(QXmlStreamWriter *stream)
|
||||
FileSaver::FileSaver(const QString &filename, QIODevice::OpenMode mode)
|
||||
{
|
||||
m_fileName = filename;
|
||||
// Workaround an assert in Qt -- and provide a useful error message, too:
|
||||
if (HostOsInfo::isWindowsHost()) {
|
||||
// Taken from: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||
static const QStringList reservedNames
|
||||
= { "CON", "PRN", "AUX", "NUL",
|
||||
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
|
||||
const QString fn = QFileInfo(filename).baseName().toUpper();
|
||||
for (const QString &rn : reservedNames) {
|
||||
if (fn == rn) {
|
||||
m_errorString = tr("%1: Is a reserved filename on Windows. Cannot save.").arg(filename);
|
||||
m_hasError = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mode & (QIODevice::ReadOnly | QIODevice::Append)) {
|
||||
m_file = new QFile(filename);
|
||||
m_isSafe = false;
|
||||
|
BIN
src/libs/utils/images/iconoverlay_add.png
Normal file
After Width: | Height: | Size: 186 B |
BIN
src/libs/utils/images/iconoverlay_add@2x.png
Normal file
After Width: | Height: | Size: 210 B |
BIN
src/libs/utils/images/iconoverlay_add_background.png
Normal file
After Width: | Height: | Size: 147 B |
BIN
src/libs/utils/images/iconoverlay_add_background@2x.png
Normal file
After Width: | Height: | Size: 225 B |
BIN
src/libs/utils/images/iconoverlay_error.png
Normal file
After Width: | Height: | Size: 157 B |
BIN
src/libs/utils/images/iconoverlay_error@2x.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
src/libs/utils/images/iconoverlay_error_background.png
Normal file
After Width: | Height: | Size: 157 B |
BIN
src/libs/utils/images/iconoverlay_error_background@2x.png
Normal file
After Width: | Height: | Size: 177 B |
BIN
src/libs/utils/images/iconoverlay_warning.png
Normal file
After Width: | Height: | Size: 142 B |
BIN
src/libs/utils/images/iconoverlay_warning@2x.png
Normal file
After Width: | Height: | Size: 216 B |
BIN
src/libs/utils/images/iconoverlay_warning_background.png
Normal file
After Width: | Height: | Size: 141 B |
BIN
src/libs/utils/images/iconoverlay_warning_background@2x.png
Normal file
After Width: | Height: | Size: 221 B |
@@ -542,7 +542,7 @@ bool PathChooser::validatePath(FancyLineEdit *edit, QString *errorMessage) const
|
||||
}
|
||||
if (!fi.isFile()) {
|
||||
if (errorMessage)
|
||||
*errorMessage = tr("The path <b>%1</b> is not a file.").arg(QDir::toNativeSeparators(expandedPath));
|
||||
*errorMessage = tr("The path \"%1\" is not a file.").arg(QDir::toNativeSeparators(expandedPath));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -554,7 +554,7 @@ bool PathChooser::validatePath(FancyLineEdit *edit, QString *errorMessage) const
|
||||
}
|
||||
if (fi.exists() && fi.isDir()) {
|
||||
if (errorMessage)
|
||||
*errorMessage = tr("The path <b>%1</b> is not a file.").arg(QDir::toNativeSeparators(fi.absolutePath()));
|
||||
*errorMessage = tr("The path \"%1\" is not a file.").arg(QDir::toNativeSeparators(fi.absolutePath()));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -566,7 +566,7 @@ bool PathChooser::validatePath(FancyLineEdit *edit, QString *errorMessage) const
|
||||
}
|
||||
if (!fi.isFile() || !fi.isExecutable()) {
|
||||
if (errorMessage)
|
||||
*errorMessage = tr("The path <b>%1</b> is not an executable file.").arg(QDir::toNativeSeparators(expandedPath));
|
||||
*errorMessage = tr("The path \"%1\" is not an executable file.").arg(QDir::toNativeSeparators(expandedPath));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -590,7 +590,7 @@ bool PathChooser::validatePath(FancyLineEdit *edit, QString *errorMessage) const
|
||||
}
|
||||
|
||||
if (errorMessage)
|
||||
*errorMessage = tr("Full path: <b>%1</b>").arg(QDir::toNativeSeparators(expandedPath));
|
||||
*errorMessage = tr("Full path: \"%1\"").arg(QDir::toNativeSeparators(expandedPath));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -138,5 +138,17 @@
|
||||
<file>images/collapse@2x.png</file>
|
||||
<file>images/expand.png</file>
|
||||
<file>images/expand@2x.png</file>
|
||||
<file>images/iconoverlay_add.png</file>
|
||||
<file>images/iconoverlay_add@2x.png</file>
|
||||
<file>images/iconoverlay_add_background.png</file>
|
||||
<file>images/iconoverlay_add_background@2x.png</file>
|
||||
<file>images/iconoverlay_error.png</file>
|
||||
<file>images/iconoverlay_error@2x.png</file>
|
||||
<file>images/iconoverlay_error_background.png</file>
|
||||
<file>images/iconoverlay_error_background@2x.png</file>
|
||||
<file>images/iconoverlay_warning.png</file>
|
||||
<file>images/iconoverlay_warning@2x.png</file>
|
||||
<file>images/iconoverlay_warning_background.png</file>
|
||||
<file>images/iconoverlay_warning_background@2x.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
@@ -181,9 +181,15 @@ const Icon COLLAPSE({
|
||||
const Icon COLLAPSE_TOOLBAR({
|
||||
{QLatin1String(":/utils/images/collapse.png"), Theme::IconsBaseColor}});
|
||||
const Icon EMPTY14(":/utils/images/empty14.png");
|
||||
const Icon ENABLE_KIT_OVERLAY({
|
||||
{":/projectexplorer/images/enablekitbackground.png", Theme::IconsRunColor},
|
||||
{":/projectexplorer/images/enablekitforeground.png", Theme::BackgroundColorNormal}}, Icon::Tint);
|
||||
const Icon OVERLAY_ADD({
|
||||
{":/utils/images/iconoverlay_add_background.png", Theme::BackgroundColorNormal},
|
||||
{":/utils/images/iconoverlay_add.png", Theme::IconsRunColor}}, Icon::Tint);
|
||||
const Icon OVERLAY_WARNING({
|
||||
{":/utils/images/iconoverlay_warning_background.png", Theme::BackgroundColorNormal},
|
||||
{":/utils/images/iconoverlay_warning.png", Theme::IconsWarningColor}}, Icon::Tint);
|
||||
const Icon OVERLAY_ERROR({
|
||||
{":/utils/images/iconoverlay_error_background.png", Theme::BackgroundColorNormal},
|
||||
{":/utils/images/iconoverlay_error.png", Theme::IconsErrorColor}}, Icon::Tint);
|
||||
|
||||
} // namespace Icons
|
||||
} // namespace Utils
|
||||
|
@@ -107,7 +107,9 @@ QTCREATOR_UTILS_EXPORT extern const Icon EXPAND_TOOLBAR;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon COLLAPSE;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon COLLAPSE_TOOLBAR;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon EMPTY14;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon ENABLE_KIT_OVERLAY;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon OVERLAY_ADD;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon OVERLAY_WARNING;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon OVERLAY_ERROR;
|
||||
|
||||
} // namespace Icons
|
||||
} // namespace Utils
|
||||
|
@@ -31,6 +31,7 @@ SOURCES += \
|
||||
gtest/gtestvisitors.cpp \
|
||||
gtest/gtestframework.cpp \
|
||||
gtest/gtestsettings.cpp \
|
||||
gtest/gtestsettingspage.cpp \
|
||||
qtest/qttesttreeitem.cpp \
|
||||
qtest/qttestvisitors.cpp \
|
||||
qtest/qttestconfiguration.cpp \
|
||||
@@ -39,6 +40,7 @@ SOURCES += \
|
||||
qtest/qttestparser.cpp \
|
||||
qtest/qttestframework.cpp \
|
||||
qtest/qttestsettings.cpp \
|
||||
qtest/qttestsettingspage.cpp \
|
||||
quick/quicktestconfiguration.cpp \
|
||||
quick/quicktestparser.cpp \
|
||||
quick/quicktesttreeitem.cpp \
|
||||
@@ -80,6 +82,8 @@ HEADERS += \
|
||||
gtest/gtestvisitors.h \
|
||||
gtest/gtestframework.h \
|
||||
gtest/gtestsettings.h \
|
||||
gtest/gtestsettingspage.h \
|
||||
gtest/gtestconstants.h \
|
||||
qtest/qttesttreeitem.h \
|
||||
qtest/qttest_utils.h \
|
||||
qtest/qttestresult.h \
|
||||
@@ -89,6 +93,8 @@ HEADERS += \
|
||||
qtest/qttestparser.h \
|
||||
qtest/qttestframework.h \
|
||||
qtest/qttestsettings.h \
|
||||
qtest/qttestsettingspage.h \
|
||||
qtest/qttestconstants.h \
|
||||
quick/quicktestconfiguration.h \
|
||||
quick/quicktestparser.h \
|
||||
quick/quicktesttreeitem.h \
|
||||
@@ -102,7 +108,9 @@ RESOURCES += \
|
||||
autotest.qrc
|
||||
|
||||
FORMS += \
|
||||
testsettingspage.ui
|
||||
testsettingspage.ui \
|
||||
qtest/qttestsettingspage.ui \
|
||||
gtest/gtestsettingspage.ui
|
||||
|
||||
equals(TEST, 1) {
|
||||
HEADERS += autotestunittests.h
|
||||
|
@@ -39,7 +39,10 @@ const char AUTOTEST_CONTEXT[] = "Auto Tests";
|
||||
const char TASK_INDEX[] = "AutoTest.Task.Index";
|
||||
const char TASK_PARSE[] = "AutoTest.Task.Parse";
|
||||
const char AUTOTEST_SETTINGS_CATEGORY[] = "ZY.Tests";
|
||||
const char AUTOTEST_SETTINGS_TR[] = QT_TRANSLATE_NOOP("AutoTest", "Test Settings");
|
||||
const char FRAMEWORK_PREFIX[] = "AutoTest.Framework.";
|
||||
|
||||
|
||||
const char SETTINGSGROUP[] = "Autotest";
|
||||
} // namespace Constants
|
||||
} // namespace Autotest
|
||||
|
@@ -135,7 +135,7 @@ bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorStri
|
||||
m_frameworkManager->registerTestFramework(new QuickTestFramework);
|
||||
m_frameworkManager->registerTestFramework(new GTestFramework);
|
||||
|
||||
m_settings->fromSettings(ICore::settings());
|
||||
m_frameworkManager->synchronizeSettings(ICore::settings());
|
||||
addAutoReleasedObject(new TestSettingsPage(m_settings));
|
||||
addAutoReleasedObject(new TestNavigationWidgetFactory);
|
||||
addAutoReleasedObject(TestResultsPane::instance());
|
||||
|
@@ -24,8 +24,10 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "gtestconfiguration.h"
|
||||
#include "gtestconstants.h"
|
||||
#include "gtestoutputreader.h"
|
||||
#include "../testsettings.h"
|
||||
#include "gtestsettings.h"
|
||||
#include "../testframeworkmanager.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
@@ -36,25 +38,32 @@ TestOutputReader *GTestConfiguration::outputReader(const QFutureInterface<TestRe
|
||||
return new GTestOutputReader(fi, app, buildDirectory());
|
||||
}
|
||||
|
||||
QStringList GTestConfiguration::argumentsForTestRunner(const TestSettings &settings) const
|
||||
QStringList GTestConfiguration::argumentsForTestRunner() const
|
||||
{
|
||||
static const Core::Id id
|
||||
= Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(GTest::Constants::FRAMEWORK_NAME);
|
||||
|
||||
QStringList arguments;
|
||||
const QStringList &testSets = testCases();
|
||||
if (testSets.size())
|
||||
arguments << "--gtest_filter=" + testSets.join(':');
|
||||
if (settings.gTestSettings.runDisabled)
|
||||
|
||||
TestFrameworkManager *manager = TestFrameworkManager::instance();
|
||||
auto gSettings = qSharedPointerCast<GTestSettings>(manager->settingsForTestFramework(id));
|
||||
if (gSettings.isNull())
|
||||
return arguments;
|
||||
|
||||
if (gSettings->runDisabled)
|
||||
arguments << "--gtest_also_run_disabled_tests";
|
||||
if (settings.gTestSettings.repeat)
|
||||
arguments << QString("--gtest_repeat=%1").arg(settings.gTestSettings.iterations);
|
||||
if (settings.gTestSettings.shuffle) {
|
||||
arguments << "--gtest_shuffle"
|
||||
<< QString("--gtest_random_seed=%1").arg(settings.gTestSettings.seed);
|
||||
}
|
||||
if (settings.gTestSettings.throwOnFailure)
|
||||
if (gSettings->repeat)
|
||||
arguments << QString("--gtest_repeat=%1").arg(gSettings->iterations);
|
||||
if (gSettings->shuffle)
|
||||
arguments << "--gtest_shuffle" << QString("--gtest_random_seed=%1").arg(gSettings->seed);
|
||||
if (gSettings->throwOnFailure)
|
||||
arguments << "--gtest_throw_on_failure";
|
||||
|
||||
if (runMode() == DebuggableTestConfiguration::Debug) {
|
||||
if (settings.gTestSettings.breakOnFailure)
|
||||
if (gSettings->breakOnFailure)
|
||||
arguments << "--gtest_break_on_failure";
|
||||
}
|
||||
return arguments;
|
||||
|
@@ -36,7 +36,7 @@ public:
|
||||
explicit GTestConfiguration() {}
|
||||
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||
QProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(const TestSettings &settings) const override;
|
||||
QStringList argumentsForTestRunner() const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
40
src/plugins/autotest/gtest/gtestconstants.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <QtGlobal>
|
||||
|
||||
namespace Autotest {
|
||||
namespace GTest {
|
||||
namespace Constants {
|
||||
|
||||
const char FRAMEWORK_NAME[] = "GTest";
|
||||
const char FRAMEWORK_SETTINGS_CATEGORY[] = QT_TRANSLATE_NOOP("GTestFramework", "Google Test");
|
||||
const unsigned FRAMEWORK_PRIORITY = 10;
|
||||
|
||||
} // namespace Constants
|
||||
} // namespace GTest
|
||||
} // namespace AutoTest
|
@@ -24,6 +24,9 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "gtestframework.h"
|
||||
#include "gtestconstants.h"
|
||||
#include "gtestsettings.h"
|
||||
#include "gtestsettingspage.h"
|
||||
#include "gtesttreeitem.h"
|
||||
#include "gtestparser.h"
|
||||
|
||||
@@ -37,18 +40,35 @@ ITestParser *GTestFramework::createTestParser() const
|
||||
|
||||
TestTreeItem *GTestFramework::createRootNode() const
|
||||
{
|
||||
return new GTestTreeItem(QCoreApplication::translate("GTestFramework", "Google Tests"),
|
||||
QString(), TestTreeItem::Root);
|
||||
return new GTestTreeItem(
|
||||
QCoreApplication::translate("GTestFramework",
|
||||
GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY),
|
||||
QString(), TestTreeItem::Root);
|
||||
}
|
||||
|
||||
const char *GTestFramework::name() const
|
||||
{
|
||||
return "GTest";
|
||||
return GTest::Constants::FRAMEWORK_NAME;
|
||||
}
|
||||
|
||||
unsigned GTestFramework::priority() const
|
||||
{
|
||||
return 10;
|
||||
return GTest::Constants::FRAMEWORK_PRIORITY;
|
||||
}
|
||||
|
||||
IFrameworkSettings *GTestFramework::createFrameworkSettings() const
|
||||
{
|
||||
return new GTestSettings;
|
||||
}
|
||||
|
||||
Core::IOptionsPage *GTestFramework::createSettingsPage(QSharedPointer<IFrameworkSettings> settings) const
|
||||
{
|
||||
return new GTestSettingsPage(settings);
|
||||
}
|
||||
|
||||
bool GTestFramework::hasFrameworkSettings() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -36,7 +36,9 @@ public:
|
||||
GTestFramework() : ITestFramework(true) {}
|
||||
const char *name() const override;
|
||||
unsigned priority() const override;
|
||||
|
||||
IFrameworkSettings *createFrameworkSettings() const override;
|
||||
Core::IOptionsPage *createSettingsPage(QSharedPointer<IFrameworkSettings> settings) const override;
|
||||
bool hasFrameworkSettings() const override;
|
||||
protected:
|
||||
ITestParser *createTestParser() const override;
|
||||
TestTreeItem *createRootNode() const override;
|
||||
|
@@ -41,7 +41,7 @@ QString GTestSettings::name() const
|
||||
return QString("GTest");
|
||||
}
|
||||
|
||||
void GTestSettings::fromSettings(const QSettings *s)
|
||||
void GTestSettings::fromFrameworkSettings(const QSettings *s)
|
||||
{
|
||||
runDisabled = s->value(runDisabledKey, false).toBool();
|
||||
repeat = s->value(repeatKey, false).toBool();
|
||||
@@ -52,7 +52,7 @@ void GTestSettings::fromSettings(const QSettings *s)
|
||||
throwOnFailure = s->value(throwOnFailureKey, false).toBool();
|
||||
}
|
||||
|
||||
void GTestSettings::toSettings(QSettings *s) const
|
||||
void GTestSettings::toFrameworkSettings(QSettings *s) const
|
||||
{
|
||||
s->setValue(runDisabledKey, runDisabled);
|
||||
s->setValue(repeatKey, repeat);
|
||||
|
@@ -35,8 +35,6 @@ class GTestSettings : public IFrameworkSettings
|
||||
public:
|
||||
GTestSettings() {}
|
||||
QString name() const override;
|
||||
void fromSettings(const QSettings *s) override;
|
||||
void toSettings(QSettings *s) const override;
|
||||
|
||||
int iterations = 1;
|
||||
int seed = 0;
|
||||
@@ -45,6 +43,10 @@ public:
|
||||
bool repeat = false;
|
||||
bool throwOnFailure = false;
|
||||
bool breakOnFailure = true;
|
||||
|
||||
protected:
|
||||
void fromFrameworkSettings(const QSettings *s) override;
|
||||
void toFrameworkSettings(QSettings *s) const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
100
src/plugins/autotest/gtest/gtestsettingspage.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "../autotestconstants.h"
|
||||
#include "gtestconstants.h"
|
||||
#include "gtestsettingspage.h"
|
||||
#include "gtestsettings.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
GTestSettingsWidget::GTestSettingsWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
connect(m_ui.repeatGTestsCB, &QCheckBox::toggled, m_ui.repetitionSpin, &QSpinBox::setEnabled);
|
||||
connect(m_ui.shuffleGTestsCB, &QCheckBox::toggled, m_ui.seedSpin, &QSpinBox::setEnabled);
|
||||
}
|
||||
|
||||
void GTestSettingsWidget::setSettings(const GTestSettings &settings)
|
||||
{
|
||||
m_ui.runDisabledGTestsCB->setChecked(settings.runDisabled);
|
||||
m_ui.repeatGTestsCB->setChecked(settings.repeat);
|
||||
m_ui.shuffleGTestsCB->setChecked(settings.shuffle);
|
||||
m_ui.repetitionSpin->setValue(settings.iterations);
|
||||
m_ui.seedSpin->setValue(settings.seed);
|
||||
m_ui.breakOnFailureCB->setChecked(settings.breakOnFailure);
|
||||
m_ui.throwOnFailureCB->setChecked(settings.throwOnFailure);
|
||||
}
|
||||
|
||||
GTestSettings GTestSettingsWidget::settings() const
|
||||
{
|
||||
GTestSettings result;
|
||||
result.runDisabled = m_ui.runDisabledGTestsCB->isChecked();
|
||||
result.repeat = m_ui.repeatGTestsCB->isChecked();
|
||||
result.shuffle = m_ui.shuffleGTestsCB->isChecked();
|
||||
result.iterations = m_ui.repetitionSpin->value();
|
||||
result.seed = m_ui.seedSpin->value();
|
||||
result.breakOnFailure = m_ui.breakOnFailureCB->isChecked();
|
||||
result.throwOnFailure = m_ui.throwOnFailureCB->isChecked();
|
||||
return result;
|
||||
}
|
||||
|
||||
GTestSettingsPage::GTestSettingsPage(QSharedPointer<IFrameworkSettings> settings)
|
||||
: m_settings(qSharedPointerCast<GTestSettings>(settings)), m_widget(0)
|
||||
{
|
||||
setId("A.AutoTest.10.GTest"); // FIXME
|
||||
setDisplayName(QCoreApplication::translate("GTestFramework",
|
||||
GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
|
||||
setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
|
||||
setDisplayCategory(QCoreApplication::translate("AutoTest", Constants::AUTOTEST_SETTINGS_TR));
|
||||
}
|
||||
|
||||
GTestSettingsPage::~GTestSettingsPage()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget *GTestSettingsPage::widget()
|
||||
{
|
||||
if (!m_widget) {
|
||||
m_widget = new GTestSettingsWidget;
|
||||
m_widget->setSettings(*m_settings);
|
||||
}
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
void GTestSettingsPage::apply()
|
||||
{
|
||||
if (!m_widget) // page was not shown at all
|
||||
return;
|
||||
*m_settings = m_widget->settings();
|
||||
m_settings->toSettings(Core::ICore::settings());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
70
src/plugins/autotest/gtest/gtestsettingspage.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "ui_gtestsettingspage.h"
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class IFrameworkSettings;
|
||||
class GTestSettings;
|
||||
|
||||
class GTestSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GTestSettingsWidget(QWidget *parent = 0);
|
||||
|
||||
void setSettings(const GTestSettings &settings);
|
||||
GTestSettings settings() const;
|
||||
|
||||
private:
|
||||
Ui::GTestSettingsPage m_ui;
|
||||
};
|
||||
|
||||
class GTestSettingsPage : public Core::IOptionsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GTestSettingsPage(QSharedPointer<IFrameworkSettings> settings);
|
||||
~GTestSettingsPage();
|
||||
|
||||
QWidget *widget() override;
|
||||
void apply() override;
|
||||
void finish() override { }
|
||||
|
||||
private:
|
||||
QSharedPointer<GTestSettings> m_settings;
|
||||
QPointer<GTestSettingsWidget> m_widget;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
219
src/plugins/autotest/gtest/gtestsettingspage.ui
Normal file
@@ -0,0 +1,219 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Autotest::Internal::GTestSettingsPage</class>
|
||||
<widget class="QWidget" name="Autotest::Internal::GTestSettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>397</width>
|
||||
<height>205</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="runDisabledGTestsCB">
|
||||
<property name="toolTip">
|
||||
<string>Executes disabled tests when performing a test run.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Run disabled tests</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="breakOnFailureCB">
|
||||
<property name="toolTip">
|
||||
<string>Turn failures into debugger breakpoints.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Break on failure while debugging</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="repeatGTestsCB">
|
||||
<property name="toolTip">
|
||||
<string>Repeats a test run (you might be required to increase the timeout to avoid canceling the tests).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Repeat tests</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Iterations:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="repetitionSpin">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="shuffleGTestsCB">
|
||||
<property name="toolTip">
|
||||
<string>Shuffle tests automatically on every iteration by the given seed.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Shuffle tests</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Seed:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="seedSpin">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>A seed of 0 generates a seed based on the current timestamp.</string>
|
||||
</property>
|
||||
<property name="specialValueText">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>99999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="throwOnFailureCB">
|
||||
<property name="toolTip">
|
||||
<string>Turn assertion failures into C++ exceptions.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Throw on failure</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "autotestconstants.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
namespace Autotest {
|
||||
@@ -37,8 +39,28 @@ public:
|
||||
virtual ~IFrameworkSettings() {}
|
||||
|
||||
virtual QString name() const = 0;
|
||||
virtual void toSettings(QSettings *s) const = 0;
|
||||
virtual void fromSettings(const QSettings *s) = 0;
|
||||
|
||||
void toSettings(QSettings *s) const
|
||||
{
|
||||
s->beginGroup(Constants::SETTINGSGROUP);
|
||||
s->beginGroup(name());
|
||||
toFrameworkSettings(s);
|
||||
s->endGroup();
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
void fromSettings(QSettings *s)
|
||||
{
|
||||
s->beginGroup(Constants::SETTINGSGROUP);
|
||||
s->beginGroup(name());
|
||||
fromFrameworkSettings(s);
|
||||
s->endGroup();
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void toFrameworkSettings(QSettings *s) const = 0;
|
||||
virtual void fromFrameworkSettings(const QSettings *s) = 0;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -28,9 +28,13 @@
|
||||
#include "testtreeitem.h"
|
||||
#include "itestparser.h"
|
||||
|
||||
namespace Core { class IOptionsPage; }
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class IFrameworkSettings;
|
||||
|
||||
class ITestFramework
|
||||
{
|
||||
public:
|
||||
@@ -43,6 +47,13 @@ public:
|
||||
|
||||
virtual const char *name() const = 0;
|
||||
virtual unsigned priority() const = 0; // should this be modifyable?
|
||||
virtual bool hasFrameworkSettings() const { return false; }
|
||||
virtual IFrameworkSettings *createFrameworkSettings() const { return 0; }
|
||||
virtual Core::IOptionsPage *createSettingsPage(QSharedPointer<IFrameworkSettings> settings) const
|
||||
{
|
||||
Q_UNUSED(settings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TestTreeItem *rootNode()
|
||||
{ if (!m_rootNode)
|
||||
|
@@ -24,8 +24,10 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qttestconfiguration.h"
|
||||
#include "qttestconstants.h"
|
||||
#include "qttestoutputreader.h"
|
||||
#include "../testsettings.h"
|
||||
#include "qttestsettings.h"
|
||||
#include "../testframeworkmanager.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
@@ -36,19 +38,26 @@ TestOutputReader *QtTestConfiguration::outputReader(const QFutureInterface<TestR
|
||||
return new QtTestOutputReader(fi, app, buildDirectory());
|
||||
}
|
||||
|
||||
QStringList QtTestConfiguration::argumentsForTestRunner(const TestSettings &settings) const
|
||||
QStringList QtTestConfiguration::argumentsForTestRunner() const
|
||||
{
|
||||
QStringList arguments("-xml");
|
||||
static const Core::Id id
|
||||
= Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME);
|
||||
|
||||
const QString &metricsOption
|
||||
= QtTestSettings::metricsTypeToOption(settings.qtTestSettings.metrics);
|
||||
if (!metricsOption.isEmpty())
|
||||
arguments << metricsOption;
|
||||
QStringList arguments("-xml");
|
||||
if (testCases().count())
|
||||
arguments << testCases();
|
||||
|
||||
TestFrameworkManager *manager = TestFrameworkManager::instance();
|
||||
auto qtSettings = qSharedPointerCast<QtTestSettings>(manager->settingsForTestFramework(id));
|
||||
if (qtSettings.isNull())
|
||||
return arguments;
|
||||
|
||||
const QString &metricsOption = QtTestSettings::metricsTypeToOption(qtSettings->metrics);
|
||||
if (!metricsOption.isEmpty())
|
||||
arguments << metricsOption;
|
||||
|
||||
if (runMode() == DebuggableTestConfiguration::Debug) {
|
||||
if (settings.qtTestSettings.noCrashHandler)
|
||||
if (qtSettings->noCrashHandler)
|
||||
arguments << "-nocrashhandler";
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,7 @@ public:
|
||||
explicit QtTestConfiguration() {}
|
||||
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||
QProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(const TestSettings &settings) const override;
|
||||
QStringList argumentsForTestRunner() const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
40
src/plugins/autotest/qtest/qttestconstants.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <QtGlobal>
|
||||
|
||||
namespace Autotest {
|
||||
namespace QtTest {
|
||||
namespace Constants {
|
||||
|
||||
const char FRAMEWORK_NAME[] = "QtTest";
|
||||
const char FRAMEWORK_SETTINGS_CATEGORY[] = QT_TRANSLATE_NOOP("QtTestFramework", "Qt Test");
|
||||
const unsigned FRAMEWORK_PRIORITY = 1;
|
||||
|
||||
} // namespace Constants
|
||||
} // namespace QtTest
|
||||
} // namespace Autotest
|
@@ -24,7 +24,10 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qttestframework.h"
|
||||
#include "qttestconstants.h"
|
||||
#include "qttestparser.h"
|
||||
#include "qttestsettings.h"
|
||||
#include "qttestsettingspage.h"
|
||||
#include "qttesttreeitem.h"
|
||||
|
||||
namespace Autotest {
|
||||
@@ -37,18 +40,35 @@ ITestParser *QtTestFramework::createTestParser() const
|
||||
|
||||
TestTreeItem *QtTestFramework::createRootNode() const
|
||||
{
|
||||
return new QtTestTreeItem(QCoreApplication::translate("QtTestFramework", "Qt Tests"),
|
||||
QString(), TestTreeItem::Root);
|
||||
return new QtTestTreeItem(
|
||||
QCoreApplication::translate("QtTestFramework",
|
||||
QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY),
|
||||
QString(), TestTreeItem::Root);
|
||||
}
|
||||
|
||||
IFrameworkSettings *QtTestFramework::createFrameworkSettings() const
|
||||
{
|
||||
return new QtTestSettings;
|
||||
}
|
||||
|
||||
Core::IOptionsPage *QtTestFramework::createSettingsPage(QSharedPointer<IFrameworkSettings> settings) const
|
||||
{
|
||||
return new QtTestSettingsPage(settings);
|
||||
}
|
||||
|
||||
bool QtTestFramework::hasFrameworkSettings() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *QtTestFramework::name() const
|
||||
{
|
||||
return "QtTest";
|
||||
return QtTest::Constants::FRAMEWORK_NAME;
|
||||
}
|
||||
|
||||
unsigned QtTestFramework::priority() const
|
||||
{
|
||||
return 1;
|
||||
return QtTest::Constants::FRAMEWORK_PRIORITY;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -36,6 +36,9 @@ public:
|
||||
QtTestFramework() : ITestFramework(true) {}
|
||||
const char *name() const override;
|
||||
unsigned priority() const override;
|
||||
IFrameworkSettings *createFrameworkSettings() const override;
|
||||
Core::IOptionsPage *createSettingsPage(QSharedPointer<IFrameworkSettings> settings) const override;
|
||||
bool hasFrameworkSettings() const override;
|
||||
|
||||
protected:
|
||||
ITestParser *createTestParser() const override;
|
||||
|
@@ -54,13 +54,13 @@ QString QtTestSettings::name() const
|
||||
return QString("QtTest");
|
||||
}
|
||||
|
||||
void QtTestSettings::fromSettings(const QSettings *s)
|
||||
void QtTestSettings::fromFrameworkSettings(const QSettings *s)
|
||||
{
|
||||
metrics = intToMetrics(s->value(metricsKey, Walltime).toInt());
|
||||
noCrashHandler = s->value(noCrashhandlerKey, true).toBool();
|
||||
}
|
||||
|
||||
void QtTestSettings::toSettings(QSettings *s) const
|
||||
void QtTestSettings::toFrameworkSettings(QSettings *s) const
|
||||
{
|
||||
s->setValue(metricsKey, metrics);
|
||||
s->setValue(noCrashhandlerKey, noCrashHandler);
|
||||
|
@@ -44,12 +44,14 @@ class QtTestSettings : public IFrameworkSettings
|
||||
public:
|
||||
QtTestSettings() {}
|
||||
QString name() const override;
|
||||
void fromSettings(const QSettings *s) override;
|
||||
void toSettings(QSettings *s) const override;
|
||||
static QString metricsTypeToOption(const MetricsType type);
|
||||
|
||||
MetricsType metrics = Walltime;
|
||||
bool noCrashHandler = true;
|
||||
|
||||
protected:
|
||||
void fromFrameworkSettings(const QSettings *s) override;
|
||||
void toFrameworkSettings(QSettings *s) const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
121
src/plugins/autotest/qtest/qttestsettingspage.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "../autotestconstants.h"
|
||||
#include "qttestconstants.h"
|
||||
#include "qttestsettingspage.h"
|
||||
#include "qttestsettings.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
QtTestSettingsWidget::QtTestSettingsWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
m_ui.callgrindRB->setEnabled(Utils::HostOsInfo::isAnyUnixHost()); // valgrind available on UNIX
|
||||
m_ui.perfRB->setEnabled(Utils::HostOsInfo::isLinuxHost()); // according to docs perf Linux only
|
||||
}
|
||||
|
||||
void QtTestSettingsWidget::setSettings(const QtTestSettings &settings)
|
||||
{
|
||||
m_ui.disableCrashhandlerCB->setChecked(settings.noCrashHandler);
|
||||
switch (settings.metrics) {
|
||||
case MetricsType::Walltime:
|
||||
m_ui.walltimeRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::TickCounter:
|
||||
m_ui.tickcounterRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::EventCounter:
|
||||
m_ui.eventCounterRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::CallGrind:
|
||||
m_ui.callgrindRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::Perf:
|
||||
m_ui.perfRB->setChecked(true);
|
||||
break;
|
||||
default:
|
||||
m_ui.walltimeRB->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
QtTestSettings QtTestSettingsWidget::settings() const
|
||||
{
|
||||
QtTestSettings result;
|
||||
|
||||
result.noCrashHandler = m_ui.disableCrashhandlerCB->isChecked();
|
||||
if (m_ui.walltimeRB->isChecked())
|
||||
result.metrics = MetricsType::Walltime;
|
||||
else if (m_ui.tickcounterRB->isChecked())
|
||||
result.metrics = MetricsType::TickCounter;
|
||||
else if (m_ui.eventCounterRB->isChecked())
|
||||
result.metrics = MetricsType::EventCounter;
|
||||
else if (m_ui.callgrindRB->isChecked())
|
||||
result.metrics = MetricsType::CallGrind;
|
||||
else if (m_ui.perfRB->isChecked())
|
||||
result.metrics = MetricsType::Perf;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QtTestSettingsPage::QtTestSettingsPage(QSharedPointer<IFrameworkSettings> settings)
|
||||
: m_settings(qSharedPointerCast<QtTestSettings>(settings)), m_widget(0)
|
||||
{
|
||||
setId("A.AutoTest.1.QtTest"); // FIXME
|
||||
setDisplayName(QCoreApplication::translate("QtTestFramework",
|
||||
QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY));
|
||||
setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
|
||||
setDisplayCategory(QCoreApplication::translate("AutoTest", Constants::AUTOTEST_SETTINGS_TR));
|
||||
}
|
||||
|
||||
QtTestSettingsPage::~QtTestSettingsPage()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget *QtTestSettingsPage::widget()
|
||||
{
|
||||
if (!m_widget) {
|
||||
m_widget = new QtTestSettingsWidget;
|
||||
m_widget->setSettings(*m_settings);
|
||||
}
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
void QtTestSettingsPage::apply()
|
||||
{
|
||||
if (!m_widget) // page was not shown at all
|
||||
return;
|
||||
*m_settings = m_widget->settings();
|
||||
m_settings->toSettings(Core::ICore::settings());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
70
src/plugins/autotest/qtest/qttestsettingspage.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "ui_qttestsettingspage.h"
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class IFrameworkSettings;
|
||||
class QtTestSettings;
|
||||
|
||||
class QtTestSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QtTestSettingsWidget(QWidget *parent = 0);
|
||||
|
||||
void setSettings(const QtTestSettings &settings);
|
||||
QtTestSettings settings() const;
|
||||
|
||||
private:
|
||||
Ui::QtTestSettingsPage m_ui;
|
||||
};
|
||||
|
||||
class QtTestSettingsPage : public Core::IOptionsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QtTestSettingsPage(QSharedPointer<IFrameworkSettings> settings);
|
||||
~QtTestSettingsPage();
|
||||
|
||||
QWidget *widget() override;
|
||||
void apply() override;
|
||||
void finish() override { }
|
||||
|
||||
private:
|
||||
QSharedPointer<QtTestSettings> m_settings;
|
||||
QPointer<QtTestSettingsWidget> m_widget;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
169
src/plugins/autotest/qtest/qttestsettingspage.ui
Normal file
@@ -0,0 +1,169 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Autotest::Internal::QtTestSettingsPage</class>
|
||||
<widget class="QWidget" name="Autotest::Internal::QtTestSettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>327</width>
|
||||
<height>266</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="disableCrashhandlerCB">
|
||||
<property name="toolTip">
|
||||
<string>Enables interrupting tests on assertions.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable crash handler while debugging</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Benchmark Metrics</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="walltimeRB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses walltime metrics for executing benchmarks (default).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Walltime</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="tickcounterRB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses tick counter when executing benchmarks.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tick counter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="eventCounterRB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses event counter when executing benchmarks.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Event counter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="callgrindRB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses Valgrind Callgrind when executing benchmarks (it must be installed).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Callgrind</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="perfRB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses Perf when executing benchmarks (it must be installed).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Perf</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@@ -24,8 +24,10 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "quicktestconfiguration.h"
|
||||
#include "../qtest/qttestconstants.h"
|
||||
#include "../qtest/qttestoutputreader.h"
|
||||
#include "../testsettings.h"
|
||||
#include "../qtest/qttestsettings.h"
|
||||
#include "../testframeworkmanager.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
@@ -36,16 +38,23 @@ TestOutputReader *QuickTestConfiguration::outputReader(const QFutureInterface<Te
|
||||
return new QtTestOutputReader(fi, app, buildDirectory());
|
||||
}
|
||||
|
||||
QStringList QuickTestConfiguration::argumentsForTestRunner(const TestSettings &settings) const
|
||||
QStringList QuickTestConfiguration::argumentsForTestRunner() const
|
||||
{
|
||||
QStringList arguments({"-xml"});
|
||||
static const Core::Id id
|
||||
= Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME);
|
||||
|
||||
const QString &metricsOption
|
||||
= QtTestSettings::metricsTypeToOption(settings.qtTestSettings.metrics);
|
||||
if (!metricsOption.isEmpty())
|
||||
arguments << metricsOption;
|
||||
QStringList arguments("-xml");
|
||||
if (testCases().count())
|
||||
arguments << testCases();
|
||||
|
||||
TestFrameworkManager *manager = TestFrameworkManager::instance();
|
||||
auto qtSettings = qSharedPointerCast<QtTestSettings>(manager->settingsForTestFramework(id));
|
||||
if (qtSettings.isNull())
|
||||
return arguments;
|
||||
|
||||
const QString &metricsOption = QtTestSettings::metricsTypeToOption(qtSettings->metrics);
|
||||
if (!metricsOption.isEmpty())
|
||||
arguments << metricsOption;
|
||||
return arguments;
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,7 @@ public:
|
||||
explicit QuickTestConfiguration() {}
|
||||
TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||
QProcess *app) const override;
|
||||
QStringList argumentsForTestRunner(const TestSettings &settings) const override;
|
||||
QStringList argumentsForTestRunner() const override;
|
||||
|
||||
void setUnnamedOnly(bool unnamedOnly);
|
||||
bool unnamedOnly() const { return m_unnamedOnly; }
|
||||
|
@@ -27,7 +27,6 @@
|
||||
#include "testoutputreader.h"
|
||||
#include "testrunconfiguration.h"
|
||||
#include "testrunner.h"
|
||||
#include "testsettings.h"
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
#include <cpptools/projectinfo.h>
|
||||
|
@@ -45,7 +45,6 @@ namespace Internal {
|
||||
class TestOutputReader;
|
||||
class TestResult;
|
||||
class TestRunConfiguration;
|
||||
struct TestSettings;
|
||||
|
||||
using TestResultPtr = QSharedPointer<TestResult>;
|
||||
|
||||
@@ -86,7 +85,7 @@ public:
|
||||
|
||||
virtual TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||
QProcess *app) const = 0;
|
||||
virtual QStringList argumentsForTestRunner(const TestSettings &settings) const = 0;
|
||||
virtual QStringList argumentsForTestRunner() const = 0;
|
||||
|
||||
private:
|
||||
QStringList m_testCases;
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "testframeworkmanager.h"
|
||||
#include "autotestconstants.h"
|
||||
#include "autotestplugin.h"
|
||||
#include "iframeworksettings.h"
|
||||
#include "itestframework.h"
|
||||
#include "itestparser.h"
|
||||
#include "testrunner.h"
|
||||
@@ -32,10 +34,12 @@
|
||||
#include "testtreeitem.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QSettings>
|
||||
|
||||
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.frameworkmanager")
|
||||
|
||||
@@ -74,6 +78,15 @@ bool TestFrameworkManager::registerTestFramework(ITestFramework *framework)
|
||||
// TODO check for unique priority before registering
|
||||
qCDebug(LOG) << "Registering" << id;
|
||||
m_registeredFrameworks.insert(id, framework);
|
||||
|
||||
AutotestPlugin *plugin = AutotestPlugin::instance();
|
||||
|
||||
if (framework->hasFrameworkSettings()) {
|
||||
QSharedPointer<IFrameworkSettings> frameworkSettings(framework->createFrameworkSettings());
|
||||
m_frameworkSettings.insert(id, frameworkSettings);
|
||||
if (auto page = framework->createSettingsPage(frameworkSettings))
|
||||
plugin->addAutoReleasedObject(page);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,6 +158,23 @@ ITestParser *TestFrameworkManager::testParserForTestFramework(const Core::Id &fr
|
||||
return testParser;
|
||||
}
|
||||
|
||||
QSharedPointer<IFrameworkSettings> TestFrameworkManager::settingsForTestFramework(
|
||||
const Core::Id &frameworkId) const
|
||||
{
|
||||
return m_frameworkSettings.contains(frameworkId) ? m_frameworkSettings.value(frameworkId)
|
||||
: QSharedPointer<IFrameworkSettings>();
|
||||
}
|
||||
|
||||
void TestFrameworkManager::synchronizeSettings(QSettings *s)
|
||||
{
|
||||
AutotestPlugin::instance()->settings()->fromSettings(s);
|
||||
for (const Core::Id &id : m_frameworkSettings.keys()) {
|
||||
QSharedPointer<IFrameworkSettings> fSettings = settingsForTestFramework(id);
|
||||
if (!fSettings.isNull())
|
||||
fSettings->fromSettings(s);
|
||||
}
|
||||
}
|
||||
|
||||
bool TestFrameworkManager::isActive(const Core::Id &frameworkId) const
|
||||
{
|
||||
ITestFramework *framework = m_registeredFrameworks.value(frameworkId);
|
||||
|
@@ -28,11 +28,16 @@
|
||||
#include <QHash>
|
||||
#include <QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Core { class Id; }
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class IFrameworkSettings;
|
||||
class ITestFramework;
|
||||
class ITestParser;
|
||||
class TestRunner;
|
||||
@@ -55,6 +60,8 @@ public:
|
||||
|
||||
TestTreeItem *rootNodeForTestFramework(const Core::Id &frameworkId) const;
|
||||
ITestParser *testParserForTestFramework(const Core::Id &frameworkId) const;
|
||||
QSharedPointer<IFrameworkSettings> settingsForTestFramework(const Core::Id &frameworkId) const;
|
||||
void synchronizeSettings(QSettings *s);
|
||||
bool isActive(const Core::Id &frameworkId) const;
|
||||
bool hasActiveFrameworks() const;
|
||||
|
||||
@@ -62,6 +69,7 @@ private:
|
||||
QVector<Core::Id> activeFrameworkIds() const;
|
||||
explicit TestFrameworkManager();
|
||||
QHash<Core::Id, ITestFramework *> m_registeredFrameworks;
|
||||
QHash<Core::Id, QSharedPointer<IFrameworkSettings> > m_frameworkSettings;
|
||||
TestTreeModel *m_testTreeModel;
|
||||
TestRunner *m_testRunner;
|
||||
|
||||
|
@@ -64,8 +64,7 @@ public:
|
||||
ProjectExplorer::StandardRunnable r;
|
||||
QTC_ASSERT(m_testConfig, return r);
|
||||
r.executable = m_testConfig->targetFile();
|
||||
r.commandLineArguments = m_testConfig->argumentsForTestRunner(
|
||||
*AutotestPlugin::instance()->settings()).join(' ');
|
||||
r.commandLineArguments = m_testConfig->argumentsForTestRunner().join(' ');
|
||||
r.workingDirectory = m_testConfig->workingDirectory();
|
||||
r.environment = m_testConfig->environment();
|
||||
r.runMode = ProjectExplorer::ApplicationLauncher::Gui;
|
||||
|
@@ -138,7 +138,7 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface,
|
||||
continue;
|
||||
}
|
||||
|
||||
testProcess.setArguments(testConfiguration->argumentsForTestRunner(settings));
|
||||
testProcess.setArguments(testConfiguration->argumentsForTestRunner());
|
||||
testProcess.setWorkingDirectory(testConfiguration->workingDirectory());
|
||||
if (Utils::HostOsInfo::isWindowsHost())
|
||||
environment.insert("QT_LOGGING_TO_CONSOLE", "1");
|
||||
@@ -304,8 +304,7 @@ void TestRunner::debugTests()
|
||||
|
||||
Debugger::DebuggerStartParameters sp;
|
||||
sp.inferior.executable = commandFilePath;
|
||||
sp.inferior.commandLineArguments = config->argumentsForTestRunner(
|
||||
*AutotestPlugin::instance()->settings()).join(' ');
|
||||
sp.inferior.commandLineArguments = config->argumentsForTestRunner().join(' ');
|
||||
sp.inferior.environment = config->environment();
|
||||
sp.inferior.workingDirectory = config->workingDirectory();
|
||||
sp.displayName = config->displayName();
|
||||
|
@@ -24,6 +24,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "testsettings.h"
|
||||
#include "autotestconstants.h"
|
||||
#include "testframeworkmanager.h"
|
||||
|
||||
#include <coreplugin/id.h>
|
||||
@@ -33,7 +34,6 @@
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
static const char group[] = "Autotest";
|
||||
static const char timeoutKey[] = "Timeout";
|
||||
static const char omitInternalKey[] = "OmitInternal";
|
||||
static const char omitRunConfigWarnKey[] = "OmitRCWarnings";
|
||||
@@ -50,7 +50,7 @@ TestSettings::TestSettings()
|
||||
|
||||
void TestSettings::toSettings(QSettings *s) const
|
||||
{
|
||||
s->beginGroup(group);
|
||||
s->beginGroup(Constants::SETTINGSGROUP);
|
||||
s->setValue(timeoutKey, timeout);
|
||||
s->setValue(omitInternalKey, omitInternalMssg);
|
||||
s->setValue(omitRunConfigWarnKey, omitRunConfigWarn);
|
||||
@@ -60,20 +60,12 @@ void TestSettings::toSettings(QSettings *s) const
|
||||
// store frameworks and their current active state
|
||||
for (const Core::Id &id : frameworks.keys())
|
||||
s->setValue(QLatin1String(id.name()), frameworks.value(id));
|
||||
|
||||
s->beginGroup(qtTestSettings.name());
|
||||
qtTestSettings.toSettings(s);
|
||||
s->endGroup();
|
||||
s->beginGroup(gTestSettings.name());
|
||||
gTestSettings.toSettings(s);
|
||||
s->endGroup();
|
||||
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
void TestSettings::fromSettings(QSettings *s)
|
||||
{
|
||||
s->beginGroup(group);
|
||||
s->beginGroup(Constants::SETTINGSGROUP);
|
||||
timeout = s->value(timeoutKey, defaultTimeout).toInt();
|
||||
omitInternalMssg = s->value(omitInternalKey, true).toBool();
|
||||
omitRunConfigWarn = s->value(omitRunConfigWarnKey, false).toBool();
|
||||
@@ -88,14 +80,6 @@ void TestSettings::fromSettings(QSettings *s)
|
||||
frameworks.insert(id, s->value(QLatin1String(id.name()),
|
||||
frameworkManager->isActive(id)).toBool());
|
||||
}
|
||||
|
||||
s->beginGroup(qtTestSettings.name());
|
||||
qtTestSettings.fromSettings(s);
|
||||
s->endGroup();
|
||||
s->beginGroup(gTestSettings.name());
|
||||
gTestSettings.fromSettings(s);
|
||||
s->endGroup();
|
||||
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
|