Merge "Merge remote-tracking branch 'origin/9.0'"

This commit is contained in:
The Qt Project
2022-10-18 14:58:14 +00:00
286 changed files with 5071 additions and 4075 deletions

View File

@@ -220,22 +220,35 @@ static void setupInstallSettings(QString &installSettingspath)
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), QLatin1String(Core::Constants::IDE_CASED_ID)));
installSettingspath.clear();
}
// Check if the default install settings contain a setting for the actual install settings.
// This can be an absolute path, or a path relative to applicationDirPath().
// The result is interpreted like -settingspath, but for SystemScope
static const char kInstallSettingsKey[] = "Settings/InstallSettings";
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope,
installSettingspath.isEmpty() ? resourcePath() : installSettingspath);
QSettings installSettings(QSettings::IniFormat, QSettings::UserScope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(Core::Constants::IDE_CASED_ID));
if (installSettings.contains(kInstallSettingsKey)) {
QString installSettingsPath = installSettings.value(kInstallSettingsKey).toString();
if (QDir::isRelativePath(installSettingsPath))
installSettingsPath = applicationDirPath() + '/' + installSettingsPath;
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, installSettingsPath);
}
// Check if the default install settings contain a setting for the actual install settings.
// This can be an absolute path, or a path relative to applicationDirPath().
// The result is interpreted like -settingspath, but for SystemScope.
//
// Through the sdktool split that is upcoming, the new install settings might redirect
// yet a second time. So try this a few times.
// (Only the first time with QSettings::UserScope, to allow setting the install settings path
// in the user settings.)
QSettings::Scope scope = QSettings::UserScope;
int count = 0;
bool containsInstallSettingsKey = false;
do {
QSettings installSettings(QSettings::IniFormat, scope,
QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR),
QLatin1String(Core::Constants::IDE_CASED_ID));
containsInstallSettingsKey = installSettings.contains(kInstallSettingsKey);
if (containsInstallSettingsKey) {
QString newInstallSettingsPath = installSettings.value(kInstallSettingsKey).toString();
if (QDir::isRelativePath(newInstallSettingsPath))
newInstallSettingsPath = applicationDirPath() + '/' + newInstallSettingsPath;
QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, newInstallSettingsPath);
}
scope = QSettings::SystemScope; // UserScope only the first time we check
++count;
} while (containsInstallSettingsKey && count < 3);
}
static Utils::QtcSettings *createUserSettings()

View File

@@ -38,13 +38,14 @@ Item {
id: dragArea
anchors.fill: txt
drag.target: dragger
cursorShape: dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
drag.minimumY: dragging ? 0 : -dragOffset // Account for parent change below
drag.maximumY: visibleHeight - (dragging ? 0 : dragOffset)
cursorShape: labelContainer.dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
// Account for parent change below
drag.minimumY: labelContainer.dragging ? 0 : -labelContainer.dragOffset
drag.maximumY: labelContainer.visibleHeight - (labelContainer.dragging ? 0 : labelContainer.dragOffset)
drag.axis: Drag.YAxis
hoverEnabled: true
ToolTip {
text: model.tooltip || labelContainer.text
text: labelContainer.model.tooltip || labelContainer.text
visible: enabled && parent.containsMouse
delay: 1000
}
@@ -76,30 +77,30 @@ Item {
text: labelContainer.text
color: Theme.color(Theme.PanelTextColorLight)
height: model ? model.defaultRowHeight : 0
height: labelContainer.model ? labelContainer.model.defaultRowHeight : 0
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
Column {
id: labelsArea
property QtObject parentModel: model
property QtObject parentModel: labelContainer.model
anchors.top: txt.bottom
visible: expanded
visible: labelContainer.expanded
Repeater {
model: expanded ? labels.length : 0
model: labelContainer.expanded ? labelContainer.labels.length : 0
Loader {
id: loader
// Initially y == 0 for all the items. Don't enable them until they have been moved
// into place.
property int offset: (index === 0 || y > 0) ? (y + txt.height) : contentHeight
active: contentBottom > offset
active: labelContainer.contentBottom > offset
width: labelContainer.width
height: labelsArea.parentModel ? labelsArea.parentModel.rowHeight(index + 1) : 0
sourceComponent: RowLabel {
label: labels[index];
label: labelContainer.labels[index];
onSelectBySelectionId: {
if (labelContainer.model.hasMixedTypesInExpandedState)
return;
@@ -127,11 +128,12 @@ Item {
property var texts: []
property int currentNote: -1
Connections {
target: notesModel
target: labelContainer.notesModel
function onChanged(typeId, modelId, timelineIndex) {
// This will only be called if notesModel != null.
if (modelId === -1 || modelId === model.modelId) {
var notes = notesModel.byTimelineModel(model.modelId);
if (modelId === -1 || modelId === labelContainer.model.modelId) {
var notes =
labelContainer.notesModel.byTimelineModel(labelContainer.model.modelId);
var newTexts = [];
var newEventIds = [];
for (var i in notes) {
@@ -161,11 +163,11 @@ Item {
anchors.verticalCenter: txt.verticalCenter
anchors.right: parent.right
implicitHeight: txt.height - 1
enabled: expanded || (model && !model.empty)
imageSource: expanded ? "image://icons/close_split" : "image://icons/split"
ToolTip.text: expanded ? qsTranslate("Tracing", "Collapse category")
: qsTranslate("Tracing", "Expand category")
onClicked: model.expanded = !expanded
enabled: labelContainer.expanded || (labelContainer.model && !labelContainer.model.empty)
imageSource: labelContainer.expanded ? "image://icons/close_split" : "image://icons/split"
ToolTip.text: labelContainer.expanded ? qsTranslate("Tracing", "Collapse category")
: qsTranslate("Tracing", "Expand category")
onClicked: labelContainer.model.expanded = !labelContainer.expanded
}
Rectangle {
@@ -199,7 +201,7 @@ Item {
when: dragger.Drag.active
ParentChange {
target: dragger
parent: draggerParent
parent: labelContainer.draggerParent
}
PropertyChanges {
target: dragger

View File

@@ -26,18 +26,18 @@ Item {
x: parent === null ? 0 : parent.width * FlameGraph.relativePosition
Rectangle {
border.color: borderColor
border.width: borderWidth
color: Qt.hsla((level % 12) / 72, 0.9 + Math.random() / 10,
border.color: flamegraphItem.borderColor
border.width: flamegraphItem.borderWidth
color: Qt.hsla((flamegraphItem.level % 12) / 72, 0.9 + Math.random() / 10,
0.45 + Math.random() / 10, 0.9 + Math.random() / 10);
height: itemHeight;
height: flamegraphItem.itemHeight
anchors.left: flamegraphItem.left
anchors.right: flamegraphItem.right
anchors.bottom: flamegraphItem.bottom
TimelineText {
id: text
visible: textVisible
visible: flamegraphItem.textVisible
anchors.fill: parent
anchors.margins: 5
verticalAlignment: Text.AlignVCenter
@@ -45,7 +45,7 @@ Item {
text: flamegraphItem.text
elide: Text.ElideRight
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
font.bold: isSelected
font.bold: flamegraphItem.isSelected
}
MouseArea {

View File

@@ -129,7 +129,7 @@ Rectangle {
color: Theme.color(Theme.PanelStatusBarBackgroundColor)
modelProxy: timelineModelAggregator
zoomer: zoomControl
reverseSelect: shiftPressed
reverseSelect: root.shiftPressed
onMoveCategories: (sourceIndex, targetIndex) => {
content.moveCategories(sourceIndex, targetIndex)
@@ -228,7 +228,7 @@ Rectangle {
MouseArea {
id: selectionRangeControl
enabled: selectionRangeMode &&
enabled: root.selectionRangeMode &&
selectionRange.creationState !== selectionRange.creationFinished
anchors.right: content.right
anchors.left: buttonsBar.right
@@ -269,7 +269,7 @@ Rectangle {
interactive: false
x: content.x
y: content.y
height: (selectionRangeMode &&
height: (root.selectionRangeMode &&
selectionRange.creationState !== selectionRange.creationInactive) ?
content.height : 0
width: content.width
@@ -328,7 +328,7 @@ Rectangle {
endTime: zoomControl.selectionEnd
referenceDuration: zoomControl.rangeDuration
showDuration: selectionRange.rangeWidth > 1
hasContents: selectionRangeMode &&
hasContents: root.selectionRangeMode &&
selectionRange.creationState !== selectionRange.creationInactive
onRecenter: {
@@ -356,7 +356,7 @@ Rectangle {
locked: content.selectionLocked
onRecenterOnItem: {
content.select(selectedModel, selectedItem)
content.select(root.selectedModel, root.selectedItem)
}
onLockedChanged: {
@@ -368,10 +368,11 @@ Rectangle {
}
onUpdateNote: (text) => {
if (timelineModelAggregator.notes && selectedModel != -1 && selectedItem != -1) {
if (timelineModelAggregator.notes && root.selectedModel != -1
&& root.selectedItem != -1) {
timelineModelAggregator.notes.setText(
timelineModelAggregator.models[selectedModel].modelId,
selectedItem, text);
timelineModelAggregator.models[root.selectedModel].modelId,
root.selectedItem, text);
}
}

View File

@@ -46,8 +46,8 @@ Rectangle {
}
Connections {
target: zoomer
function onRangeChanged() { updateRangeMover(); }
target: overview.zoomer
function onRangeChanged() { overview.updateRangeMover(); }
}
TimeDisplay {
@@ -59,9 +59,9 @@ Rectangle {
height: 10
fontSize: 6
labelsHeight: 10
windowStart: zoomer.traceStart
alignedWindowStart: zoomer.traceStart
rangeDuration: zoomer.traceDuration
windowStart: overview.zoomer.traceStart
alignedWindowStart: overview.zoomer.traceStart
rangeDuration: overview.zoomer.traceDuration
contentX: 0
offsetX: 0
}
@@ -75,35 +75,35 @@ Rectangle {
id: renderArea
Repeater {
model: modelProxy.models
model: overview.modelProxy.models
TimelineOverviewRenderer {
model: modelData
zoomer: overview.zoomer
notes: modelProxy.notes
notes: overview.modelProxy.notes
width: renderArea.width
height: renderArea.height / modelProxy.models.length
height: renderArea.height / overview.modelProxy.models.length
}
}
}
Repeater {
id: noteSigns
property var modelsById: modelProxy.models.reduce(function(prev, model) {
property var modelsById: overview.modelProxy.models.reduce(function(prev, model) {
prev[model.modelId] = model;
return prev;
}, {});
property int vertSpace: renderArea.height / 7
property color noteColor: Theme.color(Theme.Timeline_HighlightColor)
readonly property double spacing: parent.width / zoomer.traceDuration
readonly property double spacing: parent.width / overview.zoomer.traceDuration
model: modelProxy.notes ? modelProxy.notes.count : 0
model: overview.modelProxy.notes ? overview.modelProxy.notes.count : 0
Item {
property int timelineIndex: modelProxy.notes.timelineIndex(index)
property int timelineModel: modelProxy.notes.timelineModel(index)
property int timelineIndex: overview.modelProxy.notes.timelineIndex(index)
property int timelineModel: overview.modelProxy.notes.timelineModel(index)
property double startTime: noteSigns.modelsById[timelineModel].startTime(timelineIndex)
property double endTime: noteSigns.modelsById[timelineModel].endTime(timelineIndex)
x: ((startTime + endTime) / 2 - zoomer.traceStart) * noteSigns.spacing
x: ((startTime + endTime) / 2 - overview.zoomer.traceStart) * noteSigns.spacing
y: timebar.height + noteSigns.vertSpace
height: noteSigns.vertSpace * 5
width: 2
@@ -156,7 +156,7 @@ Rectangle {
RangeMover {
id: rangeMover
visible: modelProxy.height > 0
visible: overview.modelProxy.height > 0
onRangeLeftChanged: overview.updateZoomer()
onRangeRightChanged: overview.updateZoomer()
}

View File

@@ -60,9 +60,9 @@ Item {
Rectangle {
id: titleBar
width: parent.width
height: titleBarHeight
height: rangeDetails.titleBarHeight
color: Theme.color(Theme.Timeline_PanelHeaderColor)
border.width: borderWidth
border.width: rangeDetails.borderWidth
border.color: Theme.color(Theme.PanelTextColorMid)
TimelineText {
@@ -72,8 +72,8 @@ Item {
verticalAlignment: Text.AlignVCenter
anchors.left: parent.left
anchors.right: closeIcon.left
anchors.leftMargin: outerMargin
anchors.rightMargin: innerMargin
anchors.leftMargin: rangeDetails.outerMargin
anchors.rightMargin: rangeDetails.innerMargin
anchors.top: parent.top
anchors.bottom: parent.bottom
color: Theme.color(Theme.PanelTextColorLight)
@@ -93,11 +93,11 @@ Item {
ImageToolButton {
id: lockIcon
imageSource: "image://icons/lock_" + (locked ? "closed" : "open")
imageSource: "image://icons/lock_" + (rangeDetails.locked ? "closed" : "open")
anchors.top: closeIcon.top
anchors.right: closeIcon.left
implicitHeight: typeTitle.height
onClicked: locked = !locked
onClicked: rangeDetails.locked = !rangeDetails.locked
ToolTip.text: qsTranslate("Tracing", "View event information on mouseover.")
}
@@ -121,7 +121,7 @@ Item {
anchors.right: parent.right
anchors.bottom: dragHandle.bottom
border.width: borderWidth
border.width: rangeDetails.borderWidth
border.color: Theme.color(Theme.PanelTextColorMid)
}
@@ -130,17 +130,17 @@ Item {
anchors.left: parent.left
anchors.top: titleBar.bottom
anchors.topMargin: innerMargin
anchors.leftMargin: outerMargin
anchors.rightMargin: outerMargin
anchors.topMargin: rangeDetails.innerMargin
anchors.leftMargin: rangeDetails.outerMargin
anchors.rightMargin: rangeDetails.outerMargin
spacing: innerMargin
spacing: rangeDetails.innerMargin
columns: 2
property int minimumWidth: minimumInnerWidth
property int minimumWidth: rangeDetails.minimumInnerWidth
onPositioningComplete: {
// max(width of longest label * 2, minimumInnerWidth)
var result = minimumInnerWidth;
var result = rangeDetails.minimumInnerWidth;
for (var i = 0; i < children.length; ++i) {
if (children[i].isLabel)
result = Math.max(children[i].implicitWidth * 2 + innerMargin, result);
@@ -149,12 +149,14 @@ Item {
minimumWidth = result + 2 * outerMargin;
}
property int labelWidth: Math.ceil((minimumWidth - innerMargin) / 2) - outerMargin
property int valueWidth: dragHandle.x - labelWidth - innerMargin - outerMargin
property int labelWidth: Math.ceil((minimumWidth - rangeDetails.innerMargin) / 2)
- rangeDetails.outerMargin
property int valueWidth: dragHandle.x - labelWidth - rangeDetails.innerMargin
- rangeDetails.outerMargin
onMinimumWidthChanged: {
if (dragHandle.x < minimumWidth - outerMargin)
dragHandle.x = minimumWidth - outerMargin;
if (dragHandle.x < minimumWidth - rangeDetails.outerMargin)
dragHandle.x = minimumWidth - rangeDetails.outerMargin;
}
Repeater {
@@ -174,9 +176,9 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: outerMargin
anchors.rightMargin: outerMargin
anchors.topMargin: visible ? innerMargin : 0
anchors.leftMargin: rangeDetails.outerMargin
anchors.rightMargin: rangeDetails.outerMargin
anchors.topMargin: visible ? rangeDetails.innerMargin : 0
anchors.top: col.bottom
height: visible ? implicitHeight : 0
@@ -201,7 +203,7 @@ Item {
Timer {
id: saveTimer
onTriggered: {
if (!rangeDetails.readOnly)
if (!noteEdit.readOnly)
rangeDetails.updateNote(noteEdit.text);
}
interval: 1000
@@ -211,15 +213,15 @@ Item {
Item {
id: dragHandle
width: outerMargin
height: outerMargin
x: initialWidth
width: rangeDetails.outerMargin
height: rangeDetails.outerMargin
x: rangeDetails.initialWidth
anchors.top: noteEdit.bottom
clip: true
MouseArea {
anchors.fill: parent
drag.target: parent
drag.minimumX: col.minimumWidth - outerMargin
drag.minimumX: col.minimumWidth - rangeDetails.outerMargin
drag.axis: Drag.XAxis
cursorShape: Qt.SizeHorCursor
}

View File

@@ -30,7 +30,9 @@ Item {
return Qt.rgba(color.r, color.g, color.b, Math.max(Math.min(color.a, 0.7), 0.3));
}
color: width > 1 ? alphaColor(dragArea.pressed ? dragColor : rangeColor) : singleLineColor
color: width > 1 ? alphaColor(dragArea.pressed ? rangeMover.dragColor
: rangeMover.rangeColor)
: rangeMover.singleLineColor
}
Item {
@@ -49,7 +51,7 @@ Item {
height: parent.height
anchors.right: parent.left
width: 7
color: handleColor
color: rangeMover.handleColor
visible: false
Image {
source: "image://icons/range_handle"
@@ -72,7 +74,7 @@ Item {
anchors.fill: leftBorderHandle
drag.target: leftRange
drag.axis: "XAxis"
drag.axis: Drag.XAxis
drag.minimumX: 0
drag.maximumX: rangeMover.width
drag.onActiveChanged: drag.maximumX = rightRange.x
@@ -102,7 +104,7 @@ Item {
height: parent.height
anchors.left: parent.right
width: 7
color: handleColor
color: rangeMover.handleColor
visible: false
Image {
source: "image://icons/range_handle"
@@ -125,7 +127,7 @@ Item {
anchors.fill: rightBorderHandle
drag.target: rightRange
drag.axis: "XAxis"
drag.axis: Drag.XAxis
drag.minimumX: 0
drag.maximumX: rangeMover.width
drag.onActiveChanged: drag.minimumX = leftRange.x
@@ -150,7 +152,7 @@ Item {
anchors.fill: selectedRange
drag.target: leftRange
drag.axis: "XAxis"
drag.axis: Drag.XAxis
drag.minimumX: 0
drag.maximumX: rangeMover.width - origWidth
drag.onActiveChanged: origWidth = selectedRange.width

View File

@@ -42,7 +42,7 @@ Button {
onPressed: resizing = true
onReleased: resizing = false
height: dragHeight
height: button.dragHeight
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right

View File

@@ -45,8 +45,8 @@ RangeMover {
onRangeLeftChanged: updateZoomer()
Connections {
target: zoomer
function onWindowChanged() { updateRange(); }
target: selectionRange.zoomer
function onWindowChanged() { selectionRange.updateRange(); }
}
function setPos(pos) {

View File

@@ -25,8 +25,8 @@ Item {
// keep inside view
Connections {
target: selectionRangeDetails.parent
function onWidthChanged() { fitInView(); }
function onHeightChanged() { fitInView(); }
function onWidthChanged() { selectionRangeDetails.fitInView(); }
function onHeightChanged() { selectionRangeDetails.fitInView(); }
}
function fitInView() {
@@ -79,14 +79,17 @@ Item {
id: details
property var contents: [
qsTranslate("Tracing", "Start") + ":",
TimeFormatter.format(startTime, referenceDuration),
TimeFormatter.format(selectionRangeDetails.startTime,
selectionRangeDetails.referenceDuration),
(qsTranslate("Tracing", "End") + ":"),
TimeFormatter.format(endTime, referenceDuration),
TimeFormatter.format(selectionRangeDetails.endTime,
selectionRangeDetails.referenceDuration),
(qsTranslate("Tracing", "Duration") + ":"),
TimeFormatter.format(duration, referenceDuration)
TimeFormatter.format(selectionRangeDetails.duration,
selectionRangeDetails.referenceDuration)
]
model: showDuration ? 6 : 2
model: selectionRangeDetails.showDuration ? 6 : 2
Detail {
isLabel: index % 2 === 0
text: details.contents[index]

View File

@@ -40,7 +40,8 @@ Item {
id: timeDisplayArea
property int firstBlock: timeDisplay.offsetX / timeDisplay.pixelsPerBlock
property int offset: repeater.model > 0 ? repeater.model - (firstBlock % repeater.model) : 0;
property int offset: repeater.model > 0 ? repeater.model - (firstBlock % repeater.model)
: 0;
Repeater {
id: repeater

View File

@@ -78,11 +78,11 @@ Item {
property double maxVal: scope.model ? scope.model.rowMaxValue(index) : 0
property double valDiff: maxVal - minVal
property bool scaleVisible: scope.model && scope.model.expanded &&
height > scaleMinHeight && valDiff > 0
height > timeMarks.scaleMinHeight && valDiff > 0
property double stepVal: {
var ret = 1;
var ugly = Math.ceil(valDiff / Math.floor(height / scaleStepping));
var ugly = Math.ceil(valDiff / Math.floor(height / timeMarks.scaleStepping));
while (isFinite(ugly) && ugly > 1) {
ugly /= 2;
ret *= 2;
@@ -122,7 +122,8 @@ Item {
anchors.bottomMargin: 2
anchors.leftMargin: 2
anchors.left: parent.left
text: prettyPrintScale(scaleItem.minVal + index * scaleItem.stepVal)
text: prettyPrintScale(scaleItem.minVal
+ index * scaleItem.stepVal)
}
Rectangle {

View File

@@ -96,11 +96,11 @@ Flickable {
DelegateModel {
id: timelineModel
model: modelProxy.models
model: flick.modelProxy.models
delegate: TimelineRenderer {
id: renderer
model: modelData
notes: modelProxy.notes
notes: flick.modelProxy.notes
zoomer: flick.zoomer
selectionLocked: flick.selectionLocked
x: 0
@@ -124,29 +124,29 @@ Flickable {
}
function recenter() {
if (modelData.endTime(selectedItem) < zoomer.rangeStart ||
modelData.startTime(selectedItem) > zoomer.rangeEnd) {
if (modelData.endTime(renderer.selectedItem) < zoomer.rangeStart ||
modelData.startTime(renderer.selectedItem) > zoomer.rangeEnd) {
var newStart = Math.max((modelData.startTime(selectedItem) +
modelData.endTime(selectedItem) -
var newStart = Math.max((modelData.startTime(renderer.selectedItem) +
modelData.endTime(renderer.selectedItem) -
zoomer.rangeDuration) / 2, zoomer.traceStart);
zoomer.setRange(newStart,
Math.min(newStart + zoomer.rangeDuration, zoomer.traceEnd));
}
var row = modelData.row(selectedItem);
var row = renderer.model.row(renderer.selectedItem);
var rowStart = modelData.rowOffset(row) + y;
var rowEnd = rowStart + modelData.rowHeight(row);
if (rowStart < flick.contentY || rowEnd - flick.height > flick.contentY)
flick.contentY = (rowStart + rowEnd - flick.height) / 2;
}
onSelectedItemChanged: flick.propagateSelection(index, selectedItem);
onSelectedItemChanged: flick.propagateSelection(index, renderer.selectedItem);
Connections {
target: model
target: renderer.model
function onDetailsChanged() {
if (selectedItem != -1) {
if (renderer.selectedItem != -1) {
flick.propagateSelection(-1, -1);
flick.propagateSelection(index, selectedItem);
}

View File

@@ -38,7 +38,7 @@ Flickable {
// As we cannot retrieve items by visible index we keep an array of row counts here,
// for the time marks to draw the row backgrounds in the right colors.
property var rowCounts: new Array(modelProxy.models.length)
property var rowCounts: new Array(categories.modelProxy.models.length)
function updateRowCount(visualIndex, rowCount) {
if (rowCounts[visualIndex] !== rowCount) {
@@ -48,7 +48,7 @@ Flickable {
}
}
model: modelProxy.models
model: categories.modelProxy.models
delegate: Loader {
id: loader
asynchronous: y < categories.contentY + categories.height &&
@@ -73,7 +73,7 @@ Flickable {
CategoryLabel {
id: label
model: modelData
notesModel: modelProxy.notes
notesModel: categories.modelProxy.notes
visualIndex: loader.visualIndex
dragging: categories.dragging
reverseSelect: categories.reverseSelect
@@ -114,7 +114,7 @@ Flickable {
TimeMarks {
id: timeMarks
model: modelData
mockup: modelProxy.height === 0
mockup: categories.modelProxy.height === 0
anchors.right: parent.right
anchors.left: label.right
anchors.top: parent.top

View File

@@ -20,7 +20,7 @@ Item {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: scaleHeight
height: rulersParent.scaleHeight
onClicked: (mouse) => {
rulersModel.append({
@@ -36,7 +36,8 @@ Item {
if (index >= 0) {
rulersModel.setProperty(
index, "timestamp",
(x + contentX) * viewTimePerPixel + windowStart);
(x + rulersParent.contentX) * rulersParent.viewTimePerPixel
+ rulersParent.windowStart);
}
}
}
@@ -46,14 +47,15 @@ Item {
Item {
id: ruler
x: (timestamp - windowStart) / viewTimePerPixel - 1 - contentX
x: (timestamp - rulersParent.windowStart) / rulersParent.viewTimePerPixel
- 1 - rulersParent.contentX
y: 0
width: 2
height: rulersParent.height
Rectangle {
id: arrow
height: scaleHeight
width: scaleHeight
height: rulersParent.scaleHeight
width: rulersParent.scaleHeight
rotation: 45
anchors.verticalCenter: parent.top
anchors.horizontalCenter: parent.horizontalCenter
@@ -86,7 +88,7 @@ Item {
Rectangle {
anchors.top: arrow.bottom
anchors.horizontalCenter: ruler.horizontalCenter
width: scaleHeight / 4
width: rulersParent.scaleHeight / 4
height: width
color: Theme.color(Theme.Timeline_PanelBackgroundColor)

View File

@@ -11,4 +11,3 @@ Text {
renderType: Text.NativeRendering
color: Theme.color(Theme.Timeline_TextColor)
}

View File

@@ -28,6 +28,7 @@ add_qtc_library(Utils
delegates.cpp delegates.h
detailsbutton.cpp detailsbutton.h
detailswidget.cpp detailswidget.h
devicefileaccess.cpp devicefileaccess.h
deviceshell.cpp deviceshell.h
differ.cpp differ.h
displayname.cpp displayname.h

View File

@@ -1412,6 +1412,12 @@ CommandLine::CommandLine(const FilePath &exe, const QStringList &args)
addArgs(args);
}
CommandLine::CommandLine(const FilePath &exe, const QStringList &args, OsType osType)
: m_executable(exe)
{
addArgs(args, osType);
}
CommandLine::CommandLine(const FilePath &exe, const QString &args, RawType)
: m_executable(exe)
{
@@ -1438,12 +1444,23 @@ void CommandLine::addArg(const QString &arg)
ProcessArgs::addArg(&m_arguments, arg, m_executable.osType());
}
void CommandLine::addArg(const QString &arg, OsType osType)
{
ProcessArgs::addArg(&m_arguments, arg, osType);
}
void CommandLine::addArgs(const QStringList &inArgs)
{
for (const QString &arg : inArgs)
addArg(arg);
}
void CommandLine::addArgs(const QStringList &inArgs, OsType osType)
{
for (const QString &arg : inArgs)
addArg(arg, osType);
}
// Adds cmd's executable and arguments one by one to this commandline.
// Useful for 'sudo', 'nice', etc
void CommandLine::addCommandLineAsArgs(const CommandLine &cmd)

View File

@@ -115,12 +115,15 @@ public:
CommandLine();
explicit CommandLine(const FilePath &executable);
CommandLine(const FilePath &exe, const QStringList &args);
CommandLine(const FilePath &exe, const QStringList &args, OsType osType);
CommandLine(const FilePath &exe, const QString &unparsedArgs, RawType);
static CommandLine fromUserInput(const QString &cmdline, MacroExpander *expander = nullptr);
void addArg(const QString &arg);
void addArg(const QString &arg, OsType osType);
void addArgs(const QStringList &inArgs);
void addArgs(const QStringList &inArgs, OsType osType);
void addArgs(const QString &inArgs, RawType);
void prependArgs(const QStringList &inArgs);

View File

@@ -7,9 +7,10 @@
#include <utils/icon.h>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QGuiApplication>
#include <QPaintEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QStyleOption>
#include <qdrawutil.h>
@@ -69,6 +70,8 @@ DetailsButton::DetailsButton(QWidget *parent)
{
setText(tr("Details"));
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
if (HostOsInfo::isMacHost())
setFont(QGuiApplication::font());
}
QSize DetailsButton::sizeHint() const

View File

@@ -5,6 +5,7 @@
#include "utils_global.h"
#include <QCoreApplication>
#include <QToolButton>
QT_BEGIN_NAMESPACE
@@ -44,6 +45,7 @@ public:
class QTCREATOR_UTILS_EXPORT DetailsButton : public ExpandButton
{
Q_DECLARE_TR_FUNCTIONS(Utils::DetailsButton);
public:
DetailsButton(QWidget *parent = nullptr);
QSize sizeHint() const override;

View File

@@ -0,0 +1,975 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "devicefileaccess.h"
#include "algorithm.h"
#include "qtcassert.h"
#include "hostosinfo.h"
#include "commandline.h"
#include <QCoreApplication>
#include <QOperatingSystemVersion>
#include <QRegularExpression>
#include <QStorageInfo>
#ifdef Q_OS_WIN
#ifdef QTCREATOR_PCH_H
#define CALLBACK WINAPI
#endif
#include <qt_windows.h>
#include <shlobj.h>
#else
#include <qplatformdefs.h>
#endif
namespace Utils {
// DeviceFileAccess
DeviceFileAccess::~DeviceFileAccess() = default;
QString DeviceFileAccess::mapToDevicePath(const FilePath &filePath) const
{
return filePath.path();
}
bool DeviceFileAccess::isExecutableFile(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::isReadableFile(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::isWritableFile(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::isFile(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::isDirectory(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::isSymLink(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const
{
if (isWritableDirectory(filePath))
return true;
return createDirectory(filePath);
}
bool DeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::createDirectory(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::exists(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::removeFile(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
{
Q_UNUSED(filePath)
Q_UNUSED(error)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const
{
Q_UNUSED(filePath)
Q_UNUSED(target)
QTC_CHECK(false);
return false;
}
bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
{
Q_UNUSED(filePath)
Q_UNUSED(target)
QTC_CHECK(false);
return false;
}
OsType DeviceFileAccess::osType(const FilePath &filePath) const
{
Q_UNUSED(filePath)
return OsTypeOther;
}
FilePath DeviceFileAccess::symLinkTarget(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return {};
}
void DeviceFileAccess::iterateDirectory(
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const
{
Q_UNUSED(filePath)
Q_UNUSED(callBack)
Q_UNUSED(filter)
QTC_CHECK(false);
}
std::optional<QByteArray> DeviceFileAccess::fileContents(
const FilePath &filePath,
qint64 limit,
qint64 offset) const
{
Q_UNUSED(filePath)
Q_UNUSED(limit)
Q_UNUSED(offset)
QTC_CHECK(false);
return {};
}
bool DeviceFileAccess::writeFileContents(
const FilePath &filePath,
const QByteArray &data,
qint64 offset) const
{
Q_UNUSED(filePath)
Q_UNUSED(data)
Q_UNUSED(offset)
QTC_CHECK(false);
return false;
}
FilePathInfo DeviceFileAccess::filePathInfo(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return {};
}
QDateTime DeviceFileAccess::lastModified(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return {};
}
QFile::Permissions DeviceFileAccess::permissions(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return {};
}
bool DeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
qint64 DeviceFileAccess::fileSize(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return false;
}
qint64 DeviceFileAccess::bytesAvailable(const FilePath &filePath) const
{
Q_UNUSED(filePath)
QTC_CHECK(false);
return -1;
}
QByteArray DeviceFileAccess::fileId(const FilePath &filePath) const
{
Q_UNUSED(filePath);
QTC_CHECK(false);
return {};
}
void DeviceFileAccess::asyncFileContents(
const FilePath &filePath,
const Continuation<std::optional<QByteArray>> &cont,
qint64 limit,
qint64 offset) const
{
cont(fileContents(filePath, limit, offset));
}
void DeviceFileAccess::asyncWriteFileContents(
const FilePath &filePath,
const Continuation<bool> &cont,
const QByteArray &data,
qint64 offset) const
{
cont(writeFileContents(filePath, data, offset));
}
void DeviceFileAccess::asyncCopyFile(
const FilePath &filePath,
const Continuation<bool> &cont,
const FilePath &target) const
{
cont(copyFile(filePath, target));
}
// DesktopDeviceFileAccess
DesktopDeviceFileAccess::~DesktopDeviceFileAccess() = default;
DesktopDeviceFileAccess *DesktopDeviceFileAccess::instance()
{
static DesktopDeviceFileAccess theInstance;
return &theInstance;
}
bool DesktopDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.isExecutable() && !fi.isDir();
}
bool DesktopDeviceFileAccess::isReadableFile(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.exists() && fi.isFile() && fi.isReadable();
}
bool DesktopDeviceFileAccess::isWritableFile(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.exists() && fi.isFile() && fi.isWritable();
}
bool DesktopDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.exists() && fi.isDir() && fi.isReadable();
}
bool DesktopDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.exists() && fi.isDir() && fi.isWritable();
}
bool DesktopDeviceFileAccess::isFile(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.isFile();
}
bool DesktopDeviceFileAccess::isDirectory(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.isDir();
}
bool DesktopDeviceFileAccess::isSymLink(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
return fi.isSymLink();
}
bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const
{
const QFileInfo fi(filePath.path());
if (fi.isDir() && fi.isWritable())
return true;
return QDir().mkpath(filePath.path());
}
bool DesktopDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
{
QFile f(filePath.path());
if (f.exists())
return true;
f.open(QFile::WriteOnly);
f.close();
return f.exists();
}
bool DesktopDeviceFileAccess::createDirectory(const FilePath &filePath) const
{
QDir dir(filePath.path());
return dir.mkpath(dir.absolutePath());
}
bool DesktopDeviceFileAccess::exists(const FilePath &filePath) const
{
return !filePath.isEmpty() && QFileInfo::exists(filePath.path());
}
bool DesktopDeviceFileAccess::removeFile(const FilePath &filePath) const
{
return QFile::remove(filePath.path());
}
bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
{
QTC_ASSERT(!filePath.needsDevice(), return false);
QFileInfo fileInfo = filePath.toFileInfo();
if (!fileInfo.exists() && !fileInfo.isSymLink())
return true;
QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser);
if (fileInfo.isDir()) {
QDir dir(fileInfo.absoluteFilePath());
dir.setPath(dir.canonicalPath());
if (dir.isRoot()) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils",
"Refusing to remove root directory.");
}
return false;
}
if (dir.path() == QDir::home().canonicalPath()) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils",
"Refusing to remove your home directory.");
}
return false;
}
const QStringList fileNames = dir.entryList(
QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &fileName : fileNames) {
if (!removeRecursively(filePath / fileName, error))
return false;
}
if (!QDir::root().rmdir(dir.path())) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".")
.arg(filePath.toUserOutput());
}
return false;
}
} else {
if (!QFile::remove(filePath.toString())) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".")
.arg(filePath.toUserOutput());
}
return false;
}
}
return true;
}
bool DesktopDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const
{
return QFile::copy(filePath.path(), target.path());
}
bool DesktopDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
{
return QFile::rename(filePath.path(), target.path());
}
FilePathInfo DesktopDeviceFileAccess::filePathInfo(const FilePath &filePath) const
{
FilePathInfo result;
QFileInfo fi(filePath.path());
result.fileSize = fi.size();
result.lastModified = fi.lastModified();
result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions());
if (fi.isDir())
result.fileFlags |= FilePathInfo::DirectoryType;
if (fi.isFile())
result.fileFlags |= FilePathInfo::FileType;
if (fi.exists())
result.fileFlags |= FilePathInfo::ExistsFlag;
if (fi.isSymbolicLink())
result.fileFlags |= FilePathInfo::LinkType;
if (fi.isBundle())
result.fileFlags |= FilePathInfo::BundleType;
if (fi.isHidden())
result.fileFlags |= FilePathInfo::HiddenFlag;
if (fi.isRoot())
result.fileFlags |= FilePathInfo::RootFlag;
return result;
}
FilePath DesktopDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
{
const QFileInfo info(filePath.path());
if (!info.isSymLink())
return {};
return FilePath::fromString(info.symLinkTarget());
}
void DesktopDeviceFileAccess::iterateDirectory(
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const
{
QDirIterator it(filePath.path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
while (it.hasNext()) {
const FilePath path = FilePath::fromString(it.next());
bool res = false;
if (callBack.index() == 0)
res = std::get<0>(callBack)(path);
else
res = std::get<1>(callBack)(path, path.filePathInfo());
if (!res)
return;
}
}
std::optional<QByteArray> DesktopDeviceFileAccess::fileContents(
const FilePath &filePath,
qint64 limit,
qint64 offset) const
{
const QString path = filePath.path();
QFile f(path);
if (!f.exists())
return {};
if (!f.open(QFile::ReadOnly))
return {};
if (offset != 0)
f.seek(offset);
if (limit != -1)
return f.read(limit);
return f.readAll();
}
bool DesktopDeviceFileAccess::writeFileContents(
const FilePath &filePath,
const QByteArray &data,
qint64 offset) const
{
QFile file(filePath.path());
QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false);
if (offset != 0)
file.seek(offset);
qint64 res = file.write(data);
return res == data.size();
}
QDateTime DesktopDeviceFileAccess::lastModified(const FilePath &filePath) const
{
return QFileInfo(filePath.path()).lastModified();
}
QFile::Permissions DesktopDeviceFileAccess::permissions(const FilePath &filePath) const
{
return QFileInfo(filePath.path()).permissions();
}
bool DesktopDeviceFileAccess::setPermissions(const FilePath &filePath,
QFile::Permissions permissions) const
{
return QFile(filePath.path()).setPermissions(permissions);
}
qint64 DesktopDeviceFileAccess::fileSize(const FilePath &filePath) const
{
return QFileInfo(filePath.path()).size();
}
qint64 DesktopDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
{
return QStorageInfo(filePath.path()).bytesAvailable();
}
// Copied from qfilesystemengine_win.cpp
#ifdef Q_OS_WIN
// File ID for Windows up to version 7.
static inline QByteArray fileIdWin7(HANDLE handle)
{
BY_HANDLE_FILE_INFORMATION info;
if (GetFileInformationByHandle(handle, &info)) {
char buffer[sizeof "01234567:0123456701234567\0"];
qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx",
info.dwVolumeSerialNumber,
info.nFileIndexHigh,
info.nFileIndexLow);
return QByteArray(buffer);
}
return QByteArray();
}
// File ID for Windows starting from version 8.
static QByteArray fileIdWin8(HANDLE handle)
{
QByteArray result;
FILE_ID_INFO infoEx;
if (GetFileInformationByHandleEx(handle,
static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
&infoEx, sizeof(FILE_ID_INFO))) {
result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
result += ':';
// Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex();
}
return result;
}
static QByteArray fileIdWin(HANDLE fHandle)
{
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ?
fileIdWin8(HANDLE(fHandle)) : fileIdWin7(HANDLE(fHandle));
}
#endif
QByteArray DesktopDeviceFileAccess::fileId(const FilePath &filePath) const
{
QByteArray result;
#ifdef Q_OS_WIN
const HANDLE handle =
CreateFile((wchar_t*)filePath.toUserOutput().utf16(), 0,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle != INVALID_HANDLE_VALUE) {
result = fileIdWin(handle);
CloseHandle(handle);
}
#else // Copied from qfilesystemengine_unix.cpp
if (Q_UNLIKELY(filePath.isEmpty()))
return result;
QT_STATBUF statResult;
if (QT_STAT(filePath.toString().toLocal8Bit().constData(), &statResult))
return result;
result = QByteArray::number(quint64(statResult.st_dev), 16);
result += ':';
result += QByteArray::number(quint64(statResult.st_ino));
#endif
return result;
}
OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const
{
Q_UNUSED(filePath);
return HostOsInfo::hostOs();
}
// UnixDeviceAccess
UnixDeviceFileAccess::~UnixDeviceFileAccess() = default;
bool UnixDeviceFileAccess::runInShellSuccess(const CommandLine &cmdLine,
const QByteArray &stdInData) const
{
return runInShell(cmdLine, stdInData).exitCode == 0;
}
bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-x", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-r", path, "-a", "-f", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-w", path, "-a", "-f", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-r", path, "-a", "-d", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-w", path, "-a", "-d", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-f", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-d", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::isSymLink(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-h", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"touch", {path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"mkdir", {"-p", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::exists(const FilePath &filePath) const
{
const QString path = filePath.path();
return runInShellSuccess({"test", {"-e", path}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const
{
return runInShellSuccess({"rm", {filePath.path()}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const
{
QTC_ASSERT(filePath.path().startsWith('/'), return false);
const QString path = filePath.cleanPath().path();
// We are expecting this only to be called in a context of build directories or similar.
// Chicken out in some cases that _might_ be user code errors.
QTC_ASSERT(path.startsWith('/'), return false);
int levelsNeeded = path.startsWith("/home/") ? 4 : 3;
if (path.startsWith("/tmp/"))
levelsNeeded = 2;
QTC_ASSERT(path.count('/') >= levelsNeeded, return false);
RunResult result = runInShell({"rm", {"-rf", "--", path}, OsType::OsTypeLinux});
if (error)
*error = QString::fromUtf8(result.stdErr);
return result.exitCode == 0;
}
bool UnixDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const
{
return runInShellSuccess({"cp", {filePath.path(), target.path()}, OsType::OsTypeLinux});
}
bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
{
return runInShellSuccess({"mv", {filePath.path(), target.path()}, OsType::OsTypeLinux});
}
FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const
{
const RunResult result = runInShell({"readlink", {"-n", "-e", filePath.path()}, OsType::OsTypeLinux});
const QString out = QString::fromUtf8(result.stdOut);
return out.isEmpty() ? FilePath() : filePath.withNewPath(out);
}
std::optional<QByteArray> UnixDeviceFileAccess::fileContents(
const FilePath &filePath,
qint64 limit,
qint64 offset) const
{
QStringList args = {"if=" + filePath.path(), "status=none"};
if (limit > 0 || offset > 0) {
const qint64 gcd = std::gcd(limit, offset);
args += QString("bs=%1").arg(gcd);
args += QString("count=%1").arg(limit / gcd);
args += QString("seek=%1").arg(offset / gcd);
}
const RunResult r = runInShell({"dd", args, OsType::OsTypeLinux});
if (r.exitCode != 0)
return {};
return r.stdOut;
}
bool UnixDeviceFileAccess::writeFileContents(
const FilePath &filePath,
const QByteArray &data,
qint64 offset) const
{
QStringList args = {"of=" + filePath.path()};
if (offset != 0) {
args.append("bs=1");
args.append(QString("seek=%1").arg(offset));
}
return runInShellSuccess({"dd", args, OsType::OsTypeLinux}, data);
}
OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const
{
Q_UNUSED(filePath)
return OsTypeLinux;
}
QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const
{
const RunResult result = runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}, OsType::OsTypeLinux});
qint64 secs = result.stdOut.toLongLong();
const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
return dt;
}
QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const
{
const RunResult result = runInShell({"stat", {"-L", "-c", "%a", filePath.path()}, OsType::OsTypeLinux});
const uint bits = result.stdOut.toUInt(nullptr, 8);
QFileDevice::Permissions perm = {};
#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
BIT(0, ExeOther);
BIT(1, WriteOther);
BIT(2, ReadOther);
BIT(3, ExeGroup);
BIT(4, WriteGroup);
BIT(5, ReadGroup);
BIT(6, ExeUser);
BIT(7, WriteUser);
BIT(8, ReadUser);
#undef BIT
return perm;
}
bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const
{
const int flags = int(perms);
return runInShellSuccess({"chmod", {QString::number(flags, 16), filePath.path()}, OsType::OsTypeLinux});
}
qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
{
const RunResult result = runInShell({"stat", {"-L", "-c", "%s", filePath.path()}, OsType::OsTypeLinux});
return result.stdOut.toLongLong();
}
qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
{
const RunResult result = runInShell({"df", {"-k", filePath.path()}, OsType::OsTypeLinux});
return FileUtils::bytesAvailableFromDFOutput(result.stdOut);
}
QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
{
const RunResult result = runInShell({"stat", {"-L", "-c", "%D:%i", filePath.path()}, OsType::OsTypeLinux});
if (result.exitCode != 0)
return {};
return result.stdOut;
}
FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const
{
const RunResult stat = runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}, OsType::OsTypeLinux});
return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
}
// returns whether 'find' could be used.
bool UnixDeviceFileAccess::iterateWithFind(
const FilePath &filePath,
const FileFilter &filter,
const FilePath::IterateDirCallback &callBack) const
{
QTC_CHECK(filePath.isAbsolutePath());
CommandLine cmdLine{"find", filter.asFindArguments(filePath.path()), OsType::OsTypeLinux};
// TODO: Using stat -L will always return the link target, not the link itself.
// We may wan't to add the information that it is a link at some point.
if (callBack.index() == 1)
cmdLine.addArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)", CommandLine::Raw);
const RunResult result = runInShell(cmdLine);
const QString out = QString::fromUtf8(result.stdOut);
if (result.exitCode != 0) {
// Find returns non-zero exit code for any error it encounters, even if it finds some files.
if (!out.startsWith('"' + filePath.path())) {
if (!filePath.exists()) // File does not exist, so no files to find.
return true;
// If the output does not start with the path we are searching in, find has failed.
// Possibly due to unknown options.
return false;
}
}
QStringList entries = out.split("\n", Qt::SkipEmptyParts);
if (entries.isEmpty())
return true;
const auto toFilePath = [&filePath, &callBack](const QString &entry) -> bool {
if (callBack.index() == 0)
return std::get<0>(callBack)(filePath.withNewPath(entry));
const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1);
const QString infos = entry.mid(fileName.length() + 3);
const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos);
if (!fi.fileFlags)
return true;
const FilePath fp = filePath.withNewPath(fileName);
return std::get<1>(callBack)(fp, fi);
};
// Remove the first line, this can be the directory we are searching in.
// as long as we do not specify "mindepth > 0"
if (entries.front() == filePath.path())
entries.pop_front();
for (const QString &entry : entries) {
if (!toFilePath(entry))
break;
}
return true;
}
void UnixDeviceFileAccess::findUsingLs(
const QString &current,
const FileFilter &filter,
QStringList *found) const
{
const RunResult result = runInShell({"ls", {"-1", "-p", "--", current}, OsType::OsTypeLinux});
const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts);
for (QString entry : entries) {
const QChar last = entry.back();
if (last == '/') {
entry.chop(1);
if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories))
findUsingLs(current + '/' + entry, filter, found);
}
found->append(entry);
}
}
// Used on 'ls' output on unix-like systems.
static void iterateLsOutput(const FilePath &base,
const QStringList &entries,
const FileFilter &filter,
const FilePath::IterateDirCallback &callBack)
{
const QList<QRegularExpression> nameRegexps =
transform(filter.nameFilters, [](const QString &filter) {
QRegularExpression re;
re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
QTC_CHECK(re.isValid());
return re;
});
const auto nameMatches = [&nameRegexps](const QString &fileName) {
for (const QRegularExpression &re : nameRegexps) {
const QRegularExpressionMatch match = re.match(fileName);
if (match.hasMatch())
return true;
}
return nameRegexps.isEmpty();
};
// FIXME: Handle filters. For now bark on unsupported options.
QTC_CHECK(filter.fileFilters == QDir::NoFilter);
for (const QString &entry : entries) {
if (!nameMatches(entry))
continue;
const FilePath current = base.pathAppended(entry);
bool res = false;
if (callBack.index() == 0)
res = std::get<0>(callBack)(current);
else
res = std::get<1>(callBack)(current, current.filePathInfo());
if (!res)
break;
}
}
void UnixDeviceFileAccess::iterateDirectory(
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const
{
// We try to use 'find' first, because that can filter better directly.
// Unfortunately, it's not installed on all devices by default.
if (m_tryUseFind) {
if (iterateWithFind(filePath, filter, callBack))
return;
m_tryUseFind = false; // remember the failure for the next time and use the 'ls' fallback below.
}
// if we do not have find - use ls as fallback
QStringList entries;
findUsingLs(filePath.path(), filter, &entries);
iterateLsOutput(filePath, entries, filter, callBack);
}
} // Utils

View File

@@ -0,0 +1,195 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include "utils_global.h"
#include "fileutils.h"
namespace Utils {
// Base class including dummy implementation usable as fallback.
class QTCREATOR_UTILS_EXPORT DeviceFileAccess
{
public:
virtual ~DeviceFileAccess();
protected:
friend class FilePath;
virtual QString mapToDevicePath(const FilePath &filePath) const;
virtual bool isExecutableFile(const FilePath &filePath) const;
virtual bool isReadableFile(const FilePath &filePath) const;
virtual bool isWritableFile(const FilePath &filePath) const;
virtual bool isReadableDirectory(const FilePath &filePath) const;
virtual bool isWritableDirectory(const FilePath &filePath) const;
virtual bool isFile(const FilePath &filePath) const;
virtual bool isDirectory(const FilePath &filePath) const;
virtual bool isSymLink(const FilePath &filePath) const;
virtual bool ensureWritableDirectory(const FilePath &filePath) const;
virtual bool ensureExistingFile(const FilePath &filePath) const;
virtual bool createDirectory(const FilePath &filePath) const;
virtual bool exists(const FilePath &filePath) const;
virtual bool removeFile(const FilePath &filePath) const;
virtual bool removeRecursively(const FilePath &filePath, QString *error) const;
virtual bool copyFile(const FilePath &filePath, const FilePath &target) const;
virtual bool renameFile(const FilePath &filePath, const FilePath &target) const;
virtual OsType osType(const FilePath &filePath) const;
virtual FilePath symLinkTarget(const FilePath &filePath) const;
virtual FilePathInfo filePathInfo(const FilePath &filePath) const;
virtual QDateTime lastModified(const FilePath &filePath) const;
virtual QFile::Permissions permissions(const FilePath &filePath) const;
virtual bool setPermissions(const FilePath &filePath, QFile::Permissions) const;
virtual qint64 fileSize(const FilePath &filePath) const;
virtual qint64 bytesAvailable(const FilePath &filePath) const;
virtual QByteArray fileId(const FilePath &filePath) const;
virtual void iterateDirectory(
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const;
virtual std::optional<QByteArray> fileContents(
const FilePath &filePath,
qint64 limit,
qint64 offset) const;
virtual bool writeFileContents(
const FilePath &filePath,
const QByteArray &data,
qint64 offset) const;
virtual void asyncFileContents(
const FilePath &filePath,
const Continuation<std::optional<QByteArray>> &cont,
qint64 limit,
qint64 offset) const;
virtual void asyncWriteFileContents(
const FilePath &filePath,
const Continuation<bool> &cont,
const QByteArray &data,
qint64 offset) const;
virtual void asyncCopyFile(
const FilePath &filePath,
const Continuation<bool> &cont,
const FilePath &target) const;
};
class QTCREATOR_UTILS_EXPORT DesktopDeviceFileAccess : public DeviceFileAccess
{
public:
~DesktopDeviceFileAccess() override;
static DesktopDeviceFileAccess *instance();
protected:
bool isExecutableFile(const FilePath &filePath) const override;
bool isReadableFile(const FilePath &filePath) const override;
bool isWritableFile(const FilePath &filePath) const override;
bool isReadableDirectory(const FilePath &filePath) const override;
bool isWritableDirectory(const FilePath &filePath) const override;
bool isFile(const FilePath &filePath) const override;
bool isDirectory(const FilePath &filePath) const override;
bool isSymLink(const FilePath &filePath) const override;
bool ensureWritableDirectory(const FilePath &filePath) const override;
bool ensureExistingFile(const FilePath &filePath) const override;
bool createDirectory(const FilePath &filePath) const override;
bool exists(const FilePath &filePath) const override;
bool removeFile(const FilePath &filePath) const override;
bool removeRecursively(const FilePath &filePath, QString *error) const override;
bool copyFile(const FilePath &filePath, const FilePath &target) const override;
bool renameFile(const FilePath &filePath, const FilePath &target) const override;
OsType osType(const FilePath &filePath) const override;
FilePath symLinkTarget(const FilePath &filePath) const override;
FilePathInfo filePathInfo(const FilePath &filePath) const override;
QDateTime lastModified(const FilePath &filePath) const override;
QFile::Permissions permissions(const FilePath &filePath) const override;
bool setPermissions(const FilePath &filePath, QFile::Permissions) const override;
qint64 fileSize(const FilePath &filePath) const override;
qint64 bytesAvailable(const FilePath &filePath) const override;
QByteArray fileId(const FilePath &filePath) const override;
void iterateDirectory(
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const override;
std::optional<QByteArray> fileContents(
const FilePath &filePath,
qint64 limit,
qint64 offset) const override;
bool writeFileContents(
const FilePath &filePath,
const QByteArray &data,
qint64 offset) const override;
};
class QTCREATOR_UTILS_EXPORT UnixDeviceFileAccess : public DeviceFileAccess
{
public:
~UnixDeviceFileAccess() override;
protected:
virtual RunResult runInShell(const CommandLine &cmdLine,
const QByteArray &inputData = {}) const = 0;
bool runInShellSuccess(const CommandLine &cmdLine, const QByteArray &stdInData = {}) const;
bool isExecutableFile(const FilePath &filePath) const override;
bool isReadableFile(const FilePath &filePath) const override;
bool isWritableFile(const FilePath &filePath) const override;
bool isReadableDirectory(const FilePath &filePath) const override;
bool isWritableDirectory(const FilePath &filePath) const override;
bool isFile(const FilePath &filePath) const override;
bool isDirectory(const FilePath &filePath) const override;
bool isSymLink(const FilePath &filePath) const override;
bool ensureExistingFile(const FilePath &filePath) const override;
bool createDirectory(const FilePath &filePath) const override;
bool exists(const FilePath &filePath) const override;
bool removeFile(const FilePath &filePath) const override;
bool removeRecursively(const FilePath &filePath, QString *error) const override;
bool copyFile(const FilePath &filePath, const FilePath &target) const override;
bool renameFile(const FilePath &filePath, const FilePath &target) const override;
FilePathInfo filePathInfo(const FilePath &filePath) const override;
OsType osType(const FilePath &filePath) const override;
FilePath symLinkTarget(const FilePath &filePath) const override;
QDateTime lastModified(const FilePath &filePath) const override;
QFile::Permissions permissions(const FilePath &filePath) const override;
bool setPermissions(const FilePath &filePath, QFile::Permissions) const override;
qint64 fileSize(const FilePath &filePath) const override;
qint64 bytesAvailable(const FilePath &filePath) const override;
QByteArray fileId(const FilePath &filePath) const override;
void iterateDirectory(
const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const override;
std::optional<QByteArray> fileContents(
const FilePath &filePath,
qint64 limit,
qint64 offset) const override;
bool writeFileContents(
const FilePath &filePath,
const QByteArray &data,
qint64 offset) const override;
private:
bool iterateWithFind(
const FilePath &filePath,
const FileFilter &filter,
const FilePath::IterateDirCallback &callBack) const;
void findUsingLs(
const QString &current,
const FileFilter &filter,
QStringList *found) const;
mutable bool m_tryUseFind = true;
};
} // Utils

View File

@@ -14,142 +14,24 @@ Q_LOGGING_CATEGORY(deviceShellLog, "qtc.utils.deviceshell", QtWarningMsg)
namespace Utils {
namespace {
/*!
* The multiplex script waits for input via stdin.
*
* To start a command, a message is send with the format "<cmd-id> "<base64-encoded-stdin-data>" <commandline>\n"
* To start a command, a message is send with
* the format "<cmd-id> "<base64-encoded-stdin-data>" <commandline>\n"
* To stop the script, simply send "exit\n" via stdin
*
* Once a message is received, two new streams are created that the new process redirects its output to ( $stdoutraw and $stderrraw ).
* Once a message is received, two new streams are created that the new process redirects
* its output to ( $stdoutraw and $stderrraw ).
*
* These streams are piped through base64 into the two streams stdoutenc and stderrenc.
*
* Two subshells read from these base64 encoded streams, and prepend the command-id, as well as either "O:" or "E:" depending on whether its the stdout or stderr stream.
* Two subshells read from these base64 encoded streams, and prepend the command-id,
* as well as either "O:" or "E:" depending on whether its the stdout or stderr stream.
*
* Once the process exits its exit code is send to stdout with the command-id and the type "R".
*
*/
const QLatin1String r_execScript = QLatin1String(R"SCRIPT(
#!/bin/sh
FINAL_OUT=$(mktemp -u)
mkfifo "$FINAL_OUT"
finalOutput() {
local fileInputBuffer
while read fileInputBuffer
do
if test -f "$fileInputBuffer.err"; then
cat $fileInputBuffer.err
fi
cat $fileInputBuffer
rm -f $fileInputBuffer.err $fileInputBuffer
done
}
finalOutput < $FINAL_OUT &
readAndMark() {
local buffer
while read buffer
do
printf '%s:%s:%s\n' "$1" "$2" "$buffer"
done
}
base64decode()
{
base64 -d 2>/dev/null
}
base64encode()
{
base64 2>/dev/null
}
executeAndMark()
{
PID="$1"
INDATA="$2"
shift
shift
CMD="$@"
# LogFile
TMPFILE=$(mktemp)
# Output Streams
stdoutenc=$(mktemp -u)
stderrenc=$(mktemp -u)
mkfifo "$stdoutenc" "$stderrenc"
# app output streams
stdoutraw=$(mktemp -u)
stderrraw=$(mktemp -u)
mkfifo "$stdoutraw" "$stderrraw"
# Cleanup
trap 'rm -f "$stdoutenc" "$stderrenc" "$stdoutraw" "$stderrraw"' EXIT
# Pipe all app output through base64, and then into the output streams
cat $stdoutraw | base64encode > "$stdoutenc" &
cat $stderrraw | base64encode > "$stderrenc" &
# Mark the app's output streams
readAndMark $PID 'O' < "$stdoutenc" >> $TMPFILE &
readAndMark $PID 'E' < "$stderrenc" >> $TMPFILE.err &
# Start the app ...
if [ -z "$INDATA" ]
then
eval $CMD 1> "$stdoutraw" 2> "$stderrraw"
else
echo $INDATA | base64decode | eval "$CMD" 1> "$stdoutraw" 2> "$stderrraw"
fi
exitcode=$(echo $? | base64encode)
wait
echo "$PID:R:$exitcode" >> $TMPFILE
echo $TMPFILE
}
execute()
{
PID="$1"
if [ "$#" -lt "3" ]; then
TMPFILE=$(mktemp)
echo "$PID:R:MjU1Cg==" > $TMPFILE
echo $TMPFILE
else
INDATA=$(eval echo "$2")
shift
shift
CMD=$@
executeAndMark $PID "$INDATA" "$CMD"
fi
}
cleanup()
{
kill -- -$$
exit 1
}
trap cleanup 1 2 3 6
echo SCRIPT_INSTALLED >&2
(while read -r id inData cmd; do
if [ "$id" = "exit" ]; then
exit
fi
execute $id $inData $cmd || echo "$id:R:255" &
done) > $FINAL_OUT
)SCRIPT");
} // namespace
DeviceShell::DeviceShell(bool forceFailScriptInstallation)
: m_forceFailScriptInstallation(forceFailScriptInstallation)
@@ -380,8 +262,11 @@ bool DeviceShell::installShellScript()
return false;
}
const static QByteArray shellScriptBase64
= QByteArray(r_execScript.begin(), r_execScript.size()).toBase64();
const static QByteArray shellScriptBase64 = FilePath(":/utils/scripts/deviceshell.sh")
.fileContents()
.value()
.replace("\r\n", "\n")
.toBase64();
const QByteArray scriptCmd = "(scriptData=$(echo " + shellScriptBase64
+ " | base64 -d 2>/dev/null ) && /bin/sh -c \"$scriptData\") || "
"echo ERROR_INSTALL_SCRIPT >&2\n";

View File

@@ -175,13 +175,7 @@ bool Environment::isSameExecutable(const QString &exe1, const QString &exe2) con
for (const QString &i2 : exe2List) {
const FilePath f1 = FilePath::fromString(i1);
const FilePath f2 = FilePath::fromString(i2);
if (f1 == f2)
return true;
if (f1.needsDevice() != f2.needsDevice() || f1.scheme() != f2.scheme())
return false;
if (f1.resolveSymlinks() == f2.resolveSymlinks())
return true;
if (FileUtils::fileId(f1) == FileUtils::fileId(f2))
if (f1.isSameFile(f2))
return true;
}
}

View File

@@ -4,6 +4,7 @@
#include "filepath.h"
#include "algorithm.h"
#include "devicefileaccess.h"
#include "environment.h"
#include "fileutils.h"
#include "hostosinfo.h"
@@ -15,7 +16,6 @@
#include <QDirIterator>
#include <QFileInfo>
#include <QRegularExpression>
#include <QStorageInfo>
#include <QUrl>
#include <QStringView>
@@ -30,7 +30,6 @@
namespace Utils {
static DeviceFileHooks s_deviceHooks;
static bool removeRecursivelyLocal(const FilePath &filePath, QString *error);
inline bool isWindowsDriveLetter(QChar ch);
@@ -147,8 +146,7 @@ FilePath FilePath::fromFileInfo(const QFileInfo &info)
/// \returns a QFileInfo
QFileInfo FilePath::toFileInfo() const
{
QTC_ASSERT(!needsDevice(), return QFileInfo());
return QFileInfo(cleanPath().path());
return QFileInfo(toFSPathString());
}
FilePath FilePath::fromUrl(const QUrl &url)
@@ -359,136 +357,73 @@ void FilePath::setParts(const QStringView scheme, const QStringView host, const
/// FilePath exists.
bool FilePath::exists() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.exists, return false);
return s_deviceHooks.exists(*this);
}
return !isEmpty() && QFileInfo::exists(path());
return fileAccess()->exists(*this);
}
/// \returns a bool indicating whether a path is writable.
bool FilePath::isWritableDir() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.isWritableDir, return false);
return s_deviceHooks.isWritableDir(*this);
}
const QFileInfo fi{path()};
return exists() && fi.isDir() && fi.isWritable();
return fileAccess()->isWritableDirectory(*this);
}
bool FilePath::isWritableFile() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.isWritableFile, return false);
return s_deviceHooks.isWritableFile(*this);
}
const QFileInfo fi{path()};
return fi.isWritable() && !fi.isDir();
return fileAccess()->isWritableFile(*this);
}
bool FilePath::ensureWritableDir() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.ensureWritableDir, return false);
return s_deviceHooks.ensureWritableDir(*this);
}
const QFileInfo fi{path()};
if (fi.isDir() && fi.isWritable())
return true;
return QDir().mkpath(path());
return fileAccess()->ensureWritableDirectory(*this);
}
bool FilePath::ensureExistingFile() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.ensureExistingFile, return false);
return s_deviceHooks.ensureExistingFile(*this);
}
QFile f(path());
if (f.exists())
return true;
f.open(QFile::WriteOnly);
f.close();
return f.exists();
return fileAccess()->ensureExistingFile(*this);
}
bool FilePath::isExecutableFile() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.isExecutableFile, return false);
return s_deviceHooks.isExecutableFile(*this);
}
const QFileInfo fi{path()};
return fi.isExecutable() && !fi.isDir();
return fileAccess()->isExecutableFile(*this);
}
bool FilePath::isReadableFile() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.isReadableFile, return false);
return s_deviceHooks.isReadableFile(*this);
}
const QFileInfo fi{path()};
return fi.isReadable() && !fi.isDir();
return fileAccess()->isReadableFile(*this);
}
bool FilePath::isReadableDir() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.isReadableDir, return false);
return s_deviceHooks.isReadableDir(*this);
}
const QFileInfo fi{path()};
return fi.isReadable() && fi.isDir();
return fileAccess()->isReadableDirectory(*this);
}
bool FilePath::isFile() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.isFile, return false);
return s_deviceHooks.isFile(*this);
}
const QFileInfo fi{path()};
return fi.isFile();
return fileAccess()->isFile(*this);
}
bool FilePath::isDir() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.isDir, return false);
return s_deviceHooks.isDir(*this);
}
const QFileInfo fi{path()};
return fi.isDir();
return fileAccess()->isDirectory(*this);
}
bool FilePath::isSymLink() const
{
return fileAccess()->isSymLink(*this);
}
bool FilePath::createDir() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.createDir, return false);
return s_deviceHooks.createDir(*this);
}
QDir dir(path());
return dir.mkpath(dir.absolutePath());
return fileAccess()->createDirectory(*this);
}
FilePaths FilePath::dirEntries(const FileFilter &filter, QDir::SortFlags sort) const
{
FilePaths result;
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.iterateDirectory, return {});
const auto callBack = [&result](const FilePath &path) { result.append(path); return true; };
s_deviceHooks.iterateDirectory(*this, callBack, filter);
} else {
QDirIterator dit(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
while (dit.hasNext())
result.append(FilePath::fromString(dit.next()));
}
const auto callBack = [&result](const FilePath &path) { result.append(path); return true; };
iterateDirectory(callBack, filter);
// FIXME: Not all flags supported here.
const QDir::SortFlags sortBy = (sort & QDir::SortByMask);
if (sortBy == QDir::Name) {
Utils::sort(result);
@@ -515,34 +450,7 @@ FilePaths FilePath::dirEntries(QDir::Filters filters) const
void FilePath::iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.iterateDirectory, return);
s_deviceHooks.iterateDirectory(*this, callBack, filter);
return;
}
QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
while (it.hasNext()) {
if (!callBack(FilePath::fromString(it.next())))
return;
}
}
void FilePath::iterateDirectory(const IterateDirWithInfoCallback &callBack,
const FileFilter &filter) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.iterateDirectoryWithInfo, return);
s_deviceHooks.iterateDirectoryWithInfo(*this, callBack, filter);
return;
}
QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags);
while (it.hasNext()) {
const FilePath path = FilePath::fromString(it.next());
if (!callBack(path, path.filePathInfo()))
return;
}
fileAccess()->iterateDirectory(*this, callBack, filter);
}
void FilePath::iterateDirectories(const FilePaths &dirs,
@@ -555,26 +463,7 @@ void FilePath::iterateDirectories(const FilePaths &dirs,
std::optional<QByteArray> FilePath::fileContents(qint64 maxSize, qint64 offset) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.fileContents, return {});
return s_deviceHooks.fileContents(*this, maxSize, offset);
}
const QString path = toString();
QFile f(path);
if (!f.exists())
return {};
if (!f.open(QFile::ReadOnly))
return {};
if (offset != 0)
f.seek(offset);
if (maxSize != -1)
return f.read(maxSize);
return f.readAll();
return fileAccess()->fileContents(*this, maxSize, offset);
}
bool FilePath::ensureReachable(const FilePath &other) const
@@ -588,78 +477,30 @@ bool FilePath::ensureReachable(const FilePath &other) const
return false;
}
void FilePath::asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont,
qint64 maxSize,
qint64 offset) const
void FilePath::asyncFileContents(
const Continuation<const std::optional<QByteArray> &> &cont,
qint64 maxSize,
qint64 offset) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.asyncFileContents, return);
s_deviceHooks.asyncFileContents(cont, *this, maxSize, offset);
return;
}
cont(fileContents(maxSize, offset));
return fileAccess()->asyncFileContents(*this, cont, maxSize, offset);
}
bool FilePath::writeFileContents(const QByteArray &data, qint64 offset) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.writeFileContents, return {});
return s_deviceHooks.writeFileContents(*this, data, offset);
}
QFile file(path());
QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false);
if (offset != 0)
file.seek(offset);
qint64 res = file.write(data);
return res == data.size();
return fileAccess()->writeFileContents(*this, data, offset);
}
FilePathInfo FilePath::filePathInfo() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.filePathInfo, return {});
return s_deviceHooks.filePathInfo(*this);
}
FilePathInfo result;
QFileInfo fi(path());
result.fileSize = fi.size();
result.lastModified = fi.lastModified();
result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions());
if (fi.isDir())
result.fileFlags |= FilePathInfo::DirectoryType;
if (fi.isFile())
result.fileFlags |= FilePathInfo::FileType;
if (fi.exists())
result.fileFlags |= FilePathInfo::ExistsFlag;
if (fi.isSymbolicLink())
result.fileFlags |= FilePathInfo::LinkType;
if (fi.isBundle())
result.fileFlags |= FilePathInfo::BundleType;
if (fi.isHidden())
result.fileFlags |= FilePathInfo::HiddenFlag;
if (fi.isRoot())
result.fileFlags |= FilePathInfo::RootFlag;
return result;
return fileAccess()->filePathInfo(*this);
}
void FilePath::asyncWriteFileContents(const Continuation<bool> &cont,
const QByteArray &data,
qint64 offset) const
void FilePath::asyncWriteFileContents(
const Continuation<bool> &cont,
const QByteArray &data,
qint64 offset) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.asyncWriteFileContents, return);
s_deviceHooks.asyncWriteFileContents(cont, *this, data, offset);
return;
}
cont(writeFileContents(data, offset));
return fileAccess()->asyncWriteFileContents(*this, cont, data, offset);
}
bool FilePath::needsDevice() const
@@ -678,26 +519,34 @@ bool FilePath::isSameDevice(const FilePath &other) const
return s_deviceHooks.isSameDevice(*this, other);
}
bool FilePath::isSameFile(const FilePath &other) const
{
if (*this == other)
return true;
if (!isSameDevice(other))
return false;
const QByteArray fileId = fileAccess()->fileId(*this);
const QByteArray otherFileId = fileAccess()->fileId(other);
if (fileId.isEmpty() || otherFileId.isEmpty())
return false;
if (fileId == otherFileId)
return true;
return false;
}
/// \returns an empty FilePath if this is not a symbolic linl
FilePath FilePath::symLinkTarget() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.symLinkTarget, return {});
return s_deviceHooks.symLinkTarget(*this);
}
const QFileInfo info(path());
if (!info.isSymLink())
return {};
return FilePath::fromString(info.symLinkTarget());
return fileAccess()->symLinkTarget(*this);
}
QString FilePath::mapToDevicePath() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.mapToDevicePath, return path());
return s_deviceHooks.mapToDevicePath(*this);
}
return path();
return fileAccess()->mapToDevicePath(*this);
}
FilePath FilePath::withExecutableSuffix() const
@@ -948,6 +797,23 @@ void FilePath::setFromString(const QString &unnormalizedFileName)
setParts({}, {}, fileName);
}
DeviceFileAccess *FilePath::fileAccess() const
{
if (!needsDevice())
return DesktopDeviceFileAccess::instance();
if (!s_deviceHooks.fileAccess) {
// Happens during startup and in tst_fsengine
QTC_CHECK(false);
return DesktopDeviceFileAccess::instance();
}
static DeviceFileAccess dummy;
DeviceFileAccess *access = s_deviceHooks.fileAccess(*this);
QTC_ASSERT(access, return &dummy);
return access;
}
/// Constructs a FilePath from \a filePath. The \a defaultExtension is appended
/// to \a filename if that does not have an extension already.
/// \a filePath is not checked for validity.
@@ -1041,6 +907,8 @@ FilePath FilePath::operator+(const QString &s) const
/// \returns whether FilePath is a child of \a s
bool FilePath::isChildOf(const FilePath &s) const
{
if (!s.isSameDevice(*this))
return false;
if (s.isEmpty())
return false;
if (!path().startsWith(s.path(), caseSensitivity()))
@@ -1112,28 +980,26 @@ FilePath FilePath::relativeChildPath(const FilePath &parent) const
///
FilePath FilePath::relativePath(const FilePath &anchor) const
{
QTC_ASSERT(!needsDevice(), return *this);
QTC_ASSERT(isSameDevice(anchor), return *this);
const QFileInfo fileInfo(toString());
QString absolutePath;
FilePath absPath;
QString filename;
if (fileInfo.isFile()) {
absolutePath = fileInfo.absolutePath();
filename = fileInfo.fileName();
} else if (fileInfo.isDir()) {
absolutePath = fileInfo.absoluteFilePath();
if (isFile()) {
absPath = absolutePath();
filename = fileName();
} else if (isDir()) {
absPath = absoluteFilePath();
} else {
return {};
}
const QFileInfo anchorInfo(anchor.toString());
QString absoluteAnchorPath;
if (anchorInfo.isFile())
absoluteAnchorPath = anchorInfo.absolutePath();
else if (anchorInfo.isDir())
absoluteAnchorPath = anchorInfo.absoluteFilePath();
FilePath absoluteAnchorPath;
if (anchor.isFile())
absoluteAnchorPath = anchor.absolutePath();
else if (anchor.isDir())
absoluteAnchorPath = anchor.absoluteFilePath();
else
return {};
QString relativeFilePath = calcRelativePath(absolutePath, absoluteAnchorPath);
QString relativeFilePath = calcRelativePath(absPath.path(), absoluteAnchorPath.path());
if (!filename.isEmpty()) {
if (relativeFilePath == ".")
relativeFilePath.clear();
@@ -1252,10 +1118,10 @@ FilePath FilePath::withNewPath(const QString &newPath) const
*/
FilePath FilePath::searchInDirectories(const FilePaths &dirs) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.searchInPath, return {});
return s_deviceHooks.searchInPath(*this, dirs);
}
if (isAbsolutePath())
return *this;
// FIXME: Ramp down use.
QTC_ASSERT(!needsDevice(), return {});
return Environment::systemEnvironment().searchInDirectories(path(), dirs);
}
@@ -1263,6 +1129,7 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am
{
if (isAbsolutePath())
return *this;
// FIXME: Ramp down use.
FilePaths directories = deviceEnvironment().path();
if (!additionalDirs.isEmpty()) {
if (amending == AppendToPath)
@@ -1270,7 +1137,8 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am
else
directories = additionalDirs + directories;
}
return searchInDirectories(directories);
QTC_ASSERT(!needsDevice(), return {});
return Environment::systemEnvironment().searchInDirectories(path(), directories);
}
Environment FilePath::deviceEnvironment() const
@@ -1349,47 +1217,27 @@ size_t FilePath::hash(uint seed) const
QDateTime FilePath::lastModified() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.lastModified, return {});
return s_deviceHooks.lastModified(*this);
}
return toFileInfo().lastModified();
return fileAccess()->lastModified(*this);
}
QFile::Permissions FilePath::permissions() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.permissions, return {});
return s_deviceHooks.permissions(*this);
}
return toFileInfo().permissions();
return fileAccess()->permissions(*this);
}
bool FilePath::setPermissions(QFile::Permissions permissions) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.setPermissions, return false);
return s_deviceHooks.setPermissions(*this, permissions);
}
return QFile(path()).setPermissions(permissions);
return fileAccess()->setPermissions(*this, permissions);
}
OsType FilePath::osType() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.osType, return OsType::OsTypeLinux);
return s_deviceHooks.osType(*this);
}
return HostOsInfo::hostOs();
return fileAccess()->osType(*this);
}
bool FilePath::removeFile() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.removeFile, return false);
return s_deviceHooks.removeFile(*this);
}
return QFile::remove(path());
return fileAccess()->removeFile(*this);
}
/*!
@@ -1401,11 +1249,7 @@ bool FilePath::removeFile() const
*/
bool FilePath::removeRecursively(QString *error) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.removeRecursively, return false);
return s_deviceHooks.removeRecursively(*this);
}
return removeRecursivelyLocal(*this, error);
return fileAccess()->removeRecursively(*this, error);
}
bool FilePath::copyFile(const FilePath &target) const
@@ -1417,11 +1261,7 @@ bool FilePath::copyFile(const FilePath &target) const
return false;
return target.writeFileContents(*ba);
}
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.copyFile, return false);
return s_deviceHooks.copyFile(*this, target);
}
return QFile::copy(path(), target.path());
return fileAccess()->copyFile(*this, target);
}
void FilePath::asyncCopyFile(const std::function<void(bool)> &cont, const FilePath &target) const
@@ -1431,90 +1271,24 @@ void FilePath::asyncCopyFile(const std::function<void(bool)> &cont, const FilePa
if (ba)
target.asyncWriteFileContents(cont, *ba);
});
} else if (needsDevice()) {
s_deviceHooks.asyncCopyFile(cont, *this, target);
} else {
cont(copyFile(target));
return;
}
return fileAccess()->asyncCopyFile(*this, cont, target);
}
bool FilePath::renameFile(const FilePath &target) const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.renameFile, return false);
return s_deviceHooks.renameFile(*this, target);
}
return QFile::rename(path(), target.path());
return fileAccess()->renameFile(*this, target);
}
qint64 FilePath::fileSize() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.fileSize, return false);
return s_deviceHooks.fileSize(*this);
}
return QFileInfo(path()).size();
return fileAccess()->fileSize(*this);
}
qint64 FilePath::bytesAvailable() const
{
if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.bytesAvailable, return false);
return s_deviceHooks.bytesAvailable(*this);
}
return QStorageInfo(path()).bytesAvailable();
}
static bool removeRecursivelyLocal(const FilePath &filePath, QString *error)
{
QTC_ASSERT(!filePath.needsDevice(), return false);
QFileInfo fileInfo = filePath.toFileInfo();
if (!fileInfo.exists() && !fileInfo.isSymLink())
return true;
QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser);
if (fileInfo.isDir()) {
QDir dir(fileInfo.absoluteFilePath());
dir.setPath(dir.canonicalPath());
if (dir.isRoot()) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils",
"Refusing to remove root directory.");
}
return false;
}
if (dir.path() == QDir::home().canonicalPath()) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils",
"Refusing to remove your home directory.");
}
return false;
}
const QStringList fileNames = dir.entryList(
QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString &fileName : fileNames) {
if (!removeRecursivelyLocal(filePath / fileName, error))
return false;
}
if (!QDir::root().rmdir(dir.path())) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".")
.arg(filePath.toUserOutput());
}
return false;
}
} else {
if (!QFile::remove(filePath.toString())) {
if (error) {
*error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".")
.arg(filePath.toUserOutput());
}
return false;
}
}
return true;
return fileAccess()->bytesAvailable(*this);
}
/*!
@@ -1949,19 +1723,15 @@ QStringList FileFilter::asFindArguments(const QString &path) const
const QString nameOption = (filters & QDir::CaseSensitive) ? QString{"-name"}
: QString{"-iname"};
if (!nameFilters.isEmpty()) {
const QRegularExpression oneChar("\\[.*?\\]");
bool addedFirst = false;
bool isFirst = true;
filterOptions << "(";
for (const QString &current : nameFilters) {
if (current.indexOf(oneChar) != -1) {
qDebug() << "Skipped" << current << "due to presence of [] wildcard";
continue;
}
if (addedFirst)
if (!isFirst)
filterOptions << "-o";
filterOptions << nameOption << current;
addedFirst = true;
isFirst = false;
}
filterOptions << ")";
}
arguments << filterOptions;
return arguments;

View File

@@ -15,6 +15,7 @@
#include <functional>
#include <memory>
#include <optional>
#include <variant>
QT_BEGIN_NAMESPACE
class QDateTime;
@@ -27,9 +28,12 @@ class tst_fileutils; // This becomes a friend of Utils::FilePath for testing pri
namespace Utils {
class DeviceFileAccess;
class Environment;
class EnvironmentChange;
template <class ...Args> using Continuation = std::function<void(Args...)>;
class QTCREATOR_UTILS_EXPORT FileFilter
{
public:
@@ -103,6 +107,7 @@ public:
bool isAbsolutePath() const { return !isRelativePath(); }
bool isFile() const;
bool isDir() const;
bool isSymLink() const;
bool isRootPath() const;
bool isNewerThan(const QDateTime &timeStamp) const;
QDateTime lastModified() const;
@@ -154,17 +159,20 @@ public:
[[nodiscard]] FilePath onDevice(const FilePath &deviceTemplate) const;
[[nodiscard]] FilePath withNewPath(const QString &newPath) const;
using IterateDirCallback = std::function<bool(const FilePath &item)>;
using IterateDirWithInfoCallback
= std::function<bool(const FilePath &item, const FilePathInfo &info)>;
using IterateDirCallback
= std::variant<
std::function<bool(const FilePath &item)>,
std::function<bool(const FilePath &item, const FilePathInfo &info)>
>;
void iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const;
void iterateDirectory(const IterateDirWithInfoCallback &callBack,
const FileFilter &filter) const;
void iterateDirectory(
const IterateDirCallback &callBack,
const FileFilter &filter) const;
static void iterateDirectories(const FilePaths &dirs,
const IterateDirCallback &callBack,
const FileFilter &filter);
static void iterateDirectories(
const FilePaths &dirs,
const IterateDirCallback &callBack,
const FileFilter &filter);
enum PathAmending { AppendToPath, PrependToPath };
[[nodiscard]] FilePath searchInPath(const FilePaths &additionalDirs = {},
@@ -184,7 +192,6 @@ public:
static void sort(FilePaths &files);
// Asynchronous interface
template <class ...Args> using Continuation = std::function<void(Args...)>;
void asyncCopyFile(const Continuation<bool> &cont, const FilePath &target) const;
void asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont,
qint64 maxSize = -1,
@@ -201,6 +208,7 @@ public:
bool needsDevice() const;
bool isSameDevice(const FilePath &other) const;
bool isSameFile(const FilePath &other) const;
[[nodiscard]] QFileInfo toFileInfo() const;
[[nodiscard]] static FilePath fromFileInfo(const QFileInfo &info);
@@ -228,6 +236,7 @@ private:
static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath);
void setPath(QStringView path);
void setFromString(const QString &filepath);
DeviceFileAccess *fileAccess() const;
[[nodiscard]] QString mapToDevicePath() const;
[[nodiscard]] QString encodedHost() const;
@@ -248,54 +257,11 @@ class QTCREATOR_UTILS_EXPORT DeviceFileHooks
public:
static DeviceFileHooks &instance();
std::function<bool(const FilePath &)> isExecutableFile;
std::function<bool(const FilePath &)> isReadableFile;
std::function<bool(const FilePath &)> isReadableDir;
std::function<bool(const FilePath &)> isWritableDir;
std::function<bool(const FilePath &)> isWritableFile;
std::function<bool(const FilePath &)> isFile;
std::function<bool(const FilePath &)> isDir;
std::function<bool(const FilePath &)> ensureWritableDir;
std::function<bool(const FilePath &)> ensureExistingFile;
std::function<bool(const FilePath &)> createDir;
std::function<bool(const FilePath &)> exists;
std::function<bool(const FilePath &)> removeFile;
std::function<bool(const FilePath &)> removeRecursively;
std::function<bool(const FilePath &, const FilePath &)> copyFile;
std::function<bool(const FilePath &, const FilePath &)> renameFile;
std::function<FilePath(const FilePath &, const FilePaths &)> searchInPath;
std::function<FilePath(const FilePath &)> symLinkTarget;
std::function<QString(const FilePath &)> mapToDevicePath;
std::function<void(const FilePath &,
const FilePath::IterateDirCallback &, // Abort on 'false' return.
const FileFilter &)>
iterateDirectory;
std::function<void(const FilePath &,
const FilePath::IterateDirWithInfoCallback &, // Abort on 'false' return.
const FileFilter &)>
iterateDirectoryWithInfo;
std::function<std::optional<QByteArray>(const FilePath &, qint64, qint64)> fileContents;
std::function<bool(const FilePath &, const QByteArray &, qint64)> writeFileContents;
std::function<QDateTime(const FilePath &)> lastModified;
std::function<QFile::Permissions(const FilePath &)> permissions;
std::function<bool(const FilePath &, QFile::Permissions)> setPermissions;
std::function<OsType(const FilePath &)> osType;
std::function<Environment(const FilePath &)> environment;
std::function<qint64(const FilePath &)> fileSize;
std::function<qint64(const FilePath &)> bytesAvailable;
std::function<DeviceFileAccess *(const FilePath &)> fileAccess;
std::function<QString(const FilePath &)> deviceDisplayName;
std::function<bool(const FilePath &, const FilePath &)> isSameDevice;
std::function<FilePathInfo(const FilePath &)> filePathInfo;
template <class ...Args> using Continuation = std::function<void(Args...)>;
std::function<void(const Continuation<bool> &, const FilePath &, const FilePath &)> asyncCopyFile;
std::function<void(
const Continuation<const std::optional<QByteArray> &> &, const FilePath &, qint64, qint64)>
asyncFileContents;
std::function<void(const Continuation<bool> &, const FilePath &, const QByteArray &, qint64)>
asyncWriteFileContents;
std::function<bool(const FilePath &, const FilePath &)> ensureReachable;
std::function<Environment(const FilePath &)> environment;
std::function<bool(const FilePath &left, const FilePath &right)> isSameDevice;
};
} // namespace Utils

View File

@@ -5,7 +5,6 @@
#include "savefile.h"
#include "algorithm.h"
#include "commandline.h"
#include "qtcassert.h"
#include "hostosinfo.h"
@@ -15,7 +14,6 @@
#include <QDataStream>
#include <QDateTime>
#include <QDebug>
#include <QOperatingSystemVersion>
#include <QRegularExpression>
#include <QTemporaryFile>
#include <QTextStream>
@@ -25,7 +23,6 @@
#ifdef QT_GUI_LIB
#include <QMessageBox>
#include <QRegularExpression>
#include <QGuiApplication>
#endif
@@ -37,7 +34,7 @@
#include <shlobj.h>
#endif
#ifdef Q_OS_OSX
#ifdef Q_OS_MACOS
#include "fileutils_mac.h"
#endif
@@ -337,47 +334,6 @@ FilePaths FileUtils::CopyAskingForOverwrite::files() const
}
#endif // QT_GUI_LIB
// Copied from qfilesystemengine_win.cpp
#ifdef Q_OS_WIN
// File ID for Windows up to version 7.
static inline QByteArray fileIdWin7(HANDLE handle)
{
BY_HANDLE_FILE_INFORMATION info;
if (GetFileInformationByHandle(handle, &info)) {
char buffer[sizeof "01234567:0123456701234567\0"];
qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx",
info.dwVolumeSerialNumber,
info.nFileIndexHigh,
info.nFileIndexLow);
return QByteArray(buffer);
}
return QByteArray();
}
// File ID for Windows starting from version 8.
static QByteArray fileIdWin8(HANDLE handle)
{
QByteArray result;
FILE_ID_INFO infoEx;
if (GetFileInformationByHandleEx(handle,
static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8
&infoEx, sizeof(FILE_ID_INFO))) {
result = QByteArray::number(infoEx.VolumeSerialNumber, 16);
result += ':';
// Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one.
result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex();
}
return result;
}
static QByteArray fileIdWin(HANDLE fHandle)
{
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ?
fileIdWin8(HANDLE(fHandle)) : fileIdWin7(HANDLE(fHandle));
}
#endif
FilePath FileUtils::commonPath(const FilePaths &paths)
{
if (paths.isEmpty())
@@ -424,33 +380,6 @@ FilePath FileUtils::commonPath(const FilePaths &paths)
return result;
}
QByteArray FileUtils::fileId(const FilePath &fileName)
{
QByteArray result;
#ifdef Q_OS_WIN
const HANDLE handle =
CreateFile((wchar_t*)fileName.toUserOutput().utf16(), 0,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle != INVALID_HANDLE_VALUE) {
result = fileIdWin(handle);
CloseHandle(handle);
}
#else // Copied from qfilesystemengine_unix.cpp
if (Q_UNLIKELY(fileName.isEmpty()))
return result;
QT_STATBUF statResult;
if (QT_STAT(fileName.toString().toLocal8Bit().constData(), &statResult))
return result;
result = QByteArray::number(quint64(statResult.st_dev), 16);
result += ':';
result += QByteArray::number(quint64(statResult.st_ino));
#endif
return result;
}
#ifdef Q_OS_WIN
template <>
void withNtfsPermissions(const std::function<void()> &task)
@@ -476,6 +405,82 @@ static QWidget *dialogParent(QWidget *parent)
return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
}
FilePath qUrlToFilePath(const QUrl &url)
{
if (url.isLocalFile())
return FilePath::fromString(url.toLocalFile());
return FilePath::fromUrl(url);
}
QUrl filePathToQUrl(const FilePath &filePath)
{
return QUrl::fromLocalFile(filePath.toFSPathString());
}
void prepareNonNativeDialog(QFileDialog &dialog)
{
// Checking QFileDialog::itemDelegate() seems to be the only way to determine
// whether the dialog is native or not.
if (dialog.itemDelegate()) {
FilePaths sideBarPaths;
// Check existing urls, remove paths that need a device and no longer exist.
for (const QUrl &url : dialog.sidebarUrls()) {
FilePath path = qUrlToFilePath(url);
if (!path.needsDevice() || path.exists())
sideBarPaths.append(path);
}
// Add all device roots that are not already in the sidebar and exist.
for (const FilePath &path : FSEngine::registeredDeviceRoots()) {
if (!sideBarPaths.contains(path) && path.exists())
sideBarPaths.append(path);
}
dialog.setSidebarUrls(Utils::transform(sideBarPaths, filePathToQUrl));
dialog.setIconProvider(Utils::FileIconProvider::iconProvider());
}
}
FilePaths getFilePaths(QWidget *parent,
const QString &caption,
const FilePath &dir,
const QString &filter,
QString *selectedFilter,
QFileDialog::Options options,
const QStringList &supportedSchemes,
const bool forceNonNativeDialog,
QFileDialog::FileMode fileMode,
QFileDialog::AcceptMode acceptMode)
{
QFileDialog dialog(parent, caption, dir.toFSPathString(), filter);
dialog.setFileMode(fileMode);
if (forceNonNativeDialog)
options.setFlag(QFileDialog::DontUseNativeDialog);
dialog.setOptions(options);
prepareNonNativeDialog(dialog);
dialog.setSupportedSchemes(supportedSchemes);
dialog.setAcceptMode(acceptMode);
if (selectedFilter && !selectedFilter->isEmpty())
dialog.selectNameFilter(*selectedFilter);
if (dialog.exec() == QDialog::Accepted) {
if (selectedFilter)
*selectedFilter = dialog.selectedNameFilter();
return Utils::transform(dialog.selectedUrls(), &qUrlToFilePath);
}
return {};
}
FilePath firstOrEmpty(const FilePaths &filePaths)
{
return filePaths.isEmpty() ? FilePath() : filePaths.first();
}
FilePath FileUtils::getOpenFilePath(QWidget *parent,
const QString &caption,
const FilePath &dir,
@@ -484,19 +489,24 @@ FilePath FileUtils::getOpenFilePath(QWidget *parent,
QFileDialog::Options options,
bool fromDeviceIfShiftIsPressed)
{
bool forceNonNativeDialog = dir.needsDevice();
#ifdef QT_GUI_LIB
if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) {
return getOpenFilePathFromDevice(parent, caption, dir, filter, selectedFilter, options);
forceNonNativeDialog = true;
}
#endif
const QString result = QFileDialog::getOpenFileName(dialogParent(parent),
caption,
dir.toString(),
filter,
selectedFilter,
options);
return FilePath::fromString(result);
const QStringList schemes = QStringList(QStringLiteral("file"));
return firstOrEmpty(getFilePaths(dialogParent(parent),
caption,
dir,
filter,
selectedFilter,
options,
schemes,
forceNonNativeDialog,
QFileDialog::ExistingFile,
QFileDialog::AcceptOpen));
}
FilePath FileUtils::getSaveFilePath(QWidget *parent,
@@ -506,25 +516,46 @@ FilePath FileUtils::getSaveFilePath(QWidget *parent,
QString *selectedFilter,
QFileDialog::Options options)
{
const QString result = QFileDialog::getSaveFileName(dialogParent(parent),
caption,
dir.toString(),
filter,
selectedFilter,
options);
return FilePath::fromString(result);
bool forceNonNativeDialog = dir.needsDevice();
const QStringList schemes = QStringList(QStringLiteral("file"));
return firstOrEmpty(getFilePaths(dialogParent(parent),
caption,
dir,
filter,
selectedFilter,
options,
schemes,
forceNonNativeDialog,
QFileDialog::AnyFile,
QFileDialog::AcceptSave));
}
FilePath FileUtils::getExistingDirectory(QWidget *parent,
const QString &caption,
const FilePath &dir,
QFileDialog::Options options)
QFileDialog::Options options,
bool fromDeviceIfShiftIsPressed)
{
const QString result = QFileDialog::getExistingDirectory(dialogParent(parent),
caption,
dir.toString(),
options);
return FilePath::fromString(result);
bool forceNonNativeDialog = dir.needsDevice();
#ifdef QT_GUI_LIB
if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) {
forceNonNativeDialog = true;
}
#endif
const QStringList schemes = QStringList(QStringLiteral("file"));
return firstOrEmpty(getFilePaths(dialogParent(parent),
caption,
dir,
{},
nullptr,
options,
schemes,
forceNonNativeDialog,
QFileDialog::Directory,
QFileDialog::AcceptOpen));
}
FilePaths FileUtils::getOpenFilePaths(QWidget *parent,
@@ -534,92 +565,23 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent,
QString *selectedFilter,
QFileDialog::Options options)
{
const QStringList result = QFileDialog::getOpenFileNames(dialogParent(parent),
caption,
dir.toString(),
filter,
selectedFilter,
options);
return FileUtils::toFilePathList(result);
}
bool forceNonNativeDialog = dir.needsDevice();
FilePath FileUtils::getOpenFilePathFromDevice(QWidget *parent,
const QString &caption,
const FilePath &dir,
const QString &filter,
QString *selectedFilter,
QFileDialog::Options options)
{
QFileDialog dialog(parent);
dialog.setOptions(options | QFileDialog::DontUseNativeDialog);
dialog.setWindowTitle(caption);
dialog.setDirectory(dir.toString());
dialog.setNameFilter(filter);
QList<QUrl> sideBarUrls = Utils::transform(Utils::filtered(FSEngine::registeredDeviceRoots(),
[](const auto &filePath) {
return filePath.exists();
}),
[](const auto &filePath) {
return QUrl::fromLocalFile(
filePath.toFSPathString());
});
dialog.setSidebarUrls(sideBarUrls);
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setIconProvider(Utils::FileIconProvider::iconProvider());
if (dialog.exec()) {
FilePaths filePaths = Utils::transform(dialog.selectedFiles(), [](const auto &path) {
return FilePath::fromString(path);
});
if (selectedFilter) {
*selectedFilter = dialog.selectedNameFilter();
}
return filePaths.first();
}
return {};
const QStringList schemes = QStringList(QStringLiteral("file"));
return getFilePaths(dialogParent(parent),
caption,
dir,
filter,
selectedFilter,
options,
schemes,
forceNonNativeDialog,
QFileDialog::ExistingFiles,
QFileDialog::AcceptOpen);
}
#endif // QT_WIDGETS_LIB
// Used on 'ls' output on unix-like systems.
void FileUtils::iterateLsOutput(const FilePath &base,
const QStringList &entries,
const FileFilter &filter,
const std::function<bool (const FilePath &)> &callBack)
{
const QList<QRegularExpression> nameRegexps =
transform(filter.nameFilters, [](const QString &filter) {
QRegularExpression re;
re.setPattern(QRegularExpression::wildcardToRegularExpression(filter));
QTC_CHECK(re.isValid());
return re;
});
const auto nameMatches = [&nameRegexps](const QString &fileName) {
for (const QRegularExpression &re : nameRegexps) {
const QRegularExpressionMatch match = re.match(fileName);
if (match.hasMatch())
return true;
}
return nameRegexps.isEmpty();
};
// FIXME: Handle filters. For now bark on unsupported options.
QTC_CHECK(filter.fileFilters == QDir::NoFilter);
for (const QString &entry : entries) {
if (!nameMatches(entry))
continue;
if (!callBack(base.pathAppended(entry)))
break;
}
}
FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString)
{
bool ok = false;
@@ -675,148 +637,6 @@ FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos)
return {size, flags, dt};
}
bool iterateWithFind(const FilePath &filePath,
const FileFilter &filter,
const std::function<RunResult(const CommandLine &)> &runInShell,
const std::function<bool(const QString &)> callBack,
const QString &extraArguments)
{
QTC_CHECK(filePath.isAbsolutePath());
const QStringList arguments = filter.asFindArguments(filePath.path());
CommandLine cmdLine{"find", arguments};
if (!extraArguments.isEmpty())
cmdLine.addArgs(extraArguments, CommandLine::Raw);
const RunResult result = runInShell(cmdLine);
const QString out = QString::fromUtf8(result.stdOut);
if (result.exitCode != 0) {
// Find returns non-zero exit code for any error it encounters, even if it finds some files.
if (!out.startsWith('"' + filePath.path())) {
if (!filePath.exists()) // File does not exist, so no files to find.
return true;
// If the output does not start with the path we are searching in, find has failed.
// Possibly due to unknown options.
return false;
}
}
QStringList entries = out.split("\n", Qt::SkipEmptyParts);
// Remove the first line, it is always the directory we are searching in.
// as long as we do not specify "mindepth > 0"
entries.pop_front();
for (const QString &entry : entries) {
if (!callBack(entry))
break;
}
return true;
}
// returns whether 'find' could be used.
static bool iterateWithFind(const FilePath &filePath,
const FileFilter &filter,
const std::function<RunResult(const CommandLine &)> &runInShell,
const FilePath::IterateDirCallback &callBack)
{
const auto toFilePath = [&filePath, &callBack](const QString &entry){
return callBack(filePath.withNewPath(entry));
};
return iterateWithFind(filePath, filter, runInShell, toFilePath, {});
}
// returns whether 'find' could be used.
static bool iterateWithFind(const FilePath &filePath,
const FileFilter &filter,
const std::function<RunResult(const CommandLine &)> &runInShell,
const FilePath::IterateDirWithInfoCallback &callBack)
{
// TODO: Using stat -L will always return the link target, not the link itself.
// We may wan't to add the information that it is a link at some point.
const QString infoArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)");
const auto toFilePathAndInfo = [&filePath, &callBack](const QString &entry) {
const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1);
const QString infos = entry.mid(fileName.length() + 3);
const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos);
if (!fi.fileFlags)
return true;
const FilePath fp = filePath.withNewPath(fileName);
return callBack(fp, fi);
};
return iterateWithFind(filePath, filter, runInShell, toFilePathAndInfo, infoArgs);
}
static void findUsingLs(const QString &current,
const FileFilter &filter,
const std::function<RunResult(const CommandLine &)> &runInShell,
QStringList *found)
{
const RunResult result = runInShell({"ls", {"-1", "-p", "--", current}});
const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts);
for (QString entry : entries) {
const QChar last = entry.back();
if (last == '/') {
entry.chop(1);
if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories))
findUsingLs(current + '/' + entry, filter, runInShell, found);
}
found->append(entry);
}
}
void FileUtils::iterateUnixDirectory(const FilePath &filePath,
const FileFilter &filter,
bool *useFind,
const std::function<RunResult (const CommandLine &)> &runInShell,
const FilePath::IterateDirCallback &callBack)
{
QTC_ASSERT(callBack, return);
// We try to use 'find' first, because that can filter better directly.
// Unfortunately, it's not installed on all devices by default.
if (useFind && *useFind) {
if (iterateWithFind(filePath, filter, runInShell, callBack))
return;
*useFind = false; // remember the failure for the next time and use the 'ls' fallback below.
}
// if we do not have find - use ls as fallback
QStringList entries;
findUsingLs(filePath.path(), filter, runInShell, &entries);
FileUtils::iterateLsOutput(filePath, entries, filter, callBack);
}
void FileUtils::iterateUnixDirectory(const FilePath &filePath,
const FileFilter &filter,
bool *useFind,
const std::function<RunResult(const CommandLine &)> &runInShell,
const FilePath::IterateDirWithInfoCallback &callBack)
{
QTC_ASSERT(callBack, return);
// We try to use 'find' first, because that can filter better directly.
// Unfortunately, it's not installed on all devices by default.
if (useFind && *useFind) {
if (iterateWithFind(filePath, filter, runInShell, callBack))
return;
*useFind = false; // remember the failure for the next time and use the 'ls' fallback below.
}
// if we do not have find - use ls as fallback
QStringList entries;
findUsingLs(filePath.path(), filter, runInShell, &entries);
FileUtils::iterateLsOutput(filePath, entries, filter, [&callBack](const FilePath & filePath){
return callBack(filePath, filePath.filePathInfo());
});
}
/*!
Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain
the target directory, which will be created. Example usage:

View File

@@ -81,31 +81,10 @@ public:
static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); }
static FilePath commonPath(const FilePath &oldCommonPath, const FilePath &fileName);
static FilePath commonPath(const FilePaths &paths);
static QByteArray fileId(const FilePath &fileName);
static FilePath homePath();
static FilePaths toFilePathList(const QStringList &paths);
static void iterateLsOutput(
const FilePath &base,
const QStringList &entries,
const FileFilter &filter,
const std::function<bool(const FilePath &)> &callBack);
static void iterateUnixDirectory(
const FilePath &base,
const FileFilter &filter,
bool *useFind,
const std::function<RunResult(const CommandLine &)> &runInShell,
const FilePath::IterateDirCallback &callBack);
static void iterateUnixDirectory(
const FilePath &base,
const FileFilter &filter,
bool *useFind,
const std::function<RunResult(const CommandLine &)> &runInShell,
const FilePath::IterateDirWithInfoCallback &callBack);
static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput);
static FilePathInfo filePathInfoFromTriple(const QString &infos);
@@ -131,7 +110,8 @@ public:
static FilePath getExistingDirectory(QWidget *parent,
const QString &caption,
const FilePath &dir = {},
QFileDialog::Options options = QFileDialog::ShowDirsOnly);
QFileDialog::Options options = QFileDialog::ShowDirsOnly,
bool fromDeviceIfShiftIsPressed = false);
static FilePaths getOpenFilePaths(QWidget *parent,
const QString &caption,
@@ -139,13 +119,6 @@ public:
const QString &filter = {},
QString *selectedFilter = nullptr,
QFileDialog::Options options = {});
static FilePath getOpenFilePathFromDevice(QWidget *parent,
const QString &caption,
const FilePath &dir = {},
const QString &filter = {},
QString *selectedFilter = nullptr,
QFileDialog::Options options = {});
#endif
};

View File

@@ -40,7 +40,13 @@ public:
if (!data) {
data = new CachedData;
*data = retrievalFunction(filePath);
m_cache.insert(filePath, data);
if (Q_UNLIKELY(!m_cache.insert(filePath, data))) {
// This path will never happen, but to silence coverity we
// have to check it since insert in theory could delete
// the object if a cost bigger than the cache size is
// specified.
return {};
}
}
// Return a copy of the data, so it cannot be deleted by the cache
@@ -60,6 +66,12 @@ public:
m_cache.insert(path, new CachedData(data));
}
void invalidate(const FilePath &path)
{
QMutexLocker lk(&m_mutex);
m_cache.remove(path);
}
private:
QMutex m_mutex;
QCache<FilePath, CachedData> m_cache;

View File

@@ -44,6 +44,8 @@ bool FSEngineImpl::open(QIODevice::OpenMode openMode)
createCacheData);
bool exists = (data.filePathInfo.fileFlags & QAbstractFileEngine::ExistsFlag);
g_filePathInfoCache.invalidate(m_filePath);
ensureStorage();
QTC_ASSERT(m_tempStorage->open(), return false);

View File

@@ -199,6 +199,9 @@ FilePath PathChooserPrivate::expandedPath(const FilePath &input) const
if (path.isEmpty())
return path;
if (path.isAbsolutePath())
return path;
switch (m_acceptingKind) {
case PathChooser::Command:
case PathChooser::ExistingCommand: {
@@ -391,7 +394,7 @@ void PathChooser::slotBrowse()
case PathChooser::ExistingDirectory:
newPath = FileUtils::getExistingDirectory(this,
makeDialogTitle(tr("Choose Directory")),
predefined);
predefined, {}, d->m_allowPathFromDevice);
break;
case PathChooser::ExistingCommand:
case PathChooser::Command:

View File

@@ -0,0 +1,116 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
FINAL_OUT=$(mktemp -u)
mkfifo "$FINAL_OUT"
finalOutput() {
local fileInputBuffer
while read fileInputBuffer
do
if test -f "$fileInputBuffer.err"; then
cat $fileInputBuffer.err
fi
cat $fileInputBuffer
rm -f $fileInputBuffer.err $fileInputBuffer
done
}
finalOutput < $FINAL_OUT &
readAndMark() {
local buffer
while read buffer
do
printf '%s:%s:%s\n' "$1" "$2" "$buffer"
done
}
base64decode()
{
base64 -d 2>/dev/null
}
base64encode()
{
base64 2>/dev/null
}
executeAndMark()
{
PID="$1"
INDATA="$2"
shift
shift
CMD="$@"
# LogFile
TMPFILE=$(mktemp)
# Output Streams
stdoutenc=$(mktemp -u)
stderrenc=$(mktemp -u)
mkfifo "$stdoutenc" "$stderrenc"
# app output streams
stdoutraw=$(mktemp -u)
stderrraw=$(mktemp -u)
mkfifo "$stdoutraw" "$stderrraw"
# Cleanup
trap 'rm -f "$stdoutenc" "$stderrenc" "$stdoutraw" "$stderrraw"' EXIT
# Pipe all app output through base64, and then into the output streams
cat $stdoutraw | base64encode > "$stdoutenc" &
cat $stderrraw | base64encode > "$stderrenc" &
# Mark the app's output streams
readAndMark $PID 'O' < "$stdoutenc" >> $TMPFILE &
readAndMark $PID 'E' < "$stderrenc" >> $TMPFILE.err &
# Start the app ...
if [ -z "$INDATA" ]
then
eval $CMD 1> "$stdoutraw" 2> "$stderrraw"
else
echo $INDATA | base64decode | eval "$CMD" 1> "$stdoutraw" 2> "$stderrraw"
fi
exitcode=$(echo $? | base64encode)
wait
echo "$PID:R:$exitcode" >> $TMPFILE
echo $TMPFILE
}
execute()
{
PID="$1"
if [ "$#" -lt "3" ]; then
TMPFILE=$(mktemp)
echo "$PID:R:MjU1Cg==" > $TMPFILE
echo $TMPFILE
else
INDATA=$(eval echo "$2")
shift
shift
CMD=$@
executeAndMark $PID "$INDATA" "$CMD"
fi
}
cleanup()
{
kill -- -$$
exit 1
}
trap cleanup 1 2 3 6
echo SCRIPT_INSTALLED >&2
(while read -r id inData cmd; do
if [ "$id" = "exit" ]; then
exit
fi
execute $id $inData $cmd || echo "$id:R:255" &
done) > $FINAL_OUT

View File

@@ -83,6 +83,8 @@ Project {
"detailsbutton.h",
"detailswidget.cpp",
"detailswidget.h",
"devicefileaccess.cpp",
"devicefileaccess.h",
"deviceshell.cpp",
"deviceshell.h",
"differ.cpp",

View File

@@ -239,6 +239,7 @@
<file>images/message@2x.png</file>
<file>images/help.png</file>
<file alias="mimetypes/freedesktop.org.xml" compression-algorithm="best">../3rdparty/xdg/freedesktop.org.xml</file>
<file>scripts/deviceshell.sh</file>
</qresource>
<qresource prefix="/codemodel">
<file>images/enum.png</file>

View File

@@ -79,7 +79,6 @@ ClangFormatOptionsPageWidget::ClangFormatOptionsPageWidget(ClangFormatSettings *
m_command->setCommandVersionArguments({"--version"});
m_command->setPromptDialogTitle(
BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format"));
m_command->setFilePath(m_settings->command());
if (m_settings->usePredefinedStyle())
m_usePredefinedStyle->setChecked(true);
@@ -107,13 +106,16 @@ ClangFormatOptionsPageWidget::ClangFormatOptionsPageWidget(ClangFormatSettings *
}.attachTo(this);
connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled);
connect(m_predefinedStyle, &QComboBox::currentTextChanged, [this](const QString &item) {
connect(m_predefinedStyle, &QComboBox::currentTextChanged, this, [this](const QString &item) {
m_fallbackStyle->setEnabled(item == "File");
});
connect(m_usePredefinedStyle, &QRadioButton::toggled, [this](bool checked) {
connect(m_usePredefinedStyle, &QRadioButton::toggled, this, [this](bool checked) {
m_fallbackStyle->setEnabled(checked && m_predefinedStyle->currentText() == "File");
m_predefinedStyle->setEnabled(checked);
});
// might trigger PathChooser::validChanged, so so after the connect above
m_command->setFilePath(m_settings->command());
}
void ClangFormatOptionsPageWidget::apply()

View File

@@ -88,20 +88,16 @@ BookmarksPluginPrivate::BookmarksPluginPrivate()
// Toggle
Command *cmd = ActionManager::registerAction(&m_toggleAction, BOOKMARKS_TOGGLE_ACTION,
editorManagerContext);
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M")
: Tr::tr("Ctrl+M")));
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M") : Tr::tr("Ctrl+M")));
cmd->setTouchBarIcon(Utils::Icons::MACOS_TOUCHBAR_BOOKMARK.icon());
mbm->addAction(cmd);
cmd = ActionManager::registerAction(&m_editAction,
BOOKMARKS_EDIT_ACTION,
editorManagerContext);
cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M")
: Tr::tr("Ctrl+Shift+M")));
mbm->addAction(cmd);
touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_EDITOR);
cmd = ActionManager::registerAction(&m_editAction, BOOKMARKS_EDIT_ACTION, editorManagerContext);
cmd->setDefaultKeySequence(
QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M") : Tr::tr("Ctrl+Shift+M")));
mbm->addAction(cmd);
mbm->addSeparator();
// Previous

View File

@@ -6,6 +6,7 @@
#include "qdbconstants.h"
#include <projectexplorer/deploymentdataview.h>
#include "projectexplorer/devicesupport/idevice.h"
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/target.h>
@@ -31,7 +32,14 @@ QdbDeployConfigurationFactory::QdbDeployConfigurationFactory()
&& prj->hasMakeInstallEquivalent();
});
addInitialStep(Qdb::Constants::QdbStopApplicationStepId);
addInitialStep(RemoteLinux::Constants::DirectUploadStepId);
addInitialStep(RemoteLinux::Constants::RsyncDeployStepId, [](Target *target) {
auto device = DeviceKitAspect::device(target->kit());
return device && device->extraData(RemoteLinux::Constants::SupportsRSync).toBool();
});
addInitialStep(RemoteLinux::Constants::DirectUploadStepId, [](Target *target) {
auto device = DeviceKitAspect::device(target->kit());
return device && !device->extraData(RemoteLinux::Constants::SupportsRSync).toBool();
});
}
} // namespace Internal

View File

@@ -92,7 +92,8 @@ public:
QdbMakeDefaultAppStep(BuildStepList *bsl, Id id)
: AbstractRemoteLinuxDeployStep(bsl, id)
{
auto service = createDeployService<QdbMakeDefaultAppService>();
auto service = new QdbMakeDefaultAppService;
setDeployService(service);
auto selection = addAspect<SelectionAspect>();
selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault");

View File

@@ -25,6 +25,7 @@
#include <remotelinux/genericdirectuploadstep.h>
#include <remotelinux/makeinstallstep.h>
#include <remotelinux/rsyncdeploystep.h>
#include <remotelinux/remotelinux_constants.h>
#include <utils/hostosinfo.h>
@@ -156,6 +157,8 @@ public:
QdbDeployStepFactory<RemoteLinux::GenericDirectUploadStep>
m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId};
QdbDeployStepFactory<RemoteLinux::RsyncDeployStep>
m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId};
QdbDeployStepFactory<RemoteLinux::MakeInstallStep>
m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};

View File

@@ -115,7 +115,8 @@ public:
QdbStopApplicationStep(BuildStepList *bsl, Id id)
: AbstractRemoteLinuxDeployStep(bsl, id)
{
auto service = createDeployService<QdbStopApplicationService>();
auto service = new QdbStopApplicationService;
setDeployService(service);
setWidgetExpandedByDefault(false);

View File

@@ -260,7 +260,7 @@ public:
: q(q), settings(CppEditor::ClangdProjectSettings(project).settings()) {}
void findUsages(TextDocument *document, const QTextCursor &cursor,
const QString &searchTerm,
const QString &searchTerm, const std::optional<QString> &replacement,
bool categorize);
void handleDeclDefSwitchReplies();
@@ -462,11 +462,13 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
if (searchTerm.isEmpty())
return;
if (replacement) {
symbolSupport().renameSymbol(document, adjustedCursor, *replacement,
CppEditor::preferLowerCaseFileNames());
return;
}
// TODO: Fix hard file limit in clangd, then uncomment this with version check.
// Will fix QTCREATORBUG-27978 and QTCREATORBUG-28109.
// if (replacement) {
// symbolSupport().renameSymbol(document, adjustedCursor, *replacement,
// CppEditor::preferLowerCaseFileNames());
// return;
// }
const bool categorize = CppEditor::codeModelSettings()->categorizeFindReferences();
@@ -474,19 +476,19 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor,
if (searchTerm != "operator" && Utils::allOf(searchTerm, [](const QChar &c) {
return c.isLetterOrNumber() || c == '_';
})) {
d->findUsages(document, adjustedCursor, searchTerm, categorize);
d->findUsages(document, adjustedCursor, searchTerm, replacement, categorize);
return;
}
// Otherwise get the proper spelling of the search term from clang, so we can put it into the
// search widget.
const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, categorize]
const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, replacement, categorize]
(const QString &name, const QString &, const MessageId &) {
if (!doc)
return;
if (name.isEmpty())
return;
d->findUsages(doc.data(), adjustedCursor, name, categorize);
d->findUsages(doc.data(), adjustedCursor, name, replacement, categorize);
};
requestSymbolInfo(document->filePath(), Range(adjustedCursor).start(), symbolInfoHandler);
}
@@ -642,9 +644,11 @@ QVersionNumber ClangdClient::versionNumber() const
CppEditor::ClangdSettings::Data ClangdClient::settingsData() const { return d->settings; }
void ClangdClient::Private::findUsages(TextDocument *document,
const QTextCursor &cursor, const QString &searchTerm, bool categorize)
const QTextCursor &cursor, const QString &searchTerm,
const std::optional<QString> &replacement, bool categorize)
{
const auto findRefs = new ClangdFindReferences(q, document, cursor, searchTerm, categorize);
const auto findRefs = new ClangdFindReferences(q, document, cursor, searchTerm, replacement,
categorize);
if (isTesting) {
connect(findRefs, &ClangdFindReferences::foundReferences,
q, &ClangdClient::foundReferences);
@@ -732,6 +736,8 @@ void ClangdClient::updateParserConfig(const Utils::FilePath &filePath,
const CppEditor::BaseEditorDocumentParser::Configuration &config)
{
// TODO: Also handle usePrecompiledHeaders?
// TODO: Should we write the editor defines into the json file? It seems strange
// that they should affect the index only while the file is open in the editor.
const auto projectPart = !config.preferredProjectPartId.isEmpty()
? CppEditor::CppModelManager::instance()->projectPartForId(
config.preferredProjectPartId)
@@ -741,10 +747,15 @@ void ClangdClient::updateParserConfig(const Utils::FilePath &filePath,
CppEditor::BaseEditorDocumentParser::Configuration fullConfig = config;
fullConfig.preferredProjectPartId = projectPart->id();
CppEditor::BaseEditorDocumentParser::Configuration &cachedConfig = d->parserConfigs[filePath];
if (cachedConfig == fullConfig)
auto cachedConfig = d->parserConfigs.find(filePath);
if (cachedConfig == d->parserConfigs.end()) {
cachedConfig = d->parserConfigs.insert(filePath, fullConfig);
if (config.preferredProjectPartId.isEmpty() && config.editorDefines.isEmpty())
return;
} else if (cachedConfig.value() == fullConfig) {
return;
cachedConfig = fullConfig;
}
cachedConfig.value() = fullConfig;
QJsonObject cdbChanges;
const Utils::FilePath includeDir = CppEditor::ClangdSettings(d->settings).clangdIncludePath();

View File

@@ -44,12 +44,25 @@ public:
ClangdAstNode ast;
};
class ReplacementData {
public:
QString oldSymbolName;
QString newSymbolName;
QSet<Utils::FilePath> fileRenameCandidates;
};
class ClangdFindReferences::Private
{
public:
Private(ClangdFindReferences *q) : q(q) {}
ClangdClient *client() const { return qobject_cast<ClangdClient *>(q->parent()); }
static void handleRenameRequest(
const SearchResult *search,
const ReplacementData &replacementData,
const QString &newSymbolName,
const QList<SearchResultItem> &checkedItems,
bool preserveCase);
void handleFindUsagesResult(const QList<Location> &locations);
void finishSearch();
void reportAllSearchResultsAndFinish();
@@ -61,24 +74,50 @@ public:
QMap<DocumentUri, ReferencesFileData> fileData;
QList<MessageId> pendingAstRequests;
QPointer<SearchResult> search;
std::optional<ReplacementData> replacementData;
bool canceled = false;
bool categorize = false;
};
ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *document,
const QTextCursor &cursor, const QString &searchTerm, bool categorize)
const QTextCursor &cursor, const QString &searchTerm,
const std::optional<QString> &replacement, bool categorize)
: QObject(client), d(new ClangdFindReferences::Private(this))
{
d->categorize = categorize;
if (replacement) {
ReplacementData replacementData;
replacementData.oldSymbolName = searchTerm;
replacementData.newSymbolName = *replacement;
if (replacementData.newSymbolName.isEmpty())
replacementData.newSymbolName = replacementData.oldSymbolName;
d->replacementData = replacementData;
}
d->search = SearchResultWindow::instance()->startNewSearch(
tr("C++ Usages:"),
{},
searchTerm,
SearchResultWindow::SearchOnly,
replacement ? SearchResultWindow::SearchAndReplace : SearchResultWindow::SearchOnly,
SearchResultWindow::PreserveCaseDisabled,
"CppEditor");
if (categorize)
d->search->setFilter(new CppSearchResultFilter);
if (d->replacementData) {
d->search->setTextToReplace(d->replacementData->newSymbolName);
const auto renameFilesCheckBox = new QCheckBox;
renameFilesCheckBox->setVisible(false);
d->search->setAdditionalReplaceWidget(renameFilesCheckBox);
const auto renameHandler =
[search = d->search](const QString &newSymbolName,
const QList<SearchResultItem> &checkedItems,
bool preserveCase) {
const auto replacementData = search->userData().value<ReplacementData>();
Private::handleRenameRequest(search, replacementData, newSymbolName, checkedItems,
preserveCase);
};
connect(d->search, &SearchResult::replaceButtonClicked, renameHandler);
}
connect(d->search, &SearchResult::activated, [](const SearchResultItem& item) {
EditorManager::openEditorAtSearchResult(item);
});
@@ -112,6 +151,31 @@ ClangdFindReferences::~ClangdFindReferences()
delete d;
}
void ClangdFindReferences::Private::handleRenameRequest(
const SearchResult *search,
const ReplacementData &replacementData,
const QString &newSymbolName,
const QList<SearchResultItem> &checkedItems,
bool preserveCase)
{
const Utils::FilePaths filePaths = BaseFileFind::replaceAll(newSymbolName, checkedItems,
preserveCase);
if (!filePaths.isEmpty()) {
DocumentManager::notifyFilesChangedInternally(filePaths);
SearchResultWindow::instance()->hide();
}
const auto renameFilesCheckBox = qobject_cast<QCheckBox *>(search->additionalReplaceWidget());
QTC_ASSERT(renameFilesCheckBox, return);
if (!renameFilesCheckBox->isChecked())
return;
ProjectExplorerPlugin::renameFilesForSymbol(
replacementData.oldSymbolName, newSymbolName,
Utils::toList(replacementData.fileRenameCandidates),
CppEditor::preferLowerCaseFileNames());
}
void ClangdFindReferences::Private::handleFindUsagesResult(const QList<Location> &locations)
{
if (!search || canceled) {
@@ -154,7 +218,7 @@ void ClangdFindReferences::Private::handleFindUsagesResult(const QList<Location>
}
qCDebug(clangdLog) << "document count is" << fileData.size();
if (!categorize) {
if (replacementData || !categorize) {
qCDebug(clangdLog) << "skipping AST retrieval";
reportAllSearchResultsAndFinish();
return;
@@ -198,6 +262,18 @@ void ClangdFindReferences::Private::finishSearch()
if (!client()->testingEnabled() && search) {
search->finishSearch(canceled);
search->disconnect(q);
if (replacementData) {
const auto renameCheckBox = qobject_cast<QCheckBox *>(
search->additionalReplaceWidget());
QTC_CHECK(renameCheckBox);
const QSet<Utils::FilePath> files = replacementData->fileRenameCandidates;
renameCheckBox->setText(tr("Re&name %n files", nullptr, files.size()));
const QStringList filesForUser = Utils::transform<QStringList>(files,
[](const Utils::FilePath &fp) { return fp.toUserOutput(); });
renameCheckBox->setToolTip(tr("Files:\n%1").arg(filesForUser.join('\n')));
renameCheckBox->setVisible(true);
search->setUserData(QVariant::fromValue(*replacementData));
}
}
emit q->done();
q->deleteLater();
@@ -231,6 +307,15 @@ void ClangdFindReferences::Private::addSearchResultsForFile(const FilePath &file
item.setUseTextEditorFont(true);
item.setLineText(rangeWithText.second);
item.setContainingFunctionName(getContainingFunctionName(astPath, range));
if (search->supportsReplace()) {
const bool fileInSession = SessionManager::projectForFile(file);
item.setSelectForReplacement(fileInSession);
if (fileInSession && file.baseName().compare(replacementData->oldSymbolName,
Qt::CaseInsensitive) == 0) {
replacementData->fileRenameCandidates << file;
}
}
items << item;
}
if (client()->testingEnabled())
@@ -484,3 +569,5 @@ void ClangdFindLocalReferences::Private::finish()
}
} // namespace ClangCodeModel::Internal
Q_DECLARE_METATYPE(ClangCodeModel::Internal::ReplacementData)

View File

@@ -25,7 +25,7 @@ class ClangdFindReferences : public QObject
public:
explicit ClangdFindReferences(ClangdClient *client, TextEditor::TextDocument *document,
const QTextCursor &cursor, const QString &searchTerm,
bool categorize);
const std::optional<QString> &replacement, bool categorize);
~ClangdFindReferences();
signals:

View File

@@ -78,9 +78,8 @@ static QStringList projectPartArguments(const ProjectPart &projectPart)
args << "-c";
if (projectPart.toolchainType != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) {
args << "--target=" + projectPart.toolChainTargetTriple;
args << (projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit
? QLatin1String("-m64")
: QLatin1String("-m32"));
if (projectPart.toolChainAbi.architecture() == Abi::X86Architecture)
args << QLatin1String(projectPart.toolChainAbi.wordWidth() == 64 ? "-m64" : "-m32");
}
args << projectPart.compilerFlags;
for (const ProjectExplorer::HeaderPath &headerPath : projectPart.headerPaths) {
@@ -155,7 +154,7 @@ GenerateCompilationDbResult generateCompilationDB(QList<ProjectInfo::ConstPtr> p
QTC_ASSERT(!projectInfoList.isEmpty(),
return GenerateCompilationDbResult(QString(), "Could not retrieve project info."));
QTC_CHECK(baseDir.ensureWritableDir());
QFile compileCommandsFile(baseDir.toString() + "/compile_commands.json");
QFile compileCommandsFile(baseDir.pathAppended("compile_commands.json").toFSPathString());
const bool fileOpened = compileCommandsFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
if (!fileOpened) {
return GenerateCompilationDbResult(QString(),

View File

@@ -34,8 +34,8 @@ clang::format::FormatStyle qtcStyle()
style.AccessModifierOffset = -4;
style.AlignAfterOpenBracket = FormatStyle::BAS_Align;
#if LLVM_VERSION_MAJOR >= 15
style.AlignConsecutiveAssignments = {false};
style.AlignConsecutiveDeclarations = {false};
style.AlignConsecutiveAssignments = {false, false, false, false, false};
style.AlignConsecutiveDeclarations = {false, false, false, false, false};
#elif LLVM_VERSION_MAJOR >= 12
style.AlignConsecutiveAssignments = FormatStyle::ACS_None;
style.AlignConsecutiveDeclarations = FormatStyle::ACS_None;

View File

@@ -161,7 +161,8 @@ private:
QAction *m_clear = nullptr;
QAction *m_expandCollapse = nullptr;
Utils::Perspective m_perspective{ClangTidyClazyPerspectiveId, tr("Clang-Tidy and Clazy")};
Utils::Perspective m_perspective{ClangTidyClazyPerspectiveId,
::ClangTools::Internal::ClangTool::tr("Clang-Tidy and Clazy")};
private:
const QString m_name;

View File

@@ -175,14 +175,10 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
BuildConfiguration *bc = bs->buildConfiguration();
CMakeBuildConfiguration *cbc = static_cast<CMakeBuildConfiguration *>(bc);
auto vbox = new QVBoxLayout(this);
vbox->setContentsMargins(0, 0, 0, 0);
m_configureDetailsWidget = new DetailsWidget;
updateConfigureDetailsWidgetsSummary();
vbox->addWidget(m_configureDetailsWidget);
auto details = new QWidget(m_configureDetailsWidget);
m_configureDetailsWidget->setWidget(details);
@@ -333,9 +329,6 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
envWidget->setBaseEnvironmentText(cbc->baseConfigureEnvironmentText());
});
vbox->addWidget(clearBox);
vbox->addWidget(envWidget);
using namespace Layouting;
Grid cmakeConfiguration {
m_filterEdit, br,
@@ -372,10 +365,16 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
},
m_reconfigureButton,
}
}
}.setSpacing(0)
},
clearBox,
envWidget
}
}.attachTo(details, WithoutMargins);
Column {
m_configureDetailsWidget,
}.attachTo(this, WithoutMargins);
updateAdvancedCheckBox();
setError(m_buildSystem->error());
setWarning(m_buildSystem->warning());

View File

@@ -607,24 +607,24 @@ void CMakeBuildSystem::updateProjectData()
setError(errorMessage);
qCDebug(cmakeBuildSystemLog) << "Raw project parts created." << errorMessage;
{
for (RawProjectPart &rpp : rpps) {
rpp.setQtVersion(
kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt.
const QString includeFileBaseDir = buildConfiguration()->buildDirectory().toString();
if (kitInfo.cxxToolChain) {
rpp.setFlagsForCxx({kitInfo.cxxToolChain, rpp.flagsForCxx.commandLineFlags,
includeFileBaseDir});
}
if (kitInfo.cToolChain) {
rpp.setFlagsForC({kitInfo.cToolChain, rpp.flagsForC.commandLineFlags,
includeFileBaseDir});
}
}
m_cppCodeModelUpdater->update({p, kitInfo, buildConfiguration()->environment(), rpps},
m_extraCompilers);
for (RawProjectPart &rpp : rpps) {
rpp.setQtVersion(
kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt.
const QString includeFileBaseDir = buildConfiguration()->buildDirectory().toString();
QStringList cxxFlags = rpp.flagsForCxx.commandLineFlags;
QStringList cFlags = rpp.flagsForC.commandLineFlags;
addTargetFlagForIos(cxxFlags, cFlags, this, [this] {
return m_configurationFromCMake.stringValueOf("CMAKE_OSX_DEPLOYMENT_TARGET");
});
if (kitInfo.cxxToolChain)
rpp.setFlagsForCxx({kitInfo.cxxToolChain, cxxFlags, includeFileBaseDir});
if (kitInfo.cToolChain)
rpp.setFlagsForC({kitInfo.cToolChain, cFlags, includeFileBaseDir});
}
m_cppCodeModelUpdater->update({p, kitInfo, buildConfiguration()->environment(), rpps},
m_extraCompilers);
{
const bool mergedHeaderPathsAndQmlImportPaths = kit()->value(
QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), false).toBool();
@@ -1093,28 +1093,28 @@ DeploymentData CMakeBuildSystem::deploymentData() const
{
DeploymentData result;
QDir sourceDir = project()->projectDirectory().toString();
QDir buildDir = buildConfiguration()->buildDirectory().toString();
FilePath sourceDir = project()->projectDirectory();
FilePath buildDir = buildConfiguration()->buildDirectory();
QString deploymentPrefix;
QString deploymentFilePath = sourceDir.filePath("QtCreatorDeployment.txt");
FilePath deploymentFilePath = sourceDir.pathAppended("QtCreatorDeployment.txt");
bool hasDeploymentFile = QFileInfo::exists(deploymentFilePath);
bool hasDeploymentFile = deploymentFilePath.exists();
if (!hasDeploymentFile) {
deploymentFilePath = buildDir.filePath("QtCreatorDeployment.txt");
hasDeploymentFile = QFileInfo::exists(deploymentFilePath);
deploymentFilePath = buildDir.pathAppended("QtCreatorDeployment.txt");
hasDeploymentFile = deploymentFilePath.exists();
}
if (!hasDeploymentFile)
return result;
deploymentPrefix = result.addFilesFromDeploymentFile(deploymentFilePath,
sourceDir.absolutePath());
deploymentPrefix = result.addFilesFromDeploymentFile(deploymentFilePath.toString(),
sourceDir.toString());
for (const CMakeBuildTarget &ct : m_buildTargets) {
if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType) {
if (!ct.executable.isEmpty()
&& result.deployableForLocalFile(ct.executable).localFilePath() != ct.executable) {
result.addFile(ct.executable,
deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()),
deploymentPrefix + buildDir.relativeChildPath(ct.executable).toString(),
DeployableFile::TypeExecutable);
}
}

View File

@@ -682,8 +682,11 @@ QVariant CMakeGeneratorKitAspect::defaultValue(const Kit *k) const
= Internal::CMakeProjectPlugin::projectTypeSpecificSettings();
if (settings->ninjaPath.filePath().isEmpty()) {
Environment env = k->buildEnvironment();
return !env.searchInPath("ninja").isEmpty();
auto findNinja = [](const Environment &env) -> bool {
return !env.searchInPath("ninja").isEmpty();
};
if (!findNinja(Environment::systemEnvironment()))
return findNinja(k->buildEnvironment());
}
return true;
}();

View File

@@ -16,6 +16,7 @@
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <cppeditor/cpptoolsreuse.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
@@ -217,10 +218,17 @@ void CMakeManager::buildFile(Node *node)
CMakeTargetNode *targetNode = dynamic_cast<CMakeTargetNode *>(fileNode->parentProjectNode());
if (!targetNode)
return;
Utils::FilePath filePath = fileNode->filePath();
if (filePath.fileName().contains(".h")) {
bool wasHeader = false;
const QString sourceFile = CppEditor::correspondingHeaderOrSource(filePath.toString(), &wasHeader);
if (wasHeader && !sourceFile.isEmpty())
filePath = Utils::FilePath::fromString(sourceFile);
}
Target *target = project->activeTarget();
QTC_ASSERT(target, return);
const QString generator = CMakeGeneratorKitAspect::generator(target->kit());
const QString relativeSource = fileNode->filePath().relativeChildPath(targetNode->filePath()).toString();
const QString relativeSource = filePath.relativeChildPath(targetNode->filePath()).toString();
const QString objExtension = Utils::HostOsInfo::isWindowsHost() ? QString(".obj") : QString(".o");
Utils::FilePath targetBase;
BuildConfiguration *bc = target->activeBuildConfiguration();

View File

@@ -264,18 +264,18 @@ QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
continue;
const FilePath buildDir = haveLibrariesRelativeToBuildDirectory ? buildDirectory : currentBuildDir;
FilePath tmp = buildDir.resolvePath(FilePath::fromUserInput(part));
FilePath tmp = buildDir.resolvePath(FilePath::fromUserInput(part).onDevice(buildDir));
if (f.role == "libraries")
tmp = tmp.parentDir();
if (!tmp.isEmpty() && tmp.isDir()) {
// f.role is libraryPath or frameworkPath
// On Linux, exclude sub-paths from "/lib(64)", "/usr/lib(64)" and
// On *nix, exclude sub-paths from "/lib(64)", "/usr/lib(64)" and
// "/usr/local/lib" since these are usually in the standard search
// paths. There probably are more, but the naming schemes are arbitrary
// so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR").
if (!HostOsInfo::isLinuxHost()
if (buildDir.osType() == OsTypeWindows
|| !isChildOf(tmp,
{"/lib",
"/lib64",
@@ -285,9 +285,8 @@ QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input,
librarySeachPaths.append(tmp);
// Libraries often have their import libs in ../lib and the
// actual dll files in ../bin on windows. Qt is one example of that.
if (tmp.fileName() == "lib" && HostOsInfo::isWindowsHost()) {
if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) {
const FilePath path = tmp.parentDir().pathAppended("bin");
if (path.isDir())
librarySeachPaths.append(path);
}

View File

@@ -5,34 +5,42 @@
#include "actionmanager.h"
#include "command.h"
#include <utils/proxyaction.h>
#include <utils/qtcassert.h>
using namespace Core;
using namespace Utils;
/*!
\class Core::CommandAction
\inheaderfile coreplugin/actionmanager/commandbutton.h
\inmodule QtCreator
\brief The CommandAction class is an action associated with one of
the registered Command objects.
It shares the icon and text of the command.
The tooltip of the action consists of toolTipBase property value and Command's
key sequence which is automatically updated when user changes it.
*/
/*!
\class Core::CommandButton
\inheaderfile coreplugin/actionmanager/commandbutton.h
\inmodule QtCreator
\brief The CommandButton class is a tool button associated with one of
\brief The CommandButton class is an action associated with one of
the registered Command objects.
Tooltip of this button consists of toolTipBase property value and Command's
The tooltip of the button consists of toolTipBase property value and Command's
key sequence which is automatically updated when user changes it.
*/
/*!
\property CommandButton::toolTipBase
\brief The tool tip base for the command button.
*/
/*!
\internal
*/
CommandButton::CommandButton(QWidget *parent)
: QToolButton(parent)
CommandAction::CommandAction(QWidget *parent)
: QAction(parent)
, m_command(nullptr)
{
}
@@ -40,8 +48,8 @@ CommandButton::CommandButton(QWidget *parent)
/*!
\internal
*/
CommandButton::CommandButton(Id id, QWidget *parent)
: QToolButton(parent)
CommandAction::CommandAction(Id id, QWidget *parent)
: QAction(parent)
, m_command(nullptr)
{
setCommandId(id);
@@ -50,12 +58,83 @@ CommandButton::CommandButton(Id id, QWidget *parent)
/*!
Sets the ID of the command associated with this tool button to \a id.
*/
void CommandButton::setCommandId(Id id)
void CommandAction::setCommandId(Id id)
{
if (m_command)
disconnect(m_command.data(), &Command::keySequenceChanged, this, &CommandButton::updateToolTip);
disconnect(m_command.data(),
&Command::keySequenceChanged,
this,
&CommandAction::updateToolTip);
m_command = ActionManager::command(id);
QTC_ASSERT(m_command, return);
if (m_toolTipBase.isEmpty())
m_toolTipBase = m_command->description();
setIcon(m_command->action()->icon());
setIconText(m_command->action()->iconText());
setText(m_command->action()->text());
updateToolTip();
connect(m_command.data(), &Command::keySequenceChanged, this, &CommandAction::updateToolTip);
}
/*!
The base tool tip that is extended with the command's shortcut.
Defaults to the command's description.
\sa Command::description()
*/
QString CommandAction::toolTipBase() const
{
return m_toolTipBase;
}
/*!
Sets the base tool tip that is extended with the command's shortcut.
\sa toolTipBase()
*/
void CommandAction::setToolTipBase(const QString &toolTipBase)
{
m_toolTipBase = toolTipBase;
updateToolTip();
}
void CommandAction::updateToolTip()
{
if (m_command)
setToolTip(Utils::ProxyAction::stringWithAppendedShortcut(m_toolTipBase,
m_command->keySequence()));
}
/*!
\internal
*/
CommandButton::CommandButton(QWidget *parent)
: QToolButton(parent)
{}
/*!
\internal
*/
CommandButton::CommandButton(Utils::Id id, QWidget *parent)
: QToolButton(parent)
{
setCommandId(id);
}
void CommandButton::setCommandId(Utils::Id id)
{
if (m_command)
disconnect(m_command.data(),
&Command::keySequenceChanged,
this,
&CommandButton::updateToolTip);
m_command = ActionManager::command(id);
QTC_ASSERT(m_command, return);
if (m_toolTipBase.isEmpty())
m_toolTipBase = m_command->description();
@@ -64,11 +143,22 @@ void CommandButton::setCommandId(Id id)
connect(m_command.data(), &Command::keySequenceChanged, this, &CommandButton::updateToolTip);
}
/*!
The base tool tip that is extended with the command's shortcut.
Defaults to the command's description.
\sa Command::description()
*/
QString CommandButton::toolTipBase() const
{
return m_toolTipBase;
}
/*!
Sets the base tool tip that is extended with the command's shortcut.
\sa toolTipBase()
*/
void CommandButton::setToolTipBase(const QString &toolTipBase)
{
m_toolTipBase = toolTipBase;

View File

@@ -7,6 +7,7 @@
#include <utils/id.h>
#include <QAction>
#include <QPointer>
#include <QString>
#include <QToolButton>
@@ -15,10 +16,28 @@ namespace Core {
class Command;
class CORE_EXPORT CommandAction : public QAction
{
Q_OBJECT
public:
explicit CommandAction(QWidget *parent = nullptr);
explicit CommandAction(Utils::Id id, QWidget *parent = nullptr);
void setCommandId(Utils::Id id);
QString toolTipBase() const;
void setToolTipBase(const QString &toolTipBase);
private:
void updateToolTip();
QPointer<Command> m_command;
QString m_toolTipBase;
};
class CORE_EXPORT CommandButton : public QToolButton
{
Q_OBJECT
Q_PROPERTY(QString toolTipBase READ toolTipBase WRITE setToolTipBase)
public:
explicit CommandButton(QWidget *parent = nullptr);
explicit CommandButton(Utils::Id id, QWidget *parent = nullptr);
@@ -32,5 +51,4 @@ private:
QPointer<Command> m_command;
QString m_toolTipBase;
};
}

View File

@@ -1032,11 +1032,12 @@ void DocumentManager::showFilePropertiesDialog(const FilePath &filePath)
FilePaths DocumentManager::getOpenFileNames(const QString &filters,
const FilePath &pathIn,
QString *selectedFilter)
QString *selectedFilter,
QFileDialog::Options options)
{
const FilePath path = pathIn.isEmpty() ? fileDialogInitialDirectory() : pathIn;
const FilePaths files = FileUtils::getOpenFilePaths(nullptr, tr("Open File"), path, filters,
selectedFilter);
selectedFilter, options);
if (!files.isEmpty())
setFileDialogLastVisitedDirectory(files.front().absolutePath());
return files;

View File

@@ -8,6 +8,7 @@
#include <utils/filepath.h>
#include <utils/id.h>
#include <QFileDialog>
#include <QObject>
#include <QPair>
@@ -64,7 +65,8 @@ public:
static Utils::FilePaths getOpenFileNames(const QString &filters,
const Utils::FilePath &path = {},
QString *selectedFilter = nullptr);
QString *selectedFilter = nullptr,
QFileDialog::Options options = {});
static Utils::FilePath getSaveFileName(const QString &title,
const Utils::FilePath &pathIn,
const QString &filter = {},

View File

@@ -3249,11 +3249,11 @@ void EditorManager::addCloseEditorListener(const std::function<bool (IEditor *)>
\sa DocumentManager::getOpenFileNames()
*/
FilePaths EditorManager::getOpenFilePaths()
FilePaths EditorManager::getOpenFilePaths(QFileDialog::Options options)
{
QString selectedFilter;
const QString &fileFilters = DocumentManager::fileDialogFilter(&selectedFilter);
return DocumentManager::getOpenFileNames(fileFilters, {}, &selectedFilter);
return DocumentManager::getOpenFileNames(fileFilters, {}, &selectedFilter, options);
}
static QString makeTitleUnique(QString *titlePattern)

View File

@@ -12,6 +12,7 @@
#include "utils/link.h"
#include "utils/textfileformat.h"
#include <QFileDialog>
#include <QList>
#include <QWidget>
@@ -90,7 +91,7 @@ public:
static bool openExternalEditor(const Utils::FilePath &filePath, Utils::Id editorId);
static void addCloseEditorListener(const std::function<bool(IEditor *)> &listener);
static Utils::FilePaths getOpenFilePaths();
static Utils::FilePaths getOpenFilePaths(QFileDialog::Options options = {});
static IDocument *currentDocument();
static IEditor *currentEditor();

View File

@@ -205,6 +205,9 @@ void ProxyModel::setSourceModel(QAbstractItemModel *sm)
QVariant ProxyModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DecorationRole && index.column() == 0) {
const QVariant sourceDecoration = QAbstractProxyModel::data(index, role);
if (sourceDecoration.isValid())
return sourceDecoration;
const QString fileName = QAbstractProxyModel::data(index, Qt::DisplayRole).toString();
return Utils::FileIconProvider::icon(Utils::FilePath::fromString(fileName));
}

View File

@@ -524,8 +524,8 @@ void MainWindow::registerDefaultContainers()
QIcon(),
"Main TouchBar" /*never visible*/);
ac->appendGroup(Constants::G_TOUCHBAR_HELP);
ac->appendGroup(Constants::G_TOUCHBAR_EDITOR);
ac->appendGroup(Constants::G_TOUCHBAR_NAVIGATION);
ac->appendGroup(Constants::G_TOUCHBAR_EDITOR);
ac->appendGroup(Constants::G_TOUCHBAR_OTHER);
ac->touchBar()->setApplicationTouchBar();
}
@@ -1078,37 +1078,7 @@ void MainWindow::openFileWith()
void MainWindow::openFileFromDevice()
{
QSettings *settings = PluginManager::settings();
settings->beginGroup(QLatin1String(settingsGroup));
QVariant dialogSettings = settings->value(QLatin1String(openFromDeviceDialogKey));
QFileDialog dialog;
dialog.setOption(QFileDialog::DontUseNativeDialog);
if (!dialogSettings.isNull()) {
dialog.restoreState(dialogSettings.toByteArray());
}
QList<QUrl> sideBarUrls = Utils::transform(Utils::filtered(FSEngine::registeredDeviceRoots(),
[](const auto &filePath) {
return filePath.exists();
}),
[](const auto &filePath) {
return QUrl::fromLocalFile(filePath.toFSPathString());
});
dialog.setSidebarUrls(sideBarUrls);
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setIconProvider(FileIconProvider::iconProvider());
if (dialog.exec()) {
FilePaths filePaths = Utils::transform(dialog.selectedFiles(), [](const auto &path) {
return FilePath::fromString(path);
});
openFiles(filePaths, ICore::SwitchMode);
}
settings->setValue(QLatin1String(openFromDeviceDialogKey), dialog.saveState());
settings->endGroup();
openFiles(EditorManager::getOpenFilePaths(QFileDialog::DontUseNativeDialog), ICore::SwitchMode);
}
IContext *MainWindow::contextObject(QWidget *widget) const

View File

@@ -297,9 +297,11 @@ MimeTypeSettingsPrivate::~MimeTypeSettingsPrivate() = default;
void MimeTypeSettingsPrivate::configureUi(QWidget *w)
{
auto filterLineEdit = new FancyLineEdit;
filterLineEdit->setObjectName("filterLineEdit");
filterLineEdit->setFiltering(true);
m_mimeTypesTreeView = new QTreeView;
m_mimeTypesTreeView->setObjectName("mimeTypesTreeView");
m_mimeTypesTreeView->setEditTriggers(QAbstractItemView::DoubleClicked
|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked);
m_mimeTypesTreeView->setRootIsDecorated(false);
@@ -317,6 +319,7 @@ void MimeTypeSettingsPrivate::configureUi(QWidget *w)
patternsLabel->setText(QCoreApplication::translate("Core::Internal::MimeTypeSettingsPage", "Patterns:", nullptr));
m_patternsLineEdit = new QLineEdit;
m_patternsLineEdit->setObjectName("patternsLineEdit");
m_patternsLineEdit->setToolTip(QCoreApplication::translate("Core::Internal::MimeTypeSettingsPage", "A semicolon-separated list of wildcarded file names.", nullptr));
m_magicHeadersTreeWidget = new QTreeWidget;

View File

@@ -34,8 +34,6 @@ private:
QNetworkReply *m_listReply = nullptr;
QString m_fetchId;
int m_postId = -1;
bool m_hostChecked = false;
};
} // CodePaster

View File

@@ -25,15 +25,19 @@ PasteSelectDialog::PasteSelectDialog(const QList<Protocol*> &protocols, QWidget
QDialog(parent),
m_protocols(protocols)
{
setObjectName("CodePaster.PasteSelectDialog");
resize(550, 350);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_protocolBox = new QComboBox(this);
m_protocolBox->setObjectName("protocolBox");
m_pasteEdit = new QLineEdit(this);
m_pasteEdit->setObjectName("pasteEdit");
m_pasteEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_listWidget = new QListWidget(this);
m_listWidget->setObjectName("listWidget");
m_listWidget->setAlternatingRowColors(true);
auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

View File

@@ -41,10 +41,12 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
m_commentPlaceHolder(Tr::tr("<Comment>")),
m_mimeType(mt)
{
setObjectName("CodePaster.ViewDialog");
resize(670, 678);
setWindowTitle(Tr::tr("Send to Codepaster"));
m_protocolBox = new QComboBox;
m_protocolBox->setObjectName("protocolBox");
for (const Protocol *p : protocols)
m_protocolBox->addItem(p->name());
@@ -56,6 +58,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
m_uiUsername->setPlaceholderText(Tr::tr("<Username>"));
m_uiDescription = new QLineEdit(this);
m_uiDescription->setObjectName("uiDescription");
m_uiDescription->setPlaceholderText(Tr::tr("<Description>"));
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -83,6 +86,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
groupBox->setFlat(true);
m_plainTextEdit = new QPlainTextEdit;
m_plainTextEdit->setObjectName("plainTextEdit");
m_stackedWidget = new QStackedWidget(this);
m_stackedWidget->addWidget(groupBox);

View File

@@ -39,7 +39,8 @@ public:
CppcheckOptionsPage options;
DiagnosticsModel manualRunModel;
CppcheckTool manualRunTool;
Utils::Perspective perspective{Constants::PERSPECTIVE_ID, CppcheckPlugin::tr("Cppcheck")};
Utils::Perspective perspective{Constants::PERSPECTIVE_ID,
::Cppcheck::Internal::CppcheckPlugin::tr("Cppcheck")};
QAction *manualRunAction;
void startManualRun();

View File

@@ -227,7 +227,9 @@ QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objcExt)
void CompilerOptionsBuilder::addWordWidth()
{
const QString argument = m_projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit
if (m_projectPart.toolChainAbi.architecture() != Abi::X86Architecture)
return;
const QString argument = m_projectPart.toolChainAbi.wordWidth() == 64
? QLatin1String("-m64")
: QLatin1String("-m32");
add(argument);

View File

@@ -37,8 +37,12 @@ public:
rpp.setConfigFileName(projectConfigFile);
ToolChainInfo tcInfo;
tcInfo.type = toolchainType;
tcInfo.wordWidth = 64;
tcInfo.targetTriple = targetTriple;
tcInfo.abi = Abi::fromString(targetTriple);
if (!tcInfo.abi.isValid()) {
tcInfo.abi = Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::FreeBsdFlavor,
Abi::MachOFormat, 64);
}
tcInfo.isMsvc2015ToolChain = isMsvc2015;
tcInfo.extraCodeModelFlags = extraFlags;
tcInfo.macroInspectionRunner = [this](const QStringList &) {

View File

@@ -1789,7 +1789,7 @@ void CppCodeModelInspectorDialog::updateProjectPartData(const ProjectPart::Const
{QString::fromLatin1("Build Target Type"), CMI::Utils::toString(part->buildTargetType)},
{QString::fromLatin1("ToolChain Type"), part->toolchainType.toString()},
{QString::fromLatin1("ToolChain Target Triple"), part->toolChainTargetTriple},
{QString::fromLatin1("ToolChain Word Width"), CMI::Utils::toString(part->toolChainWordWidth)},
{QString::fromLatin1("ToolChain Word Width"), CMI::Utils::toString(part->toolChainAbi.wordWidth())},
{QString::fromLatin1("ToolChain Install Dir"), part->toolChainInstallDir.toString()},
{QString::fromLatin1("Language Version"), CMI::Utils::toString(part->languageVersion)},
{QString::fromLatin1("Language Extensions"), CMI::Utils::toString(part->languageExtensions)},

View File

@@ -373,15 +373,15 @@ QString Utils::toString(CPlusPlus::Kind kind)
return QString();
}
QString Utils::toString(ProjectPart::ToolChainWordWidth width)
QString Utils::toString(const ProjectExplorer::Abi &abi)
{
switch (width) {
case ProjectPart::ToolChainWordWidth::WordWidth32Bit:
switch (abi.wordWidth()) {
case 32:
return QString("32");
case ProjectPart::ToolChainWordWidth::WordWidth64Bit:
case 64:
return QString("64");
}
return QString();
return QString("??");
}
QString Utils::partsForFile(const QString &fileName)
@@ -508,7 +508,7 @@ void Dumper::dumpProjectInfos(const QList<ProjectInfo::ConstPtr> &projectInfos)
m_out << i3 << "Project File : " << projectFilePath << "\n";
m_out << i3 << "ToolChain Type : " << part->toolchainType.toString() << "\n";
m_out << i3 << "ToolChain Target Triple: " << part->toolChainTargetTriple << "\n";
m_out << i3 << "ToolChain Word Width : " << part->toolChainWordWidth << "\n";
m_out << i3 << "ToolChain Word Width : " << part->toolChainAbi.wordWidth() << "\n";
m_out << i3 << "ToolChain Install Dir : " << part->toolChainInstallDir << "\n";
m_out << i3 << "Compiler Flags : " << part->compilerFlags.join(", ") << "\n";
m_out << i3 << "Selected For Building : " << part->selectedForBuilding << "\n";

View File

@@ -32,7 +32,7 @@ struct Utils
static QString toString(const QVector<ProjectFile> &projectFiles);
static QString toString(ProjectFile::Kind kind);
static QString toString(CPlusPlus::Kind kind);
static QString toString(ProjectPart::ToolChainWordWidth width);
static QString toString(const ProjectExplorer::Abi &abi);
static QString partsForFile(const QString &fileName);
static QString unresolvedFileNameWithDelimiters(const CPlusPlus::Document::Include &include);
static QString pathListToString(const QStringList &pathList);

View File

@@ -137,6 +137,27 @@ ProjectPart::ConstPtr ProjectInfoGenerator::createProjectPart(
tcInfo = m_projectUpdateInfo.cxxToolChainInfo;
}
QString explicitTarget;
if (!tcInfo.targetTripleIsAuthoritative) {
for (int i = 0; i < flags.commandLineFlags.size(); ++i) {
const QString &flag = flags.commandLineFlags.at(i);
if (flag == "-target") {
if (i + 1 < flags.commandLineFlags.size())
explicitTarget = flags.commandLineFlags.at(i + 1);
break;
} else if (flag.startsWith("--target=")) {
explicitTarget = flag.mid(9);
break;
}
}
}
if (!explicitTarget.isEmpty()) {
tcInfo.targetTriple = explicitTarget;
tcInfo.targetTripleIsAuthoritative = true;
if (const Abi abi = Abi::fromString(tcInfo.targetTriple); abi.isValid())
tcInfo.abi = abi;
}
return ProjectPart::create(projectFilePath, rawProjectPart, partName, projectFiles,
language, languageExtensions, flags, tcInfo);
}

View File

@@ -145,8 +145,7 @@ ProjectPart::ProjectPart(const Utils::FilePath &topLevelProject,
isMsvc2015Toolchain(tcInfo.isMsvc2015ToolChain),
toolChainTargetTriple(tcInfo.targetTriple),
targetTripleIsAuthoritative(tcInfo.targetTripleIsAuthoritative),
toolChainWordWidth(tcInfo.wordWidth == 64 ? ProjectPart::WordWidth64Bit
: ProjectPart::WordWidth32Bit),
toolChainAbi(tcInfo.abi),
toolChainInstallDir(tcInfo.installDir),
compilerFilePath(tcInfo.compilerFilePath),
warningFlags(flags.warningFlags),

View File

@@ -28,11 +28,6 @@ namespace CppEditor {
class CPPEDITOR_EXPORT ProjectPart
{
public:
enum ToolChainWordWidth {
WordWidth32Bit,
WordWidth64Bit,
};
using ConstPtr = QSharedPointer<const ProjectPart>;
public:
@@ -94,7 +89,7 @@ public:
const bool isMsvc2015Toolchain = false;
const QString toolChainTargetTriple;
const bool targetTripleIsAuthoritative;
const ToolChainWordWidth toolChainWordWidth = WordWidth32Bit;
const ProjectExplorer::Abi toolChainAbi = ProjectExplorer::Abi::hostAbi();
const Utils::FilePath toolChainInstallDir;
const Utils::FilePath compilerFilePath;
const Utils::WarningFlags warningFlags = Utils::WarningFlags::Default;

View File

@@ -46,7 +46,7 @@ private:
void toggleThreadRestriction(QAction *action);
Utils::Perspective m_perspective{Constants::CtfVisualizerPerspectiveId,
Tr::tr("Chrome Trace Format Visualizer")};
::CtfVisualizer::Tr::tr("Chrome Trace Format Visualizer")};
bool m_isLoading;
QScopedPointer<QAction> m_loadJson;

View File

@@ -611,9 +611,6 @@ void CdbEngine::shutdownInferior()
if (commandsPending()) {
showMessage("Cannot shut down inferior due to pending commands.", LogWarning);
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFinished")
} else if (!canInterruptInferior()) {
showMessage("Cannot interrupt the inferior.", LogWarning);
STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFinished")
} else {
interruptInferior(); // Calls us again
return;
@@ -758,11 +755,6 @@ void CdbEngine::doContinueInferior()
runCommand({"g", NoFlags});
}
bool CdbEngine::canInterruptInferior() const
{
return m_effectiveStartMode != AttachToRemoteServer && inferiorPid();
}
void CdbEngine::interruptInferior()
{
if (debug)
@@ -771,6 +763,18 @@ void CdbEngine::interruptInferior()
doInterruptInferior();
}
void CdbEngine::handleDoInterruptInferior(const QString &errorMessage)
{
if (errorMessage.isEmpty()) {
showMessage("Interrupted " + QString::number(inferiorPid()));
} else {
showMessage(errorMessage, LogError);
notifyInferiorStopFailed();
}
m_signalOperation->disconnect(this);
m_signalOperation.clear();
}
void CdbEngine::doInterruptInferior(const InterruptCallback &callback)
{
const bool requestInterrupt = m_stopMode == NoStopRequested;
@@ -787,6 +791,18 @@ void CdbEngine::doInterruptInferior(const InterruptCallback &callback)
if (!requestInterrupt)
return; // we already requested a stop no need to interrupt twice
showMessage(QString("Interrupting process %1...").arg(inferiorPid()), LogMisc);
QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed(); return);
if (m_effectiveStartMode != AttachToRemoteServer && device()) {
m_signalOperation = device()->signalOperation();
if (m_signalOperation) {
connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished,
this, &CdbEngine::handleDoInterruptInferior);
m_signalOperation->setDebuggerCommand(runParameters().debugger.command.executable());
m_signalOperation->interruptProcess(inferiorPid());
return;
}
}
m_process.interrupt();
}

View File

@@ -85,6 +85,8 @@ private:
void createFullBacktrace();
void handleDoInterruptInferior(const QString &errorMessage);
typedef QPair<QString, QString> SourcePathMapping;
struct NormalizedSourceFileName // Struct for caching mapped/normalized source files.
{
@@ -129,7 +131,6 @@ private:
void doContinueInferior();
void parseOutputLine(QString line);
bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; }
bool canInterruptInferior() const;
inline void postDisassemblerCommand(quint64 address, DisassemblerAgent *agent);
void postDisassemblerCommand(quint64 address, quint64 endAddress,
DisassemblerAgent *agent);
@@ -176,6 +177,7 @@ private:
//! Debugger accessible (expecting commands)
bool m_accessible = false;
StopMode m_stopMode = NoStopRequested;
ProjectExplorer::DeviceProcessSignalOperation::Ptr m_signalOperation;
int m_nextCommandToken = 0;
QHash<int, DebuggerCommand> m_commandForToken;
QString m_currentBuiltinResponse;

View File

@@ -77,7 +77,6 @@ UnifiedDiffEditorWidget::~UnifiedDiffEditorWidget()
void UnifiedDiffEditorWidget::setDocument(DiffEditorDocument *document)
{
m_controller.setBusyShowing(true);
m_controller.setDocument(document);
clear();
setDiff(document ? document->diffFiles() : QList<FileData>());

View File

@@ -32,6 +32,7 @@
#include <utils/algorithm.h>
#include <utils/basetreeview.h>
#include <utils/devicefileaccess.h>
#include <utils/deviceshell.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
@@ -113,6 +114,21 @@ private:
FilePath m_devicePath;
};
class DockerDeviceFileAccess : public UnixDeviceFileAccess
{
public:
DockerDeviceFileAccess(DockerDevicePrivate *dev)
: m_dev(dev)
{}
RunResult runInShell(const CommandLine &cmdLine,
const QByteArray &stdInData) const override;
QString mapToDevicePath(const FilePath &filePath) const override;
OsType osType(const FilePath &filePath) const override;
DockerDevicePrivate *m_dev = nullptr;
};
class DockerDevicePrivate : public QObject
{
public:
@@ -125,11 +141,6 @@ public:
~DockerDevicePrivate() { stopCurrentContainer(); }
RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {});
bool runInShellSuccess(const CommandLine &cmd, const QByteArray &stdInData = {}) {
return runInShell(cmd, stdInData).exitCode == 0;
}
std::optional<QByteArray> fileContents(const FilePath &filePath, qint64 limit, qint64 offset);
void updateContainerAccess();
void changeMounts(QStringList newMounts);
@@ -177,8 +188,8 @@ public:
QString m_container;
Environment m_cachedEnviroment;
bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback
bool m_isShutdown = false;
DockerDeviceFileAccess m_fileAccess{this};
};
class DockerProcessImpl : public Utils::ProcessInterface
@@ -337,9 +348,36 @@ Tasks DockerDevicePrivate::validateMounts() const
return result;
}
RunResult DockerDeviceFileAccess::runInShell(const CommandLine &cmdLine,
const QByteArray &stdInData) const
{
QTC_ASSERT(m_dev, return {});
return m_dev->runInShell(cmdLine, stdInData);
}
QString DockerDeviceFileAccess::mapToDevicePath(const FilePath &filePath) const
{
// make sure to convert windows style paths to unix style paths with the file system case:
// C:/dev/src -> /c/dev/src
const FilePath normalized = FilePath::fromString(filePath.path()).normalizedPathName();
QString path = normalized.path();
if (HostOsInfo::isWindowsHost() && normalized.startsWithDriveLetter()) {
const QChar lowerDriveLetter = path.at(0);
path = '/' + lowerDriveLetter + path.mid(2); // strip C:
}
return path;
}
OsType DockerDeviceFileAccess::osType(const FilePath &filePath) const
{
QTC_ASSERT(m_dev, return UnixDeviceFileAccess::osType(filePath));
return m_dev->q->osType();
}
DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data)
: d(new DockerDevicePrivate(this, settings, data))
{
setFileAccess(&d->m_fileAccess);
setDisplayType(Tr::tr("Docker"));
setOsType(OsTypeOtherUnix);
setDefaultDisplayName(Tr::tr("Docker Image"));
@@ -423,7 +461,7 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd, bool
args << m_container;
CommandLine dcmd{m_settings->dockerBinaryPath.filePath(), args};
dcmd.addCommandLineAsArgs(cmd);
dcmd.addCommandLineAsArgs(cmd, CommandLine::Raw);
return dcmd;
}
@@ -448,20 +486,6 @@ void DockerDevicePrivate::stopCurrentContainer()
m_cachedEnviroment.clear();
}
static QString getLocalIPv4Address()
{
const QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
for (auto &a : addresses) {
if (a.isInSubnet(QHostAddress("192.168.0.0"), 16))
return a.toString();
if (a.isInSubnet(QHostAddress("10.0.0.0"), 8))
return a.toString();
if (a.isInSubnet(QHostAddress("172.16.0.0"), 12))
return a.toString();
}
return QString();
}
bool DockerDevicePrivate::prepareForBuild(const Target *target)
{
QTC_ASSERT(QThread::currentThread() == thread(), return false);
@@ -483,18 +507,21 @@ QString escapeMountPathWin(const FilePath &fp)
return result;
}
QString escapeMountPath(const FilePath &fp)
{
if (HostOsInfo::isWindowsHost())
return escapeMountPathWin(fp);
return escapeMountPathUnix(fp);
}
QStringList toMountArg(const DockerDevicePrivate::TemporaryMountInfo &mi)
{
QString escapedPath;
QString escapedContainerPath;
if (HostOsInfo::isWindowsHost()) {
escapedPath = escapeMountPathWin(mi.path);
escapedContainerPath = escapeMountPathWin(mi.containerPath);
} else {
escapedPath = escapeMountPathUnix(mi.path);
escapedContainerPath = escapeMountPathUnix(mi.containerPath);
}
escapedPath = escapeMountPath(mi.path);
escapedContainerPath = escapeMountPath(mi.containerPath);
const QString mountArg = QString(R"(type=bind,"source=%1","destination=%2")")
.arg(escapedPath)
@@ -505,8 +532,18 @@ QStringList toMountArg(const DockerDevicePrivate::TemporaryMountInfo &mi)
bool isValidMountInfo(const DockerDevicePrivate::TemporaryMountInfo &mi)
{
return !mi.path.isEmpty() && !mi.containerPath.isEmpty() && mi.path.isAbsolutePath()
&& mi.containerPath.isAbsolutePath();
if (mi.path.needsDevice())
return false;
if (mi.path.isEmpty() || mi.containerPath.isEmpty())
return false;
if (!mi.path.isAbsolutePath() || !mi.containerPath.isAbsolutePath())
return false;
if (!mi.path.exists())
return false;
return true;
}
QStringList DockerDevicePrivate::createMountArgs() const
@@ -530,7 +567,7 @@ bool DockerDevicePrivate::createContainer()
return false;
const QString display = HostOsInfo::isLinuxHost() ? QString(":0")
: QString(getLocalIPv4Address() + ":0.0");
: QString("host.docker.internal:0");
CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(),
{"create",
"-i",
@@ -744,19 +781,6 @@ FilePath DockerDevice::mapToGlobalPath(const FilePath &pathOnDevice) const
// result.setHost(id().toString());
}
QString DockerDevice::mapToDevicePath(const Utils::FilePath &globalPath) const
{
// make sure to convert windows style paths to unix style paths with the file system case:
// C:/dev/src -> /c/dev/src
const FilePath normalized = FilePath::fromString(globalPath.path()).normalizedPathName();
QString path = normalized.path();
if (HostOsInfo::isWindowsHost() && normalized.startsWithDriveLetter()) {
const QChar lowerDriveLetter = path.at(0).toLower();
path = '/' + lowerDriveLetter + path.mid(2); // strip C:
}
return path;
}
Utils::FilePath DockerDevice::rootPath() const
{
return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME, d->repoAndTag(), u"/");
@@ -778,167 +802,11 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const
return false;
}
bool DockerDevice::isExecutableFile(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-x", path}});
}
bool DockerDevice::isReadableFile(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}});
}
bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}});
}
bool DockerDevice::isReadableDirectory(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}});
}
bool DockerDevice::isWritableDirectory(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}});
}
bool DockerDevice::isFile(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-f", path}});
}
bool DockerDevice::isDirectory(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-d", path}});
}
bool DockerDevice::createDirectory(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"mkdir", {"-p", path}});
}
bool DockerDevice::exists(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"test", {"-e", path}});
}
bool DockerDevice::ensureExistingFile(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
const QString path = filePath.path();
return d->runInShellSuccess({"touch", {path}});
}
bool DockerDevice::removeFile(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
return d->runInShellSuccess({"rm", {filePath.path()}});
}
bool DockerDevice::removeRecursively(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return false);
QTC_ASSERT(filePath.path().startsWith('/'), return false);
const QString path = filePath.cleanPath().path();
// We are expecting this only to be called in a context of build directories or similar.
// Chicken out in some cases that _might_ be user code errors.
QTC_ASSERT(path.startsWith('/'), return false);
const int levelsNeeded = path.startsWith("/home/") ? 4 : 3;
QTC_ASSERT(path.count('/') >= levelsNeeded, return false);
return d->runInShellSuccess({"rm", {"-rf", "--", path}});
}
bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) const
{
QTC_ASSERT(handlesFile(filePath), return false);
QTC_ASSERT(handlesFile(target), return false);
return d->runInShellSuccess({"cp", {filePath.path(), target.path()}});
}
bool DockerDevice::renameFile(const FilePath &filePath, const FilePath &target) const
{
QTC_ASSERT(handlesFile(filePath), return false);
QTC_ASSERT(handlesFile(target), return false);
return d->runInShellSuccess({"mv", {filePath.path(), target.path()}});
}
QDateTime DockerDevice::lastModified(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return {});
const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}});
qint64 secs = result.stdOut.toLongLong();
const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC);
return dt;
}
FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return {});
const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}});
const QString out = QString::fromUtf8(result.stdOut);
return out.isEmpty() ? FilePath() : filePath.withNewPath(out);
}
qint64 DockerDevice::fileSize(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return -1);
const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}});
return result.stdOut.toLongLong();
}
QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return {});
const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}});
const uint bits = result.stdOut.toUInt(nullptr, 8);
QFileDevice::Permissions perm = {};
#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
BIT(0, ExeOther);
BIT(1, WriteOther);
BIT(2, ReadOther);
BIT(3, ExeGroup);
BIT(4, WriteGroup);
BIT(5, ReadGroup);
BIT(6, ExeUser);
BIT(7, WriteUser);
BIT(8, ReadUser);
#undef BIT
return perm;
}
bool DockerDevice::setPermissions(const FilePath &filePath,
QFileDevice::Permissions permissions) const
{
Q_UNUSED(permissions)
QTC_ASSERT(handlesFile(filePath), return {});
QTC_CHECK(false); // FIXME: Implement.
return false;
}
bool DockerDevice::ensureReachable(const FilePath &other) const
{
if (other.isSameDevice(rootPath()))
return true;
if (other.needsDevice())
return false;
@@ -947,52 +815,6 @@ bool DockerDevice::ensureReachable(const FilePath &other) const
return d->ensureReachable(other.parentDir());
}
void DockerDevice::iterateDirectory(const FilePath &filePath,
const FilePath::IterateDirCallback &callBack,
const FileFilter &filter) const
{
QTC_ASSERT(handlesFile(filePath), return);
auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); };
FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack);
}
void DockerDevice::iterateDirectory(const FilePath &filePath,
const FilePath::IterateDirWithInfoCallback &callBack,
const FileFilter &filter) const
{
QTC_ASSERT(handlesFile(filePath), return);
auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); };
FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack);
}
std::optional<QByteArray> DockerDevice::fileContents(const FilePath &filePath,
qint64 limit,
qint64 offset) const
{
QTC_ASSERT(handlesFile(filePath), return {});
return d->fileContents(filePath, limit, offset);
}
bool DockerDevice::writeFileContents(const FilePath &filePath,
const QByteArray &data,
qint64 offset) const
{
QTC_ASSERT(handlesFile(filePath), return {});
CommandLine cmd({"dd", {"of=" + filePath.path()}});
if (offset != 0) {
cmd.addArg("bs=1");
cmd.addArg(QString("seek=%1").arg(offset));
}
return d->runInShellSuccess(cmd, data);
}
FilePathInfo DockerDevice::filePathInfo(const FilePath &filePath) const
{
QTC_ASSERT(handlesFile(filePath), return {});
const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}});
return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
}
Environment DockerDevice::systemEnvironment() const
{
return d->environment();
@@ -1004,28 +826,6 @@ void DockerDevice::aboutToBeRemoved() const
detector.undoAutoDetect(id().toString());
}
std::optional<QByteArray> DockerDevicePrivate::fileContents(const FilePath &filePath,
qint64 limit,
qint64 offset)
{
updateContainerAccess();
QStringList args = {"if=" + filePath.path(), "status=none"};
if (limit > 0 || offset > 0) {
const qint64 gcd = std::gcd(limit, offset);
args += {QString("bs=%1").arg(gcd),
QString("count=%1").arg(limit / gcd),
QString("seek=%1").arg(offset / gcd)};
}
const RunResult r = m_shell->runInShell({"dd", args});
if (r.exitCode != 0)
return {};
return r.stdOut;
}
void DockerDevicePrivate::fetchSystemEnviroment()
{
updateContainerAccess();
@@ -1277,8 +1077,12 @@ bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath
if (alreadyAdded)
return false;
const TemporaryMountInfo newMount{path, containerPath};
QTC_ASSERT(isValidMountInfo(newMount), return false);
qCDebug(dockerDeviceLog) << "Adding temporary mount:" << path;
m_temporaryMounts.append({path, containerPath});
m_temporaryMounts.append(newMount);
stopCurrentContainer(); // Force re-start with new mounts.
return true;
}

View File

@@ -79,44 +79,10 @@ public:
bool usableAsBuildDevice() const override;
Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override;
QString mapToDevicePath(const Utils::FilePath &globalPath) const override;
Utils::FilePath rootPath() const override;
bool handlesFile(const Utils::FilePath &filePath) const override;
bool isExecutableFile(const Utils::FilePath &filePath) const override;
bool isReadableFile(const Utils::FilePath &filePath) const override;
bool isWritableFile(const Utils::FilePath &filePath) const override;
bool isReadableDirectory(const Utils::FilePath &filePath) const override;
bool isWritableDirectory(const Utils::FilePath &filePath) const override;
bool isFile(const Utils::FilePath &filePath) const override;
bool isDirectory(const Utils::FilePath &filePath) const override;
bool createDirectory(const Utils::FilePath &filePath) const override;
bool exists(const Utils::FilePath &filePath) const override;
bool ensureExistingFile(const Utils::FilePath &filePath) const override;
bool removeFile(const Utils::FilePath &filePath) const override;
bool removeRecursively(const Utils::FilePath &filePath) const override;
bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override;
void iterateDirectory(const Utils::FilePath &filePath,
const Utils::FilePath::IterateDirCallback &callBack,
const Utils::FileFilter &filter) const override;
void iterateDirectory(const Utils::FilePath &filePath,
const Utils::FilePath::IterateDirWithInfoCallback &callBack,
const Utils::FileFilter &filter) const override;
std::optional<QByteArray> fileContents(const Utils::FilePath &filePath,
qint64 limit,
qint64 offset) const override;
bool writeFileContents(const Utils::FilePath &filePath,
const QByteArray &data,
qint64 offset) const override;
Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override;
QDateTime lastModified(const Utils::FilePath &filePath) const override;
qint64 fileSize(const Utils::FilePath &filePath) const override;
QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override;
bool setPermissions(const Utils::FilePath &filePath,
QFileDevice::Permissions permissions) const override;
bool ensureReachable(const Utils::FilePath &other) const override;
Utils::Environment systemEnvironment() const override;

View File

@@ -81,7 +81,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
m_runAsOutsideUser->setToolTip(Tr::tr("Uses user ID and group ID of the user running Qt Creator "
"in the docker container."));
m_runAsOutsideUser->setChecked(m_data.useLocalUidGid);
m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost());
m_runAsOutsideUser->setEnabled(HostOsInfo::isAnyUnixHost());
connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [this, dockerDevice](bool on) {
m_data.useLocalUidGid = on;

View File

@@ -233,7 +233,7 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const
emit q->logOutput(ProjectExplorer::Tr::tr("Searching for qmake executables..."));
const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"};
const QStringList candidates = {"qmake6", "qmake-qt6", "qmake-qt5", "qmake"};
for (const FilePath &searchPath : m_searchPaths) {
searchPath.iterateDirectory(handleQmake,
{candidates,

View File

@@ -278,7 +278,8 @@ public:
: GitBaseDiffEditorController(document, leftCommit, rightCommit)
{
setReloader([this, extraArgs] {
runCommand({addConfigurationArguments(baseArguments() << extraArgs)});
runCommand({addConfigurationArguments(baseArguments() << extraArgs)},
VcsBaseEditor::getCodec(workingDirectory(), {}));
});
}
};
@@ -413,7 +414,7 @@ public:
argLists << addConfigurationArguments(baseArguments() << "--" << unstagedFiles);
if (!argLists.isEmpty())
runCommand(argLists);
runCommand(argLists, VcsBaseEditor::getCodec(workingDirectory(), stagedFiles + unstagedFiles));
});
}
};
@@ -1353,9 +1354,10 @@ VcsBaseEditorWidget *GitClient::annotate(
editor->setWorkingDirectory(workingDir);
QStringList arguments = {"blame", "--root"};
arguments << argWidget->arguments() << "--" << file;
arguments << argWidget->arguments();
if (!revision.isEmpty())
arguments << revision;
arguments << "--" << file;
editor->setDefaultLineNumber(lineNumber);
vcsExec(workingDir, arguments, editor);
return editor;

View File

@@ -28,8 +28,9 @@
#include <QImageReader>
#include <QLabel>
#include <QMap>
#include <QMenu>
#include <QSpacerItem>
#include <QWidget>
#include <QToolBar>
#include <QWidget>
using namespace Core;
@@ -44,16 +45,17 @@ struct ImageViewerPrivate
ImageView *imageView;
QWidget *toolbar;
CommandButton *toolButtonExportImage;
CommandButton *toolButtonMultiExportImages;
CommandButton *toolButtonCopyDataUrl;
CommandButton *toolButtonBackground;
CommandButton *toolButtonOutline;
CommandButton *toolButtonFitToScreen;
CommandButton *toolButtonOriginalSize;
CommandButton *toolButtonZoomIn;
CommandButton *toolButtonZoomOut;
CommandButton *toolButtonPlayPause;
QToolButton *shareButton;
CommandAction *actionExportImage;
CommandAction *actionMultiExportImages;
CommandAction *actionButtonCopyDataUrl;
CommandAction *actionBackground;
CommandAction *actionOutline;
CommandAction *actionFitToScreen;
CommandAction *actionOriginalSize;
CommandAction *actionZoomIn;
CommandAction *actionZoomOut;
CommandAction *actionPlayPause;
QLabel *labelImageSize;
QLabel *labelInfo;
};
@@ -63,12 +65,12 @@ struct ImageViewerPrivate
from the current theme. Returns \c true if icon is updated, \c false
otherwise.
*/
static bool updateButtonIconByTheme(QAbstractButton *button, const QString &name)
static bool updateIconByTheme(QAction *action, const QString &name)
{
QTC_ASSERT(!name.isEmpty(), return false);
if (QIcon::hasThemeIcon(name)) {
button->setIcon(QIcon::fromTheme(name));
action->setIcon(QIcon::fromTheme(name));
return true;
}
@@ -100,135 +102,127 @@ void ImageViewer::ctor()
setDuplicateSupported(true);
// toolbar
d->toolbar = new QWidget;
d->toolbar = new StyledBar;
d->toolButtonExportImage = new CommandButton;
d->toolButtonMultiExportImages = new CommandButton;
d->toolButtonCopyDataUrl = new CommandButton;
d->toolButtonBackground = new CommandButton;
d->toolButtonOutline = new CommandButton;
d->toolButtonFitToScreen = new CommandButton;
d->toolButtonOriginalSize = new CommandButton;
d->toolButtonZoomIn = new CommandButton;
d->toolButtonZoomOut = new CommandButton;
d->toolButtonPlayPause = new CommandButton;
d->actionExportImage = new CommandAction(Constants::ACTION_EXPORT_IMAGE, d->toolbar);
d->actionMultiExportImages = new CommandAction(Constants::ACTION_EXPORT_MULTI_IMAGES,
d->toolbar);
d->actionButtonCopyDataUrl = new CommandAction(Constants::ACTION_COPY_DATA_URL, d->toolbar);
d->shareButton = new QToolButton;
d->shareButton->setToolTip(Tr::tr("Export"));
d->shareButton->setPopupMode(QToolButton::InstantPopup);
d->shareButton->setIcon(Icons::EXPORTFILE_TOOLBAR.icon());
d->shareButton->setProperty("noArrow", true);
auto shareMenu = new QMenu(d->shareButton);
shareMenu->addAction(d->actionExportImage);
shareMenu->addAction(d->actionMultiExportImages);
shareMenu->addAction(d->actionButtonCopyDataUrl);
d->shareButton->setMenu(shareMenu);
d->toolButtonBackground->setCheckable(true);
d->toolButtonBackground->setChecked(settings.showBackground);
d->actionBackground = new CommandAction(Constants::ACTION_BACKGROUND, d->toolbar);
d->actionOutline = new CommandAction(Constants::ACTION_OUTLINE, d->toolbar);
d->actionFitToScreen = new CommandAction(Constants::ACTION_FIT_TO_SCREEN, d->toolbar);
d->actionOriginalSize = new CommandAction(Core::Constants::ZOOM_RESET, d->toolbar);
d->actionZoomIn = new CommandAction(Core::Constants::ZOOM_IN, d->toolbar);
d->actionZoomOut = new CommandAction(Core::Constants::ZOOM_OUT, d->toolbar);
d->actionPlayPause = new CommandAction(Constants::ACTION_TOGGLE_ANIMATION, d->toolbar);
d->toolButtonOutline->setCheckable(true);
d->toolButtonOutline->setChecked(settings.showOutline);
d->actionBackground->setCheckable(true);
d->actionBackground->setChecked(settings.showBackground);
d->toolButtonFitToScreen->setCheckable(true);
d->toolButtonFitToScreen->setChecked(settings.fitToScreen);
d->actionOutline->setCheckable(true);
d->actionOutline->setChecked(settings.showOutline);
d->toolButtonZoomIn->setAutoRepeat(true);
d->actionFitToScreen->setCheckable(true);
d->actionFitToScreen->setChecked(settings.fitToScreen);
d->toolButtonZoomOut->setAutoRepeat(true);
d->actionZoomIn->setAutoRepeat(true);
d->toolButtonExportImage->setToolTipBase(Tr::tr("Export as Image"));
d->toolButtonMultiExportImages->setToolTipBase(Tr::tr("Export Images of Multiple Sizes"));
d->toolButtonOutline->setToolTipBase(Tr::tr("Show Outline"));
d->toolButtonFitToScreen->setToolTipBase(Tr::tr("Fit to Screen"));
d->toolButtonOriginalSize->setToolTipBase(Tr::tr("Original Size"));
d->toolButtonZoomIn->setToolTipBase(Tr::tr("Zoom In"));
d->toolButtonZoomOut->setToolTipBase(Tr::tr("Zoom Out"));
d->actionZoomOut->setAutoRepeat(true);
d->toolButtonExportImage->setIcon(Icons::EXPORTFILE_TOOLBAR.icon());
d->toolButtonMultiExportImages->setIcon(Icons::MULTIEXPORTFILE_TOOLBAR.icon());
d->toolButtonCopyDataUrl->setIcon(Icons::COPY_TOOLBAR.icon());
const Icon backgroundIcon({{":/utils/images/desktopdevicesmall.png", Theme::IconsBaseColor}});
d->toolButtonBackground->setIcon(backgroundIcon.icon());
d->toolButtonOutline->setIcon(Icons::BOUNDING_RECT.icon());
d->toolButtonZoomIn->setIcon(
ActionManager::command(Core::Constants::ZOOM_IN)->action()->icon());
d->toolButtonZoomOut->setIcon(
ActionManager::command(Core::Constants::ZOOM_OUT)->action()->icon());
d->toolButtonOriginalSize->setIcon(
ActionManager::command(Core::Constants::ZOOM_RESET)->action()->icon());
d->toolButtonFitToScreen->setIcon(Icons::FITTOVIEW_TOOLBAR.icon());
d->actionBackground->setIcon(backgroundIcon.icon());
d->actionOutline->setIcon(Icons::BOUNDING_RECT.icon());
d->actionZoomIn->setIcon(ActionManager::command(Core::Constants::ZOOM_IN)->action()->icon());
d->actionZoomOut->setIcon(ActionManager::command(Core::Constants::ZOOM_OUT)->action()->icon());
d->actionOriginalSize->setIcon(
ActionManager::command(Core::Constants::ZOOM_RESET)->action()->icon());
d->actionFitToScreen->setIcon(Icons::FITTOVIEW_TOOLBAR.icon());
// icons update - try to use system theme
updateButtonIconByTheme(d->toolButtonFitToScreen, QLatin1String("zoom-fit-best"));
updateIconByTheme(d->actionFitToScreen, QLatin1String("zoom-fit-best"));
// a display - something is on the background
updateButtonIconByTheme(d->toolButtonBackground, QLatin1String("video-display"));
updateIconByTheme(d->actionBackground, QLatin1String("video-display"));
// "emblem to specify the directory where the user stores photographs"
// (photograph has outline - piece of paper)
updateButtonIconByTheme(d->toolButtonOutline, QLatin1String("emblem-photos"));
updateIconByTheme(d->actionOutline, QLatin1String("emblem-photos"));
auto setAsDefaultButton = new QToolButton;
auto setAsDefault = new QAction(Tr::tr("Set as Default"), setAsDefaultButton);
setAsDefault->setToolTip(Tr::tr("Use the current settings for background, outline, and fitting "
"to screen as the default for new image viewers."));
setAsDefaultButton->setDefaultAction(setAsDefault);
d->toolButtonExportImage->setCommandId(Constants::ACTION_EXPORT_IMAGE);
d->toolButtonMultiExportImages->setCommandId(Constants::ACTION_EXPORT_MULTI_IMAGES);
d->toolButtonCopyDataUrl->setCommandId(Constants::ACTION_COPY_DATA_URL);
d->toolButtonZoomIn->setCommandId(Core::Constants::ZOOM_IN);
d->toolButtonZoomOut->setCommandId(Core::Constants::ZOOM_OUT);
d->toolButtonOriginalSize->setCommandId(Core::Constants::ZOOM_RESET);
d->toolButtonFitToScreen->setCommandId(Constants::ACTION_FIT_TO_SCREEN);
d->toolButtonBackground->setCommandId(Constants::ACTION_BACKGROUND);
d->toolButtonOutline->setCommandId(Constants::ACTION_OUTLINE);
d->toolButtonPlayPause->setCommandId(Constants::ACTION_TOGGLE_ANIMATION);
auto setAsDefault = new QAction(Tr::tr("Set as Default"), d->toolbar);
const auto updateSetAsDefaultToolTip = [this, setAsDefault] {
const ImageView::Settings settings = d->imageView->settings();
const QString on = Tr::tr("on");
const QString off = Tr::tr("off");
setAsDefault->setToolTip(
"<p>"
+ Tr::tr("Use the current settings for background, outline, and fitting "
"to screen as the default for new image viewers. Current default:")
+ "</p><p><ul><li>" + Tr::tr("Background: %1").arg(settings.showBackground ? on : off)
+ "</li><li>" + Tr::tr("Outline: %1").arg(settings.showOutline ? on : off) + "</li><li>"
+ Tr::tr("Fit to Screen: %1").arg(settings.fitToScreen ? on : off) + "</li></ul>");
};
updateSetAsDefaultToolTip();
d->labelImageSize = new QLabel;
d->labelInfo = new QLabel;
auto bar = new QToolBar;
bar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
bar->addWidget(d->shareButton);
bar->addSeparator();
bar->addAction(d->actionOriginalSize);
bar->addAction(d->actionZoomIn);
bar->addAction(d->actionZoomOut);
bar->addAction(d->actionPlayPause);
bar->addAction(d->actionPlayPause);
bar->addSeparator();
bar->addAction(d->actionBackground);
bar->addAction(d->actionOutline);
bar->addAction(d->actionFitToScreen);
bar->addAction(setAsDefault);
auto horizontalLayout = new QHBoxLayout(d->toolbar);
horizontalLayout->setSpacing(0);
horizontalLayout->setContentsMargins(0, 0, 0, 0);
horizontalLayout->addWidget(d->toolButtonExportImage);
horizontalLayout->addWidget(d->toolButtonMultiExportImages);
horizontalLayout->addWidget(d->toolButtonCopyDataUrl);
horizontalLayout->addWidget(new StyledSeparator);
horizontalLayout->addWidget(d->toolButtonBackground);
horizontalLayout->addWidget(d->toolButtonOutline);
horizontalLayout->addWidget(d->toolButtonFitToScreen);
horizontalLayout->addWidget(setAsDefaultButton);
horizontalLayout->addWidget(new StyledSeparator);
horizontalLayout->addWidget(d->toolButtonOriginalSize);
horizontalLayout->addWidget(d->toolButtonZoomIn);
horizontalLayout->addWidget(d->toolButtonZoomOut);
horizontalLayout->addWidget(d->toolButtonPlayPause);
horizontalLayout->addWidget(d->toolButtonPlayPause);
horizontalLayout->addWidget(new StyledSeparator);
horizontalLayout->addItem(new QSpacerItem(315, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
horizontalLayout->addWidget(bar);
horizontalLayout->addItem(
new QSpacerItem(315, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
horizontalLayout->addWidget(new StyledSeparator);
horizontalLayout->addWidget(d->labelImageSize);
horizontalLayout->addWidget(new StyledSeparator);
horizontalLayout->addWidget(d->labelInfo);
// connections
connect(d->toolButtonExportImage, &QAbstractButton::clicked,
d->imageView, &ImageView::exportImage);
connect(d->toolButtonMultiExportImages, &QAbstractButton::clicked,
d->imageView, &ImageView::exportMultiImages);
connect(d->toolButtonCopyDataUrl, &QAbstractButton::clicked,
d->imageView, &ImageView::copyDataUrl);
connect(d->toolButtonZoomIn, &QAbstractButton::clicked,
d->imageView, &ImageView::zoomIn);
connect(d->toolButtonZoomOut, &QAbstractButton::clicked,
d->imageView, &ImageView::zoomOut);
connect(d->toolButtonFitToScreen,
&QAbstractButton::toggled,
connect(d->actionExportImage, &QAction::triggered, d->imageView, &ImageView::exportImage);
connect(d->actionMultiExportImages,
&QAction::triggered,
d->imageView,
&ImageView::setFitToScreen);
&ImageView::exportMultiImages);
connect(d->actionButtonCopyDataUrl, &QAction::triggered, d->imageView, &ImageView::copyDataUrl);
connect(d->actionZoomIn, &QAction::triggered, d->imageView, &ImageView::zoomIn);
connect(d->actionZoomOut, &QAction::triggered, d->imageView, &ImageView::zoomOut);
connect(d->actionFitToScreen, &QAction::triggered, d->imageView, &ImageView::setFitToScreen);
connect(d->imageView,
&ImageView::fitToScreenChanged,
d->toolButtonFitToScreen,
&QAbstractButton::setChecked);
connect(d->toolButtonOriginalSize,
&QAbstractButton::clicked,
d->actionFitToScreen,
&QAction::setChecked);
connect(d->actionOriginalSize,
&QAction::triggered,
d->imageView,
&ImageView::resetToOriginalSize);
connect(d->toolButtonBackground, &QAbstractButton::toggled,
d->imageView, &ImageView::setViewBackground);
connect(d->toolButtonOutline, &QAbstractButton::toggled,
d->imageView, &ImageView::setViewOutline);
connect(d->toolButtonPlayPause, &CommandButton::clicked,
this, &ImageViewer::playToggled);
connect(d->actionBackground, &QAction::toggled, d->imageView, &ImageView::setViewBackground);
connect(d->actionOutline, &QAction::toggled, d->imageView, &ImageView::setViewOutline);
connect(d->actionPlayPause, &QAction::triggered, this, &ImageViewer::playToggled);
connect(d->file.data(), &ImageViewerFile::imageSizeChanged,
this, &ImageViewer::imageSizeUpdated);
connect(d->file.data(), &ImageViewerFile::openFinished,
@@ -243,8 +237,9 @@ void ImageViewer::ctor()
this, &ImageViewer::updatePauseAction);
connect(d->imageView, &ImageView::scaleFactorChanged,
this, &ImageViewer::scaleFactorUpdate);
connect(setAsDefault, &QAction::triggered, d->imageView, [this] {
connect(setAsDefault, &QAction::triggered, d->imageView, [this, updateSetAsDefaultToolTip] {
d->imageView->writeSettings(ICore::settings());
updateSetAsDefaultToolTip();
});
}
@@ -280,18 +275,18 @@ IEditor *ImageViewer::duplicate()
void ImageViewer::exportImage()
{
if (d->file->type() == ImageViewerFile::TypeSvg)
d->toolButtonExportImage->click();
d->actionExportImage->trigger();
}
void ImageViewer::exportMultiImages()
{
if (d->file->type() == ImageViewerFile::TypeSvg)
d->toolButtonMultiExportImages->click();
d->actionMultiExportImages->trigger();
}
void ImageViewer::copyDataUrl()
{
d->toolButtonCopyDataUrl->click();
d->actionButtonCopyDataUrl->trigger();
}
void ImageViewer::imageSizeUpdated(const QSize &size)
@@ -310,45 +305,45 @@ void ImageViewer::scaleFactorUpdate(qreal factor)
void ImageViewer::switchViewBackground()
{
d->toolButtonBackground->click();
d->actionBackground->trigger();
}
void ImageViewer::switchViewOutline()
{
d->toolButtonOutline->click();
d->actionOutline->trigger();
}
void ImageViewer::zoomIn()
{
d->toolButtonZoomIn->click();
d->actionZoomIn->trigger();
}
void ImageViewer::zoomOut()
{
d->toolButtonZoomOut->click();
d->actionZoomOut->trigger();
}
void ImageViewer::resetToOriginalSize()
{
d->toolButtonOriginalSize->click();
d->actionOriginalSize->trigger();
}
void ImageViewer::fitToScreen()
{
d->toolButtonFitToScreen->click();
d->actionFitToScreen->trigger();
}
void ImageViewer::updateToolButtons()
{
const bool isSvg = d->file->type() == ImageViewerFile::TypeSvg;
d->toolButtonExportImage->setEnabled(isSvg);
d->toolButtonMultiExportImages->setEnabled(isSvg);
d->actionExportImage->setEnabled(isSvg);
d->actionMultiExportImages->setEnabled(isSvg);
updatePauseAction();
}
void ImageViewer::togglePlay()
{
d->toolButtonPlayPause->click();
d->actionPlayPause->trigger();
}
void ImageViewer::playToggled()
@@ -360,12 +355,12 @@ void ImageViewer::updatePauseAction()
{
bool isMovie = d->file->type() == ImageViewerFile::TypeMovie;
if (isMovie && !d->file->isPaused()) {
d->toolButtonPlayPause->setToolTipBase(Tr::tr("Pause Animation"));
d->toolButtonPlayPause->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon());
d->actionPlayPause->setToolTipBase(Tr::tr("Pause Animation"));
d->actionPlayPause->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon());
} else {
d->toolButtonPlayPause->setToolTipBase(Tr::tr("Play Animation"));
d->toolButtonPlayPause->setIcon(Icons::RUN_SMALL_TOOLBAR.icon());
d->toolButtonPlayPause->setEnabled(isMovie);
d->actionPlayPause->setToolTipBase(Tr::tr("Play Animation"));
d->actionPlayPause->setIcon(Icons::RUN_SMALL_TOOLBAR.icon());
d->actionPlayPause->setEnabled(isMovie);
}
}

View File

@@ -879,6 +879,7 @@ void Client::deactivateDocument(TextEditor::TextDocument *document)
TextEditor::TextEditorWidget *widget = textEditor->editorWidget();
widget->removeHoverHandler(&d->m_hoverHandler);
widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, {});
updateEditorToolBar(editor);
}
}
}

View File

@@ -35,6 +35,7 @@ namespace LanguageClient {
static Q_LOGGING_CATEGORY(Log, "qtc.languageclient.manager", QtWarningMsg)
static LanguageClientManager *managerInstance = nullptr;
static bool g_shuttingDown = false;
LanguageClientManager::LanguageClientManager(QObject *parent)
: QObject (parent)
@@ -112,7 +113,7 @@ void LanguageClientManager::clientStarted(Client *client)
qCDebug(Log) << "client started: " << client->name() << client;
QTC_ASSERT(managerInstance, return);
QTC_ASSERT(client, return);
if (managerInstance->m_shuttingDown) {
if (g_shuttingDown) {
clientFinished(client);
return;
}
@@ -131,7 +132,7 @@ void LanguageClientManager::clientFinished(Client *client)
&& client->state() != Client::ShutdownRequested;
if (unexpectedFinish) {
if (!managerInstance->m_shuttingDown) {
if (!g_shuttingDown) {
const QList<TextEditor::TextDocument *> &clientDocs
= managerInstance->m_clientForDocument.keys(client);
if (client->reset()) {
@@ -153,7 +154,7 @@ void LanguageClientManager::clientFinished(Client *client)
}
}
deleteClient(client);
if (managerInstance->m_shuttingDown && managerInstance->m_clients.isEmpty())
if (g_shuttingDown && managerInstance->m_clients.isEmpty())
emit managerInstance->shutdownFinished();
}
@@ -198,10 +199,10 @@ void LanguageClientManager::shutdownClient(Client *client)
if (!client)
return;
qCDebug(Log) << "request client shutdown: " << client->name() << client;
// reset the documents for that client already when requesting the shutdown so they can get
// reassigned to another server right after this request to another server
// reset and deactivate the documents for that client by assigning a null client already when
// requesting the shutdown so they can get reassigned to another server right after this request
for (TextEditor::TextDocument *document : managerInstance->m_clientForDocument.keys(client))
managerInstance->m_clientForDocument.remove(document);
openDocumentWithClient(document, nullptr);
if (client->reachable())
client->shutdown();
else if (client->state() != Client::Shutdown && client->state() != Client::ShutdownRequested)
@@ -218,17 +219,17 @@ void LanguageClientManager::deleteClient(Client *client)
for (QList<Client *> &clients : managerInstance->m_clientsForSetting)
clients.removeAll(client);
client->deleteLater();
if (!managerInstance->m_shuttingDown)
if (!g_shuttingDown)
emit instance()->clientRemoved(client);
}
void LanguageClientManager::shutdown()
{
QTC_ASSERT(managerInstance, return);
if (managerInstance->m_shuttingDown)
if (g_shuttingDown)
return;
qCDebug(Log) << "shutdown manager";
managerInstance->m_shuttingDown = true;
g_shuttingDown = true;
const auto clients = managerInstance->clients();
for (Client *client : clients)
shutdownClient(client);
@@ -242,7 +243,7 @@ void LanguageClientManager::shutdown()
bool LanguageClientManager::isShuttingDown()
{
return managerInstance->m_shuttingDown;
return g_shuttingDown;
}
LanguageClientManager *LanguageClientManager::instance()
@@ -408,6 +409,7 @@ void LanguageClientManager::openDocumentWithClient(TextEditor::TextDocument *doc
Client *currentClient = clientForDocument(document);
if (client == currentClient)
return;
managerInstance->m_clientForDocument.remove(document);
if (currentClient)
currentClient->deactivateDocument(document);
managerInstance->m_clientForDocument[document] = client;

View File

@@ -100,7 +100,6 @@ private:
QList<Client *> reachableClients();
bool m_shuttingDown = false;
QList<Client *> m_clients;
QList<BaseSettings *> m_currentSettings; // owned
QMap<QString, QList<Client *>> m_clientsForSetting;

View File

@@ -38,15 +38,13 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler,
const QStringList &versions,
const QString &downloadUrl,
const McuPackageVersionDetector *versionDetector,
const bool addToSystemPath,
const FilePath &relativePathModifier)
const bool addToSystemPath)
: settingsHandler(settingsHandler)
, m_label(label)
, m_defaultPath(settingsHandler->getPath(settingsKey, QSettings::SystemScope, defaultPath))
, m_detectionPath(detectionPath)
, m_settingsKey(settingsKey)
, m_versionDetector(versionDetector)
, m_relativePathModifier(relativePathModifier)
, m_versions(versions)
, m_cmakeVariableName(cmakeVarName)
, m_environmentVariableName(envVarName)
@@ -101,7 +99,7 @@ FilePath McuPackage::basePath() const
FilePath McuPackage::path() const
{
return (basePath() / m_relativePathModifier.path()).cleanPath();
return basePath().cleanPath();
}
FilePath McuPackage::defaultPath() const
@@ -230,7 +228,7 @@ bool McuPackage::writeToSettings() const
QWidget *McuPackage::widget()
{
auto *widget = new QWidget;
m_fileChooser = new PathChooser;
m_fileChooser = new PathChooser(widget);
m_fileChooser->lineEdit()->setButtonIcon(FancyLineEdit::Right, Icons::RESET.icon());
m_fileChooser->lineEdit()->setButtonVisible(FancyLineEdit::Right, true);
connect(m_fileChooser->lineEdit(), &FancyLineEdit::rightButtonClicked, this, [&] {
@@ -239,10 +237,10 @@ QWidget *McuPackage::widget()
auto layout = new QGridLayout(widget);
layout->setContentsMargins(0, 0, 0, 0);
m_infoLabel = new InfoLabel();
m_infoLabel = new InfoLabel(widget);
if (!m_downloadUrl.isEmpty()) {
auto downLoadButton = new QToolButton;
auto downLoadButton = new QToolButton(widget);
downLoadButton->setIcon(Icons::ONLINE.icon());
downLoadButton->setToolTip(tr("Download from \"%1\"").arg(m_downloadUrl));
QObject::connect(downLoadButton, &QToolButton::pressed, this, [this] {

View File

@@ -40,8 +40,7 @@ public:
const QStringList &versions = {},
const QString &downloadUrl = {},
const McuPackageVersionDetector *versionDetector = nullptr,
const bool addToPath = false,
const Utils::FilePath &relativePathModifier = Utils::FilePath());
const bool addToPath = false);
~McuPackage() override = default;
@@ -87,7 +86,6 @@ private:
QScopedPointer<const McuPackageVersionDetector> m_versionDetector;
Utils::FilePath m_path;
Utils::FilePath m_relativePathModifier; // relative path to m_path to be returned by path()
QString m_detectedVersion;
QStringList m_versions;
const QString m_cmakeVariableName;

View File

@@ -86,7 +86,7 @@ McuSupportOptionsWidget::McuSupportOptionsWidget(McuSupportOptions &options,
}
{
m_qtForMCUsSdkGroupBox = new QGroupBox(m_options.qtForMCUsSdkPackage->label());
m_qtForMCUsSdkGroupBox = new QGroupBox(tr("Qt for MCUs SDK"));
m_qtForMCUsSdkGroupBox->setFlat(true);
auto *layout = new QVBoxLayout(m_qtForMCUsSdkGroupBox);
layout->addWidget(m_options.qtForMCUsSdkPackage->widget());
@@ -251,15 +251,13 @@ void McuSupportOptionsWidget::showMcuTargetPackages()
return;
while (m_packagesLayout->rowCount() > 0) {
QFormLayout::TakeRowResult row = m_packagesLayout->takeRow(0);
row.labelItem->widget()->hide();
row.fieldItem->widget()->hide();
m_packagesLayout->removeRow(0);
}
for (const auto &package : std::as_const(m_options.sdkRepository.packages)) {
QWidget *packageWidget = package->widget();
if (!mcuTarget->packages().contains(package) || package->label().isEmpty())
for (const auto &package : mcuTarget->packages()) {
if (package->label().isEmpty())
continue;
QWidget *packageWidget = package->widget();
m_packagesLayout->addRow(package->label(), packageWidget);
packageWidget->show();
}

View File

@@ -361,15 +361,12 @@ McuPackagePtr createStm32CubeProgrammerPackage(const SettingsHandler::Ptr &setti
{
FilePath defaultPath;
const QString cubePath = "STMicroelectronics/STM32Cube/STM32CubeProgrammer";
if (HostOsInfo::isWindowsHost()) {
const FilePath programPath = findInProgramFiles(cubePath);
if (!programPath.isEmpty())
defaultPath = programPath;
} else {
const FilePath programPath = FileUtils::homePath() / cubePath;
if (programPath.exists())
defaultPath = programPath;
}
if (HostOsInfo::isWindowsHost())
defaultPath = findInProgramFiles(cubePath) / "bin";
else
defaultPath = FileUtils::homePath() / cubePath / "bin";
if (!defaultPath.exists())
FilePath defaultPath = {};
const FilePath detectionPath = FilePath::fromUserInput(
QLatin1String(Utils::HostOsInfo::isWindowsHost() ? "/bin/STM32_Programmer_CLI.exe"
@@ -386,8 +383,7 @@ McuPackagePtr createStm32CubeProgrammerPackage(const SettingsHandler::Ptr &setti
{}, // versions
"https://www.st.com/en/development-tools/stm32cubeprog.html", // download url
nullptr, // version detector
true, // add to path
"/bin" // relative path modifier
true // add to path
)};
}
@@ -542,7 +538,7 @@ static McuAbstractTargetFactory::Ptr createFactory(bool isLegacy,
{"arm-greenhills",
McuPackagePtr{new McuPackage{settingsHandler,
{},
toolchainFilePrefix / "arm-ghs.cmake",
toolchainFilePrefix / "ghs-arm.cmake",
{},
{},
Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,

View File

@@ -18,6 +18,7 @@ constexpr auto armgcc_stm32f469i_discovery_baremetal_json = R"(
"id": "STM32CubeProgrammer_PATH",
"label": "STM32CubeProgrammer",
"type": "path",
"setting": "Stm32CubeProgrammer",
"defaultValue": {
"windows": "%{Env:PROGRAMSANDFILES}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/",
"unix": "%{Env:HOME}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/"

View File

@@ -17,14 +17,15 @@ constexpr auto ghs_rh850_d1m1a_baremetal_json = R"(
{
"id": "FlashProgrammer_path",
"setting": "FlashProgrammerPath",
"label": "Path to Renesas Flash Programmer",
"label": "Renesas Flash Programmer",
"type": "path",
"setting": "RenesasFlashProgrammer",
"cmakeVar": "RENESAS_FLASH_PROGRAMMER_PATH",
"defaultValue": {
"windows": "%{Env:PROGRAMSANDFILES}/Renesas Electronics/Programming Tools/Renesas Flash Programmer V3.09",
"unix": "%{Env:HOME}"
},
"envVar": "RenesasFlashProgrammer_PATH",
"envVar": "RENESAS_FLASH_PROGRAMMER_PATH",
"optional": true,
"addToSystemPath": true
}

View File

@@ -18,6 +18,7 @@ constexpr auto iar_stm32f469i_discovery_baremetal_json = R"(
"id": "STM32CubeProgrammer_PATH",
"label": "STM32CubeProgrammer",
"type": "path",
"setting": "Stm32CubeProgrammer",
"defaultValue": {
"windows": "%{Env:PROGRAMSANDFILES}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/",
"unix": "%{Env:HOME}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/"

View File

@@ -3,6 +3,7 @@
#include "unittest.h"
#include "armgcc_ek_ra6m3g_baremetal_json.h"
#include "armgcc_mimxrt1050_evk_freertos_json.h"
#include "armgcc_mimxrt1064_evk_freertos_json.h"
#include "armgcc_mimxrt1170_evk_freertos_json.h"
@@ -11,6 +12,7 @@
#include "errors_json.h"
#include "gcc_desktop_json.h"
#include "ghs_rh850_d1m1a_baremetal_json.h"
#include "ghs_tviic2d6m_baremetal_json.h"
#include "iar_mimxrt1064_evk_freertos_json.h"
#include "iar_stm32f469i_discovery_baremetal_json.h"
#include "msvc_desktop_json.h"
@@ -123,6 +125,43 @@ const char vendor[]{"target_vendor"};
const QString settingsPrefix = QLatin1String(Constants::SETTINGS_GROUP) + '/'
+ QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX);
const char defaultToolPath[]{"/opt/biz/foo"};
const char xpressoIdePath[]{"/usr/local/mcuxpressoide"};
const char xpressoIdeLabel[]{"MCUXpresso IDE"};
const char xpressoIdeSetting[]{"MCUXpressoIDE"};
const char xpressoIdeCmakeVar[]{"MCUXPRESSO_IDE_PATH"};
const char xpressoIdeEnvVar[]{"MCUXpressoIDE_PATH"};
const char xpressoIdeDetectionPath[]{"ide/binaries/crt_emu_cm_redlink"};
const char stmCubeProgrammerSetting[]{"Stm32CubeProgrammer"};
const char stmCubeProgrammerLabel[]{"STM32CubeProgrammer"};
const QString stmCubeProgrammerPath{QString{defaultToolPath} + "/bin"};
const QString stmCubeProgrammerDetectionPath{"/bin/STM32_Programmer.sh"};
const char renesasProgrammerSetting[]{"RenesasFlashProgrammer"};
const char renesasProgrammerCmakeVar[]{"RENESAS_FLASH_PROGRAMMER_PATH"};
const QString renesasProgrammerEnvVar{renesasProgrammerCmakeVar};
const char renesasProgrammerLabel[]{"Renesas Flash Programmer"};
const char renesasProgrammerDetectionPath[]{"rfp-cli"};
const char renesasE2StudioCmakeVar[]{"EK_RA6M3G_E2_PROJECT_PATH"};
const char renesasE2StudioDefaultPath[]{"%{Env:HOME}/e2_studio/workspace"};
const QString renesasE2StudioPath{(FileUtils::homePath() / "/e2_studio/workspace").toUserOutput()};
const char renesasE2StudioLabel[]{"Path to project for Renesas e2 Studio"};
const char renesasE2StudioSetting[]{"RenesasE2StudioPath"};
const char cypressProgrammerSetting[]{"CypressAutoFlashUtil"};
const char cypressProgrammerCmakeVar[]{"INFINEON_AUTO_FLASH_UTILITY_DIR"};
const char cypressProgrammerEnvVar[]{"CYPRESS_AUTO_FLASH_UTILITY_DIR"};
const char cypressProgrammerLabel[]{"Cypress Auto Flash Utility"};
const char cypressProgrammerDetectionPath[]{"/bin/openocd"};
const char jlinkPath[]{"/opt/SEGGER/JLink"};
const char jlinkSetting[]{"JLinkPath"};
const char jlinkCmakeVar[]{"JLINK_PATH"};
const char jlinkEnvVar[]{"JLINK_PATH"};
const char jlinkLabel[]{"Path to SEGGER J-Link"};
const QString unsupportedToolchainFilePath = QString{qtForMcuSdkPath}
+ "/lib/cmake/Qul/toolchain/unsupported.cmake";
@@ -282,6 +321,7 @@ void verifyPackage(const McuPackagePtr &package,
const QString &cmakeVar,
const QString &envVar,
const QString &label,
const QString &detectionPath,
const QStringList &versions)
{
QVERIFY(package);
@@ -290,6 +330,7 @@ void verifyPackage(const McuPackagePtr &package,
QCOMPARE(package->cmakeVariableName(), cmakeVar);
QCOMPARE(package->environmentVariableName(), envVar);
QCOMPARE(package->label(), label);
QCOMPARE(package->detectionPath().toString(), detectionPath);
QCOMPARE(package->settingsKey(), setting);
QCOMPARE(package->versions(), versions);
}
@@ -928,11 +969,12 @@ void McuSupportTest::test_createTargetWithToolchainPackages()
verifyPackage(qtForMCUsSDK,
qtForMcuSdkPath,
{}, // qtForMcuSdkPath
{},
Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK,
QUL_CMAKE_VAR,
QUL_ENV_VAR,
QUL_LABEL,
{},
{});
verifyTargetToolchains(targets,
@@ -1372,76 +1414,164 @@ void McuSupportTest::test_resolveCmakeVariablesInDefaultPath()
void McuSupportTest::test_legacy_createThirdPartyPackage_data()
{
const QString defaultToolPath{"/opt/biz/foo"};
const char xpressoIdeSetting[]{"MCUXpressoIDE"};
const char xpressoIdeCmakeVar[]{"MCUXPRESSO_IDE_PATH"};
const char xpressoIdeEnvVar[]{"MCUXpressoIDE_PATH"};
const char stmCubeProgrammerSetting[]{"Stm32CubeProgrammer"};
const QString stmCubeProgrammerPath{defaultToolPath + "/bin"};
const char renesasProgrammerSetting[]{"RenesasFlashProgrammer"};
const char renesasProgrammerCmakeVar[]{"RENESAS_FLASH_PROGRAMMER_PATH"};
const QString renesasProgrammerEnvVar{renesasProgrammerCmakeVar};
const char cypressProgrammerSetting[]{"CypressAutoFlashUtil"};
const char cypressProgrammerCmakeVar[]{"INFINEON_AUTO_FLASH_UTILITY_DIR"};
const char cypressProgrammerEnvVar[]{"CYPRESS_AUTO_FLASH_UTILITY_DIR"};
QTest::addColumn<PackageCreator>("creator");
QTest::addColumn<QString>("json");
QTest::addColumn<QString>("path");
QTest::addColumn<QString>("defaultPath");
QTest::addColumn<QString>("setting");
QTest::addColumn<QString>("cmakeVar");
QTest::addColumn<QString>("envVar");
QTest::addColumn<QString>("label");
QTest::addColumn<QString>("detectionPath");
QTest::newRow("mcuXpresso") << PackageCreator{[this]() {
return Legacy::createMcuXpressoIdePackage(settingsMockPtr);
}} << defaultToolPath << defaultToolPath
<< xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar;
QTest::newRow("stmCubeProgrammer") << PackageCreator{[this]() {
return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr);
}} << stmCubeProgrammerPath << defaultToolPath
<< stmCubeProgrammerSetting << empty << empty;
QTest::newRow("armgcc_mimxrt1050_evk_freertos_json mcuXpresso")
<< PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }}
<< armgcc_mimxrt1050_evk_freertos_json << xpressoIdePath << xpressoIdePath
<< xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel
<< xpressoIdeDetectionPath;
QTest::newRow("renesasProgrammer") << PackageCreator{[this]() {
return Legacy::createRenesasProgrammerPackage(settingsMockPtr);
}} << defaultToolPath << defaultToolPath
<< renesasProgrammerSetting << renesasProgrammerCmakeVar
<< renesasProgrammerEnvVar;
QTest::newRow("armgcc_mimxrt1064_evk_freertos_json mcuXpresso")
<< PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }}
<< armgcc_mimxrt1064_evk_freertos_json << xpressoIdePath << xpressoIdePath
<< xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel
<< xpressoIdeDetectionPath;
QTest::newRow("cypressProgrammer") << PackageCreator{[this]() {
return Legacy::createCypressProgrammerPackage(settingsMockPtr);
}} << defaultToolPath << defaultToolPath
<< cypressProgrammerSetting << cypressProgrammerCmakeVar
<< cypressProgrammerEnvVar;
QTest::newRow("armgcc_mimxrt1170_evk_freertos_json mcuXpresso")
<< PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }}
<< armgcc_mimxrt1170_evk_freertos_json << xpressoIdePath << xpressoIdePath
<< xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel
<< xpressoIdeDetectionPath;
QTest::newRow("armgcc_stm32h750b_discovery_baremetal_json stmCubeProgrammer")
<< PackageCreator{[this]() {
return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr);
}}
<< armgcc_stm32h750b_discovery_baremetal_json << stmCubeProgrammerPath
<< stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty
<< stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath;
QTest::newRow("armgcc_stm32f769i_discovery_freertos_json stmCubeProgrammer")
<< PackageCreator{[this]() {
return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr);
}}
<< armgcc_stm32f769i_discovery_freertos_json << stmCubeProgrammerPath
<< stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty
<< stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath;
QTest::newRow("ghs_rh850_d1m1a_baremetal_json renesasProgrammer")
<< PackageCreator{[this]() {
return Legacy::createRenesasProgrammerPackage(settingsMockPtr);
}}
<< ghs_rh850_d1m1a_baremetal_json << defaultToolPath << defaultToolPath
<< renesasProgrammerSetting << renesasProgrammerCmakeVar << renesasProgrammerEnvVar
<< renesasProgrammerLabel << renesasProgrammerDetectionPath;
}
void McuSupportTest::test_legacy_createThirdPartyPackage()
{
QFETCH(PackageCreator, creator);
QFETCH(QString, json);
QFETCH(QString, path);
QFETCH(QString, defaultPath);
QFETCH(QString, setting);
QFETCH(QString, cmakeVar);
QFETCH(QString, envVar);
if (!envVar.isEmpty())
QVERIFY(qputenv(envVar.toLocal8Bit(), defaultPath.toLocal8Bit()));
QFETCH(QString, label);
QFETCH(QString, detectionPath);
EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, _, _))
.Times(2)
.WillRepeatedly(Return(FilePath::fromUserInput(defaultPath)));
McuPackagePtr thirdPartyPacakge{creator()};
QVERIFY(thirdPartyPacakge);
QCOMPARE(thirdPartyPacakge->settingsKey(), setting);
QCOMPARE(thirdPartyPacakge->environmentVariableName(), envVar);
QCOMPARE(thirdPartyPacakge->path().toString(), path);
McuPackagePtr thirdPartyPackage{creator()};
verifyPackage(thirdPartyPackage,
path,
defaultPath,
setting,
cmakeVar,
envVar,
label,
detectionPath,
{});
}
if (!envVar.isEmpty())
QVERIFY(qunsetenv(envVar.toLocal8Bit()));
void McuSupportTest::test_createThirdPartyPackage_data()
{
test_legacy_createThirdPartyPackage_data();
}
void McuSupportTest::test_createThirdPartyPackage()
{
QFETCH(QString, json);
QFETCH(QString, path);
QFETCH(QString, defaultPath);
QFETCH(QString, setting);
QFETCH(QString, cmakeVar);
QFETCH(QString, envVar);
QFETCH(QString, label);
McuTargetDescription targetDescription{parseDescriptionJson(json.toLocal8Bit())};
EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, QSettings::SystemScope, _))
.Times(testing::AtMost(1))
.WillOnce(Return(FilePath::fromUserInput(defaultPath)));
EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, QSettings::UserScope, _))
.Times(testing::AtMost(1))
.WillOnce(Return(FilePath::fromUserInput(path)));
auto [targets, packages] = targetFactory.createTargets(targetDescription, sdkPackagePtr);
auto thirdPartyPackage = findOrDefault(packages, [&setting](const McuPackagePtr &pkg) {
return (pkg->settingsKey() == setting);
});
verifyPackage(thirdPartyPackage, path, defaultPath, setting, cmakeVar, envVar, label, {}, {});
}
void McuSupportTest::test_legacy_createCypressProgrammer3rdPartyPackage()
{
EXPECT_CALL(*settingsMockPtr, getPath(QString{cypressProgrammerSetting}, _, _))
.Times(2)
.WillRepeatedly(Return(FilePath::fromUserInput(defaultToolPath)));
McuPackagePtr thirdPartyPackage{Legacy::createCypressProgrammerPackage(settingsMockPtr)};
verifyPackage(thirdPartyPackage,
defaultToolPath,
defaultToolPath,
cypressProgrammerSetting,
cypressProgrammerCmakeVar,
cypressProgrammerEnvVar,
cypressProgrammerLabel,
cypressProgrammerDetectionPath,
{});
}
void McuSupportTest::test_createJLink3rdPartyPackage()
{
McuTargetDescription targetDescription{parseDescriptionJson(armgcc_ek_ra6m3g_baremetal_json)};
EXPECT_CALL(*settingsMockPtr, getPath(QString{jlinkSetting}, QSettings::SystemScope, _))
.Times(testing::AtMost(1))
.WillOnce(Return(FilePath::fromUserInput(jlinkPath)));
EXPECT_CALL(*settingsMockPtr, getPath(QString{jlinkSetting}, QSettings::UserScope, _))
.Times(testing::AtMost(1))
.WillOnce(Return(FilePath::fromUserInput(jlinkPath)));
auto [targets, packages] = targetFactory.createTargets(targetDescription, sdkPackagePtr);
auto thirdPartyPackage = findOrDefault(packages, [](const McuPackagePtr &pkg) {
return (pkg->settingsKey() == jlinkSetting);
});
verifyPackage(thirdPartyPackage,
jlinkPath,
jlinkPath,
jlinkSetting,
jlinkCmakeVar,
jlinkEnvVar,
jlinkLabel,
{},
{});
}
void McuSupportTest::test_defaultValueForEachOperationSystem()

View File

@@ -94,6 +94,10 @@ private slots:
void test_legacy_createThirdPartyPackage_data();
void test_legacy_createThirdPartyPackage();
void test_createThirdPartyPackage_data();
void test_createThirdPartyPackage();
void test_legacy_createCypressProgrammer3rdPartyPackage();
void test_createJLink3rdPartyPackage();
void test_defaultValueForEachOperationSystem();
void test_addToSystemPathFlag();

View File

@@ -14,7 +14,8 @@ class MesonActionsManager : public QObject
{
Q_OBJECT
Utils::ParameterAction buildTargetContextAction{
Tr::tr("Build"), Tr::tr("Build \"%1\""),
::MesonProjectManager::Tr::tr("Build"),
::MesonProjectManager::Tr::tr("Build \"%1\""),
Utils::ParameterAction::AlwaysEnabled /*handled manually*/
};
QAction configureActionMenu;

View File

@@ -83,7 +83,6 @@ void ModelEditorPlugin::extensionsInitialized()
ExtensionSystem::IPlugin::ShutdownFlag ModelEditorPlugin::aboutToShutdown()
{
d->settingsController.save(Core::ICore::settings());
QApplication::clipboard()->clear();
return SynchronousShutdown;
}

View File

@@ -79,7 +79,7 @@ private:
void finalize();
Utils::Perspective m_perspective{Constants::PerfProfilerPerspectiveId,
Tr::tr("Performance Analyzer")};
::PerfProfiler::Tr::tr("Performance Analyzer")};
QAction *m_startAction = nullptr;
QAction *m_stopAction = nullptr;

View File

@@ -131,13 +131,14 @@ ToolChain::BuiltInHeaderPathsRunner CustomToolChain::createBuiltInHeaderPathsRun
void CustomToolChain::addToEnvironment(Environment &env) const
{
if (!m_compilerCommand.isEmpty()) {
const FilePath path = m_compilerCommand.parentDir();
env.prependOrSetPath(path);
const FilePath makePath = m_makeCommand.parentDir();
if (makePath != path)
env.prependOrSetPath(makePath);
}
const FilePath compiler = compilerCommand();
if (compiler.isEmpty())
return;
const FilePath path = compiler.parentDir();
env.prependOrSetPath(path);
const FilePath makePath = m_makeCommand.parentDir();
if (makePath != path)
env.prependOrSetPath(makePath);
}
QStringList CustomToolChain::suggestedMkspecList() const
@@ -252,8 +253,7 @@ bool CustomToolChain::operator ==(const ToolChain &other) const
return false;
auto customTc = static_cast<const CustomToolChain *>(&other);
return m_compilerCommand == customTc->m_compilerCommand
&& m_makeCommand == customTc->m_makeCommand
return m_makeCommand == customTc->m_makeCommand
&& targetAbi() == customTc->targetAbi()
&& m_predefinedMacros == customTc->m_predefinedMacros
&& m_builtInHeaderPaths == customTc->m_builtInHeaderPaths;

View File

@@ -83,7 +83,6 @@ private:
CustomParserSettings customParserSettings() const;
Utils::FilePath m_compilerCommand;
Utils::FilePath m_makeCommand;
Macros m_predefinedMacros;

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