Merge remote-tracking branch 'origin/12.0'

Conflicts:
	src/plugins/python/pyside.cpp

Change-Id: I1f84ed56d38355cef6076797c72693fff4c1aa78
This commit is contained in:
Eike Ziller
2023-09-28 11:02:38 +02:00
45 changed files with 1741 additions and 747 deletions

View File

@@ -9,7 +9,7 @@ on:
env: env:
QT_VERSION: 6.5.2 QT_VERSION: 6.5.2
MACOS_DEPLOYMENT_TARGET: 10.15 MACOS_DEPLOYMENT_TARGET: 10.15
CLANG_VERSION: 17.0.0-rc4 CLANG_VERSION: 17.0.1
ELFUTILS_VERSION: 0.175 ELFUTILS_VERSION: 0.175
CMAKE_VERSION: 3.21.1 CMAKE_VERSION: 3.21.1
NINJA_VERSION: 1.10.2 NINJA_VERSION: 1.10.2

View File

@@ -7,7 +7,7 @@ instructions:
variableValue: "RelWithDebInfo" variableValue: "RelWithDebInfo"
- type: EnvironmentVariable - type: EnvironmentVariable
variableName: LLVM_BASE_URL variableName: LLVM_BASE_URL
variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_17.0.0-rc4-based variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_17.0.1-based
- type: EnvironmentVariable - type: EnvironmentVariable
variableName: QTC_QT_BASE_URL variableName: QTC_QT_BASE_URL
variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.2-released/Qt" variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.2-released/Qt"

View File

@@ -2068,20 +2068,23 @@ class Tester(Dumper):
lldb.SBDebugger.Destroy(self.debugger) lldb.SBDebugger.Destroy(self.debugger)
if 'QT_CREATOR_LLDB_PROCESS' in os.environ:
# Initialize Qt Creator dumper
try:
theDumper = Dumper()
except Exception as error:
print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error))
# ------------------------------ For use in LLDB ------------------------------ # ------------------------------ For use in LLDB ------------------------------
debug = print if 'QT_LLDB_SUMMARY_PROVIDER_DEBUG' in os.environ \
else lambda *a, **k: None
from pprint import pprint debug(f"Loading lldbbridge.py from {__file__}")
__module__ = sys.modules[__name__]
DEBUG = False if not hasattr(__module__, 'DEBUG') else DEBUG
class LogMixin(): class LogMixin():
@staticmethod @staticmethod
def log(message='', log_caller=False, frame=1, args=''): def log(message='', log_caller=False, frame=1, args=''):
if not DEBUG:
return
if log_caller: if log_caller:
message = ": " + message if len(message) else '' message = ": " + message if len(message) else ''
# FIXME: Compute based on first frame not in this class? # FIXME: Compute based on first frame not in this class?
@@ -2090,7 +2093,7 @@ class LogMixin():
localz = frame.f_locals localz = frame.f_locals
instance = str(localz["self"]) + "." if 'self' in localz else '' instance = str(localz["self"]) + "." if 'self' in localz else ''
message = "%s%s(%s)%s" % (instance, fn, args, message) message = "%s%s(%s)%s" % (instance, fn, args, message)
print(message) debug(message)
@staticmethod @staticmethod
def log_fn(arg_str=''): def log_fn(arg_str=''):
@@ -2437,6 +2440,11 @@ class SyntheticChildrenProvider(SummaryProvider):
def __lldb_init_module(debugger, internal_dict): def __lldb_init_module(debugger, internal_dict):
# Module is being imported in an LLDB session # Module is being imported in an LLDB session
if 'QT_CREATOR_LLDB_PROCESS' in os.environ:
# Let Qt Creator take care of its own dumper
return
debug("Initializing module with", debugger)
if not __name__ == 'qt': if not __name__ == 'qt':
# Make available under global 'qt' name for consistency, # Make available under global 'qt' name for consistency,
@@ -2471,10 +2479,3 @@ def __lldb_init_module(debugger, internal_dict):
% ("qt.SyntheticChildrenProvider", type_category)) % ("qt.SyntheticChildrenProvider", type_category))
debugger.HandleCommand('type category enable %s' % type_category) debugger.HandleCommand('type category enable %s' % type_category)
if __name__ == "lldbbridge":
try:
theDumper = Dumper()
except Exception as error:
print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error))

View File

@@ -1,12 +1,12 @@
{ {
"version": 1, "version": 1,
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ], "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
"id": "R.AutoTest", "id": "M.BoostAutoTest",
"category": "H.Project", "category": "I.TestProject",
"trDescription": "Creates a new unit test project. Unit tests allow you to verify that the code is fit for use and that there are no regressions.", "trDescription": "Creates a new unit test project using Boost. Unit tests allow you to verify that the code is fit for use and that there are no regressions.",
"trDisplayName": "Auto Test Project", "trDisplayName": "Boost Test Project",
"trDisplayCategory": "Other Project", "trDisplayCategory": "Test Project",
"icon": "autotest.png", "icon": "../autotest.png",
"iconKind": "Themed", "iconKind": "Themed",
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ], "featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
@@ -33,10 +33,6 @@
{ "key": "MainCppName", { "key": "MainCppName",
"value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }" "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }"
}, },
{
"key": "TestCaseFileGTestWithCppSuffix",
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
},
{ {
"key": "GUARD", "key": "GUARD",
"value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }" "value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }"
@@ -44,10 +40,6 @@
{ {
"key": "TestCaseFileWithCppSuffix", "key": "TestCaseFileWithCppSuffix",
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }" "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
},
{
"key": "TestCaseFileWithQmlSuffix",
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.qml' }"
} }
], ],
@@ -59,7 +51,7 @@
"typeId": "Project", "typeId": "Project",
"data": "data":
{ {
"trDescription": "This wizard creates a simple unit test project." "trDescription": "This wizard creates a simple unit test project using Boost."
} }
}, },
{ {
@@ -77,18 +69,6 @@
"index": 0, "index": 0,
"items": "items":
[ [
{
"trKey": "Qt Test",
"value": "QtTest"
},
{
"trKey": "Google Test",
"value": "GTest"
},
{
"trKey": "Qt Quick Test",
"value": "QtQuickTest"
},
{ {
"trKey": "Boost Test (header only)", "trKey": "Boost Test (header only)",
"value": "BoostTest" "value": "BoostTest"
@@ -96,28 +76,13 @@
{ {
"trKey": "Boost Test (shared libraries)", "trKey": "Boost Test (shared libraries)",
"value": "BoostTest_dyn" "value": "BoostTest_dyn"
},
{
"trKey": "Catch2",
"value": "Catch2"
} }
] ]
} }
}, },
{
"name": "RequireGUI",
"trDisplayName": "GUI Application",
"visible": "%{JS: value('TestFrameWork') === 'QtTest'}",
"type": "CheckBox",
"data": {
"checked": false
}
},
{ {
"name": "TestSuiteName", "name": "TestSuiteName",
"trDisplayName": "Test suite name:", "trDisplayName": "Test suite name:",
"visible": "%{JS: ['BoostTest', 'BoostTest_dyn', 'GTest'].indexOf(value('TestFrameWork')) >= 0}",
"mandatory": true, "mandatory": true,
"type": "LineEdit", "type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" } "data": { "validator": "^[a-zA-Z_0-9]+$" }
@@ -129,43 +94,6 @@
"type": "LineEdit", "type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" } "data": { "validator": "^[a-zA-Z_0-9]+$" }
}, },
{
"name": "RequireApplication",
"trDisplayName": "Requires QApplication",
"visible": "%{JS: value('TestFrameWork') === 'QtTest'}",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "UseSetupCode",
"trDisplayName": "Generate setup code",
"visible": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "GenerateInitAndCleanup",
"trDisplayName": "Generate initialization and cleanup code",
"visible": "%{JS: [ 'QtTest', 'QtQuickTest' ].indexOf(value('TestFrameWork')) >= 0 }",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "GTestRepository",
"trDisplayName": "Googletest source directory (optional):",
"visible": "%{JS: value('TestFrameWork') === 'GTest'}",
"mandatory": false,
"type": "PathChooser",
"data": {
"kind": "existingDirectory"
}
},
{ {
"name": "BoostIncDir", "name": "BoostIncDir",
"trDisplayName": "Boost include directory (optional):", "trDisplayName": "Boost include directory (optional):",
@@ -186,25 +114,6 @@
"kind": "existingDirectory" "kind": "existingDirectory"
} }
}, },
{
"name": "CatchIncDir",
"trDisplayName": "Catch2 include directory (optional):",
"visible": "%{JS: value('TestFrameWork') === 'Catch2'}",
"mandatory": false,
"type": "PathChooser",
"data": {
"kind": "existingDirectory"
}
},
{
"name": "Catch2NeedsQt",
"trDisplayName": "Use Qt libraries",
"visible": "%{JS: '%{TestFrameWork}' === 'Catch2'}",
"type": "CheckBox",
"data": {
"checked": true
}
},
{ {
"name": "BuildSystem", "name": "BuildSystem",
"trDisplayName": "Build system:", "trDisplayName": "Build system:",
@@ -242,7 +151,7 @@
"enabled": "%{IsTopLevelProject}", "enabled": "%{IsTopLevelProject}",
"data": { "data": {
"projectFilePath": "%{ProjectFilePath}", "projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "%{JS: (value('TestFrameWork') === 'QtQuickTest' ? 'QtSupport.Wizards.FeatureQtQuick.2' : ((value('BuildSystem') === 'qmake' || value('TestFrameWork') === 'QtTest') ? 'QtSupport.Wizards.FeatureQt' : 'QtSupport.Wizards.FeatureDesktop' )) }" ] "requiredFeatures": [ "%{JS: value('BuildSystem') === 'qmake' ? 'QtSupport.Wizards.FeatureQt' : 'QtSupport.Wizards.FeatureDesktop' }" ]
} }
}, },
{ {
@@ -258,87 +167,38 @@
"data": "data":
[ [
{ {
"source": "files/gtest_dependency.pri", "source": "../files/tst.pro",
"target": "gtest_dependency.pri",
"condition": "%{JS: value('TestFrameWork') == 'GTest' && value('BuildSystem') == 'qmake'}",
"openInEditor": false
},
{
"source": "files/googlecommon.js",
"target": "googlecommon.js",
"condition": "%{JS: value('TestFrameWork') == 'GTest' && value('BuildSystem') == 'qbs'}",
"openInEditor": false
},
{
"source": "files/tst.pro",
"target": "%{ProjectFilePath}", "target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qmake'}", "condition": "%{JS: value('BuildSystem') == 'qmake'}",
"openInEditor": false, "openInEditor": false,
"openAsProject": true "openAsProject": true
}, },
{ {
"source": "files/tst.qbs", "source": "../files/tst.qbs",
"target": "%{ProjectFilePath}", "target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qbs'}", "condition": "%{JS: value('BuildSystem') == 'qbs'}",
"openInEditor": false, "openInEditor": false,
"openAsProject": true "openAsProject": true
}, },
{ {
"source": "files/tst.txt", "source": "../files/tst.txt",
"target": "CMakeLists.txt", "target": "CMakeLists.txt",
"condition": "%{JS: value('BuildSystem') == 'cmake'}", "condition": "%{JS: value('BuildSystem') == 'cmake'}",
"openInEditor": false, "openInEditor": false,
"openAsProject": true "openAsProject": true
}, },
{ {
"source": "files/tst_src_gt.cpp", "source": "../files/tst_main.cpp",
"target": "%{TestCaseFileGTestWithCppSuffix}",
"condition": "%{JS: value('TestFrameWork') == 'GTest'}",
"openInEditor": true
},
{
"source": "files/tst_src.cpp",
"target": "%{TestCaseFileWithCppSuffix}",
"condition": "%{JS: value('TestFrameWork') == 'QtTest'}",
"openInEditor": true
},
{
"source": "files/tst_main.cpp",
"target": "%{MainCppName}", "target": "%{MainCppName}",
"condition": "%{JS: ['GTest', 'QtQuickTest', 'BoostTest', 'BoostTest_dyn', 'Catch2'].indexOf(value('TestFrameWork')) >= 0}",
"openInEditor": true "openInEditor": true
}, },
{ {
"source": "files/tst_src_boost.cpp", "source": "../files/tst_src_boost.cpp",
"target": "%{TestCaseFileWithCppSuffix}", "target": "%{TestCaseFileWithCppSuffix}",
"condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}" "condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}"
}, },
{ {
"source": "files/tst_qml.tmpl", "source": "../../projects/git.ignore",
"target": "%{TestCaseFileWithQmlSuffix}",
"condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
"openInEditor": true
},
{
"source": "files/setup.cpp",
"target": "setup.cpp",
"condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
"openInEditor": true
},
{
"source": "files/setup.h",
"target": "setup.h",
"condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
"openInEditor": true
},
{
"source": "files/catch2_tst.cpp",
"target": "%{TestCaseFileWithCppSuffix}",
"condition": "%{JS: '%{TestFrameWork}' === 'Catch2'}",
"openInEditor": true
},
{
"source": "../projects/git.ignore",
"target": ".gitignore", "target": ".gitignore",
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}" "condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
} }

View File

@@ -0,0 +1,184 @@
{
"version": 1,
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
"id": "R.CatchAutoTest",
"category": "I.TestProject",
"trDescription": "Creates a new unit test project using Catch2. Unit tests allow you to verify that the code is fit for use and that there are no regressions.",
"trDisplayName": "Catch2 Test Project",
"trDisplayCategory": "Test Project",
"icon": "../autotest.png",
"iconKind": "Themed",
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
"options":
[
{ "key": "TestFrameWork",
"value": "Catch2"
},
{ "key": "ProjectFilePath",
"value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }"
},
{ "key": "ProFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}"
},
{
"key": "QbsFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}"
},
{
"key": "CMakeFileName",
"value": "%{ProjectDirectory}/CMakeLists.txt"
},
{ "key": "IsTopLevelProject",
"value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }"
},
{ "key": "MainCppName",
"value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }"
},
{
"key": "GUARD",
"value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }"
},
{
"key": "TestCaseFileWithCppSuffix",
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
}
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project",
"data":
{
"trDescription": "This wizard creates a simple unit test project using Catch2."
}
},
{
"trDisplayName": "Project and Test Information",
"trShortTitle": "Details",
"typeId": "Fields",
"data":
[
{
"name": "TestCaseName",
"trDisplayName": "Test case name:",
"mandatory": true,
"type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" }
},
{
"name": "CatchIncDir",
"trDisplayName": "Catch2 include directory (optional):",
"visible": "%{JS: value('TestFrameWork') === 'Catch2'}",
"mandatory": false,
"type": "PathChooser",
"data": {
"kind": "existingDirectory"
}
},
{
"name": "Catch2NeedsQt",
"trDisplayName": "Use Qt libraries",
"visible": "%{JS: '%{TestFrameWork}' === 'Catch2'}",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "BuildSystem",
"trDisplayName": "Build system:",
"type": "ComboBox",
"persistenceKey": "BuildSystemType",
"data":
{
"index": 1,
"items":
[
{
"trKey": "qmake",
"value": "qmake",
"condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}"
},
{
"trKey": "CMake",
"value": "cmake",
"condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}"
},
{
"trKey": "Qbs",
"value": "qbs",
"condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}"
}
]
}
}
]
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"enabled": "%{IsTopLevelProject}",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "%{JS: (value('Catch2NeedsQt') || value('BuildSystem') === 'qmake') ? 'QtSupport.Wizards.FeatureQt' : 'QtSupport.Wizards.FeatureDesktop' }" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "../files/tst.pro",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.qbs",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.txt",
"target": "CMakeLists.txt",
"condition": "%{JS: value('BuildSystem') == 'cmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst_main.cpp",
"target": "%{MainCppName}",
"openInEditor": true
},
{
"source": "../files/catch2_tst.cpp",
"target": "%{TestCaseFileWithCppSuffix}",
"condition": "%{JS: '%{TestFrameWork}' === 'Catch2'}",
"openInEditor": true
},
{
"source": "../../projects/git.ignore",
"target": ".gitignore",
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
}
]
}
]
}

View File

@@ -150,7 +150,7 @@ find_package(Boost COMPONENTS unit_test_framework REQUIRED)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileGTestWithCppSuffix}) add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileWithCppSuffix})
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName}) add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
if (Boost_FOUND) if (Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
@@ -165,7 +165,8 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Gui)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui)
@endif @endif
add_executable(${PROJECT_NAME} %{TestCaseFileWithCppSuffix} main.cpp) add_executable(%{TestCaseName} %{TestCaseFileWithCppSuffix} main.cpp)
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
@if "%{Catch2NeedsQt}" == "true" @if "%{Catch2NeedsQt}" == "true"
target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::Gui) target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::Gui)

View File

@@ -0,0 +1,192 @@
{
"version": 1,
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
"id": "G.AutoTest",
"category": "I.TestProject",
"trDescription": "Creates a new unit test project using Google Test. Unit tests allow you to verify that the code is fit for use and that there are no regressions.",
"trDisplayName": "Google Test Project",
"trDisplayCategory": "Test Project",
"icon": "../autotest.png",
"iconKind": "Themed",
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
"options":
[
{ "key": "TestFrameWork",
"value": "GTest"
},
{ "key": "ProjectFilePath",
"value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }"
},
{ "key": "ProFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}"
},
{
"key": "QbsFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}"
},
{
"key": "CMakeFileName",
"value": "%{ProjectDirectory}/CMakeLists.txt"
},
{ "key": "IsTopLevelProject",
"value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }"
},
{ "key": "MainCppName",
"value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }"
},
{
"key": "TestCaseFileGTestWithCppSuffix",
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
},
{
"key": "GUARD",
"value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }"
}
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project",
"data":
{
"trDescription": "This wizard creates a simple unit test project using Google Test."
}
},
{
"trDisplayName": "Project and Test Information",
"trShortTitle": "Details",
"typeId": "Fields",
"data":
[
{
"name": "TestSuiteName",
"trDisplayName": "Test suite name:",
"mandatory": true,
"type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" }
},
{
"name": "TestCaseName",
"trDisplayName": "Test case name:",
"mandatory": true,
"type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" }
},
{
"name": "GTestRepository",
"trDisplayName": "Googletest source directory (optional):",
"mandatory": false,
"type": "PathChooser",
"data": {
"kind": "existingDirectory"
}
},
{
"name": "BuildSystem",
"trDisplayName": "Build system:",
"type": "ComboBox",
"persistenceKey": "BuildSystemType",
"data":
{
"index": 1,
"items":
[
{
"trKey": "qmake",
"value": "qmake",
"condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}"
},
{
"trKey": "CMake",
"value": "cmake",
"condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}"
},
{
"trKey": "Qbs",
"value": "qbs",
"condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}"
}
]
}
}
]
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"enabled": "%{IsTopLevelProject}",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "%{JS: value('BuildSystem') === 'qmake' ? 'QtSupport.Wizards.FeatureQt' : 'QtSupport.Wizards.FeatureDesktop' }" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "../files/gtest_dependency.pri",
"target": "gtest_dependency.pri",
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
"openInEditor": false
},
{
"source": "../files/googlecommon.js",
"target": "googlecommon.js",
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
"openInEditor": false
},
{
"source": "../files/tst.pro",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.qbs",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.txt",
"target": "CMakeLists.txt",
"condition": "%{JS: value('BuildSystem') == 'cmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst_src_gt.cpp",
"target": "%{TestCaseFileGTestWithCppSuffix}",
"openInEditor": true
},
{
"source": "../files/tst_main.cpp",
"target": "%{MainCppName}",
"openInEditor": true
},
{
"source": "../../projects/git.ignore",
"target": ".gitignore",
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
}
]
}
]
}

View File

@@ -0,0 +1,180 @@
{
"version": 1,
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
"id": "A.QtTestAutoTest",
"category": "I.TestProject",
"trDescription": "Creates a new unit test project using Qt Test. Unit tests allow you to verify that the code is fit for use and that there are no regressions.",
"trDisplayName": "Qt Test Project",
"trDisplayCategory": "Test Project",
"icon": "../autotest.png",
"iconKind": "Themed",
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
"options":
[
{ "key": "TestFrameWork",
"value": "QtTest"
},
{ "key": "ProjectFilePath",
"value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }"
},
{ "key": "ProFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}"
},
{
"key": "QbsFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}"
},
{
"key": "CMakeFileName",
"value": "%{ProjectDirectory}/CMakeLists.txt"
},
{ "key": "IsTopLevelProject",
"value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }"
},
{
"key": "GUARD",
"value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }"
},
{
"key": "TestCaseFileWithCppSuffix",
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
}
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project",
"data":
{
"trDescription": "This wizard creates a simple unit test project using Qt Test."
}
},
{
"trDisplayName": "Project and Test Information",
"trShortTitle": "Details",
"typeId": "Fields",
"data":
[
{
"name": "TestCaseName",
"trDisplayName": "Test case name:",
"mandatory": true,
"type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" }
},
{
"name": "RequireApplication",
"trDisplayName": "Requires QApplication",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "RequireGUI",
"trDisplayName": "GUI Application",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "GenerateInitAndCleanup",
"trDisplayName": "Generate initialization and cleanup code",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "BuildSystem",
"trDisplayName": "Build system:",
"type": "ComboBox",
"persistenceKey": "BuildSystemType",
"data":
{
"index": 1,
"items":
[
{
"trKey": "qmake",
"value": "qmake",
"condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}"
},
{
"trKey": "CMake",
"value": "cmake",
"condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}"
},
{
"trKey": "Qbs",
"value": "qbs",
"condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}"
}
]
}
}
]
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"enabled": "%{IsTopLevelProject}",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "QtSupport.Wizards.FeatureQt", "QtSupport.Wizards.FeatureDesktop" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "../files/tst.pro",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.qbs",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.txt",
"target": "CMakeLists.txt",
"condition": "%{JS: value('BuildSystem') == 'cmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst_src.cpp",
"target": "%{TestCaseFileWithCppSuffix}",
"openInEditor": true
},
{
"source": "../../projects/git.ignore",
"target": ".gitignore",
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
}
]
}
]
}

View File

@@ -0,0 +1,192 @@
{
"version": 1,
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
"id": "C.QuickAutoTest",
"category": "I.TestProject",
"trDescription": "Creates a new unit test project using Qt Quick Test. Unit tests allow you to verify that the code is fit for use and that there are no regressions.",
"trDisplayName": "Qt Quick Test Project",
"trDisplayCategory": "Test Project",
"icon": "../autotest.png",
"iconKind": "Themed",
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
"options":
[
{ "key": "TestFrameWork",
"value": "QtQuickTest"
},
{ "key": "ProjectFilePath",
"value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }"
},
{ "key": "ProFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}"
},
{
"key": "QbsFileName",
"value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}"
},
{
"key": "CMakeFileName",
"value": "%{ProjectDirectory}/CMakeLists.txt"
},
{ "key": "IsTopLevelProject",
"value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }"
},
{ "key": "MainCppName",
"value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }"
},
{
"key": "GUARD",
"value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }"
},
{
"key": "TestCaseFileWithQmlSuffix",
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.qml' }"
}
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project",
"data":
{
"trDescription": "This wizard creates a simple unit test project using Qt Quick Test."
}
},
{
"trDisplayName": "Project and Test Information",
"trShortTitle": "Details",
"typeId": "Fields",
"data":
[
{
"name": "TestCaseName",
"trDisplayName": "Test case name:",
"mandatory": true,
"type": "LineEdit",
"data": { "validator": "^[a-zA-Z_0-9]+$" }
},
{
"name": "UseSetupCode",
"trDisplayName": "Generate setup code",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "GenerateInitAndCleanup",
"trDisplayName": "Generate initialization and cleanup code",
"type": "CheckBox",
"data": {
"checked": false
}
},
{
"name": "BuildSystem",
"trDisplayName": "Build system:",
"type": "ComboBox",
"persistenceKey": "BuildSystemType",
"data":
{
"index": 1,
"items":
[
{
"trKey": "qmake",
"value": "qmake",
"condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}"
},
{
"trKey": "CMake",
"value": "cmake",
"condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}"
},
{
"trKey": "Qbs",
"value": "qbs",
"condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}"
}
]
}
}
]
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"enabled": "%{IsTopLevelProject}",
"data": {
"projectFilePath": "%{ProjectFilePath}",
"requiredFeatures": [ "QtSupport.Wizards.FeatureQtQuick.2", "QtSupport.Wizards.FeatureDesktop" ]
}
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "../files/tst.pro",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.qbs",
"target": "%{ProjectFilePath}",
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst.txt",
"target": "CMakeLists.txt",
"condition": "%{JS: value('BuildSystem') == 'cmake'}",
"openInEditor": false,
"openAsProject": true
},
{
"source": "../files/tst_main.cpp",
"target": "%{MainCppName}",
"openInEditor": true
},
{
"source": "../files/tst_qml.tmpl",
"target": "%{TestCaseFileWithQmlSuffix}",
"openInEditor": true
},
{
"source": "../files/setup.cpp",
"target": "setup.cpp",
"condition": "%{JS: value('UseSetupCode')}",
"openInEditor": true
},
{
"source": "../files/setup.h",
"target": "setup.h",
"condition": "%{JS: value('UseSetupCode')}",
"openInEditor": true
},
{
"source": "../../projects/git.ignore",
"target": ".gitignore",
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
}
]
}
]
}

View File

@@ -7,8 +7,8 @@
#include "execmenu.h" #include "execmenu.h"
#include "historycompleter.h" #include "historycompleter.h"
#include "hostosinfo.h" #include "hostosinfo.h"
#include "icon.h"
#include "qtcassert.h" #include "qtcassert.h"
#include "utilsicons.h"
#include "utilstr.h" #include "utilstr.h"
#include <solutions/spinner/spinner.h> #include <solutions/spinner/spinner.h>
@@ -621,7 +621,6 @@ QString FancyLineEdit::fixInputString(const QString &string)
FancyIconButton::FancyIconButton(QWidget *parent) FancyIconButton::FancyIconButton(QWidget *parent)
: QAbstractButton(parent) : QAbstractButton(parent)
, m_autoHide(false)
{ {
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
setFocusPolicy(Qt::NoFocus); setFocusPolicy(Qt::NoFocus);

View File

@@ -45,8 +45,8 @@ protected:
void keyReleaseEvent(QKeyEvent *ke) override; void keyReleaseEvent(QKeyEvent *ke) override;
private: private:
float m_iconOpacity; float m_iconOpacity = 1.0f;
bool m_autoHide; bool m_autoHide = false;
QIcon m_icon; QIcon m_icon;
}; };

View File

@@ -79,6 +79,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
d->m_nameLineEdit = new Utils::FancyLineEdit(frame); d->m_nameLineEdit = new Utils::FancyLineEdit(frame);
d->m_pathChooser = new Utils::PathChooser(frame); d->m_pathChooser = new Utils::PathChooser(frame);
d->m_pathChooser->setObjectName("baseFolder"); // used by Squish
d->m_pathChooser->setExpectedKind(PathChooser::Directory); d->m_pathChooser->setExpectedKind(PathChooser::Directory);
d->m_pathChooser->setDisabled(d->m_forceSubProject); d->m_pathChooser->setDisabled(d->m_forceSubProject);

View File

@@ -261,7 +261,10 @@ bool QuickTestParser::handleQtQuickTest(QPromise<TestParseResultPtr> &promise,
return false; return false;
const FilePath cppFileName = document->filePath(); const FilePath cppFileName = document->filePath();
const FilePath proFile = FilePath::fromString(ppList.at(0)->projectFile); const FilePath proFile = FilePath::fromString(ppList.at(0)->projectFile);
{
QWriteLocker lock(&m_parseLock);
m_mainCppFiles.insert(cppFileName, proFile); m_mainCppFiles.insert(cppFileName, proFile);
}
const FilePath srcDir = FilePath::fromString(quickTestSrcDir(cppFileName)); const FilePath srcDir = FilePath::fromString(quickTestSrcDir(cppFileName));
if (srcDir.isEmpty()) if (srcDir.isEmpty())
return false; return false;
@@ -340,13 +343,13 @@ QuickTestParser::QuickTestParser(ITestFramework *framework)
void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse) void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
{ {
m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot(); m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
QWriteLocker lock(&m_parseLock); // should not be necessary
if (!fullParse) { if (!fullParse) {
// in a full parse we get the correct entry points by the respective main // in a full parse we get the correct entry points by the respective main
m_proFilesForQmlFiles = QuickTestUtils::proFilesForQmlFiles(framework(), filesToParse); m_proFilesForQmlFiles = QuickTestUtils::proFilesForQmlFiles(framework(), filesToParse);
// get rid of cached main cpp files that are going to get processed anyhow // get rid of cached main cpp files that are going to get processed anyhow
for (const FilePath &file : filesToParse) { for (const FilePath &file : filesToParse) {
if (m_mainCppFiles.contains(file)) { if (m_mainCppFiles.remove(file) == 1) {
m_mainCppFiles.remove(file);
if (m_mainCppFiles.isEmpty()) if (m_mainCppFiles.isEmpty())
break; break;
} }
@@ -355,6 +358,7 @@ void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
// get rid of all cached main cpp files // get rid of all cached main cpp files
m_mainCppFiles.clear(); m_mainCppFiles.clear();
} }
lock.unlock();
m_checkForDerivedTests = theQtTestFramework().quickCheckForDerivedTests(); m_checkForDerivedTests = theQtTestFramework().quickCheckForDerivedTests();
@@ -399,9 +403,10 @@ bool QuickTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
return handleQtQuickTest(promise, cppdoc, framework()); return handleQtQuickTest(promise, cppdoc, framework());
} }
FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName) const FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName)
{ {
return m_mainCppFiles.contains(fileName) ? m_mainCppFiles.value(fileName) : FilePath(); QReadLocker lock(&m_parseLock);
return m_mainCppFiles.value(fileName);
} }
} // namespace Autotest::Internal } // namespace Autotest::Internal

View File

@@ -8,6 +8,7 @@
#include <qmljs/qmljsdocument.h> #include <qmljs/qmljsdocument.h>
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QReadWriteLock>
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
@@ -28,7 +29,7 @@ public:
void release() override; void release() override;
bool processDocument(QPromise<TestParseResultPtr> &promise, bool processDocument(QPromise<TestParseResultPtr> &promise,
const Utils::FilePath &fileName) override; const Utils::FilePath &fileName) override;
Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName) const; Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName);
QStringList supportedExtensions() const override { return {"qml"}; }; QStringList supportedExtensions() const override { return {"qml"}; };
private: private:
@@ -45,6 +46,7 @@ private:
QMap<QString, QMap<QString, QDateTime> > m_watchedFiles; QMap<QString, QMap<QString, QDateTime> > m_watchedFiles;
QMap<Utils::FilePath, Utils::FilePath> m_mainCppFiles; QMap<Utils::FilePath, Utils::FilePath> m_mainCppFiles;
QSet<Utils::FilePath> m_prefilteredFiles; QSet<Utils::FilePath> m_prefilteredFiles;
QReadWriteLock m_parseLock; // guard for m_mainCppFiles
bool m_checkForDerivedTests = false; bool m_checkForDerivedTests = false;
}; };

View File

@@ -611,7 +611,8 @@ void TestTreeModel::insertItemInParent(TestTreeItem *item, TestTreeItem *root, b
delete item; delete item;
} else { } else {
// restore former check state if available // restore former check state if available
std::optional<Qt::CheckState> cached = m_checkStateCache->get(item); std::optional<Qt::CheckState> cached = m_checkStateCache ? m_checkStateCache->get(item)
: std::optional<Qt::CheckState>{};
if (cached.has_value()) if (cached.has_value())
item->setData(0, cached.value(), Qt::CheckStateRole); item->setData(0, cached.value(), Qt::CheckStateRole);
else else

View File

@@ -48,6 +48,27 @@ class TestHandler : public rst::ContentHandler {
void StartBlock(rst::BlockType type) { void StartBlock(rst::BlockType type) {
std::string tag; std::string tag;
switch (type) { switch (type) {
case rst::REFERENCE_LINK:
// not used, HandleReferenceLink is used instead
break;
case rst::H1:
tag = "h1";
break;
case rst::H2:
tag = "h2";
break;
case rst::H3:
tag = "h3";
break;
case rst::H4:
tag = "h4";
break;
case rst::H5:
tag = "h5";
break;
case rst::CODE:
tag = "code";
break;
case rst::PARAGRAPH: case rst::PARAGRAPH:
tag = "p"; tag = "p";
break; break;
@@ -80,8 +101,12 @@ class TestHandler : public rst::ContentHandler {
content_.append(text, size); content_.append(text, size);
} }
void HandleDirective(const char *type) { void HandleDirective(const std::string &type, const std::string &name) {
content_ += std::string("<") + type + " />"; content_ += std::string("<div class=\"") + name + "\">" + type + "</div>";
}
void HandleReferenceLink(const std::string &type, const std::string &text) {
content_ += std::string("<a href=\"#") + type + "\">" + text + "</a>";
} }
}; };
@@ -93,6 +118,14 @@ std::string Parse(const char *s) {
} }
} }
TEST(ParserTest, HX) {
EXPECT_EQ("<h1>test</h1>", Parse("====\ntest\n===="));
EXPECT_EQ("<h2>test</h2>", Parse("test\n===="));
EXPECT_EQ("<h3>test</h3>", Parse("test\n----"));
EXPECT_EQ("<h4>test</h4>", Parse("test\n^^^^"));
EXPECT_EQ("<h5>test</h5>", Parse("test\n\"\"\"\""));
}
TEST(ParserTest, Paragraph) { TEST(ParserTest, Paragraph) {
EXPECT_EQ("<p>test</p>", Parse("test")); EXPECT_EQ("<p>test</p>", Parse("test"));
EXPECT_EQ("<p>test</p>", Parse("\ntest")); EXPECT_EQ("<p>test</p>", Parse("\ntest"));
@@ -143,6 +176,14 @@ TEST(ParserTest, Literal) {
EXPECT_EQ("<p>::\nabc\ndef</p>", Parse("::\nabc\ndef")); EXPECT_EQ("<p>::\nabc\ndef</p>", Parse("::\nabc\ndef"));
} }
TEST(ParserTest, InlineCode) {
EXPECT_EQ("<p><code>code</code></p>", Parse("``code``"));
EXPECT_EQ("<p>`code``</p>", Parse("`code``"));
EXPECT_EQ("<p>some <code>code</code></p>", Parse("some ``code``"));
EXPECT_EQ("<p><code>code</code> some</p>", Parse("``code`` some"));
EXPECT_EQ("<p>some <code>code</code> and more</p>", Parse("some ``code`` and more"));
}
TEST(ParserTest, Comment) { TEST(ParserTest, Comment) {
EXPECT_EQ("", Parse("..")); EXPECT_EQ("", Parse(".."));
EXPECT_EQ("", Parse("..\n")); EXPECT_EQ("", Parse("..\n"));
@@ -151,11 +192,49 @@ TEST(ParserTest, Comment) {
} }
TEST(ParserTest, Directive) { TEST(ParserTest, Directive) {
EXPECT_EQ("<test />", Parse(".. test::")); EXPECT_EQ("<div class=\"\">test</div>", Parse(".. test::"));
EXPECT_EQ("<test />", Parse(".. test::")); EXPECT_EQ("<div class=\"name\">test</div>", Parse(".. test:: name"));
EXPECT_EQ("<test />", Parse("..\ttest::")); EXPECT_EQ("<div class=\"\">test</div>", Parse(".. test::"));
EXPECT_EQ("<div class=\"\">test</div>", Parse("..\ttest::"));
EXPECT_EQ("<div class=\"to-text\">|from-text| replace</div>", Parse(".. |from-text| replace:: to-text"));
std::string rst =
R"(.. code-block:: c++
int main() {
if (false)
return 1;
return 0;
})";
std::string html =
R"(<div class="c++">code-block</div><blockquote>int main() {
if (false)
return 1;
return 0;
}</blockquote>)";
EXPECT_EQ(html, Parse(rst.c_str()));
rst =
R"(.. note:: This is a cool
note. Such a cool note.)";
html =
R"(<div class="">note</div><blockquote>This is a cool
note. Such a cool note.</blockquote>)";
EXPECT_EQ(html, Parse(rst.c_str()));
} }
TEST(ParserTest, ReferenceLinks) {
EXPECT_EQ("<p><a href=\"#ref\">info</a></p>", Parse(":ref:`info`"));
EXPECT_EQ("<p>some <a href=\"#ref\">info</a></p>", Parse("some :ref:`info`"));
EXPECT_EQ("<p>some <a href=\"#ref\">info</a> and more</p>", Parse("some :ref:`info` and more"));
EXPECT_EQ("<p><a href=\"#ref\">info</a>.</p>", Parse(":ref:`info`."));
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
#ifdef _WIN32 #ifdef _WIN32
// Disable message boxes on assertion failures. // Disable message boxes on assertion failures.

View File

@@ -27,6 +27,7 @@
#include "rstparser.h" #include "rstparser.h"
#include <algorithm>
#include <cctype> #include <cctype>
#include <cstring> #include <cstring>
@@ -55,15 +56,15 @@ void rst::Parser::SkipSpace() {
std::string rst::Parser::ParseDirectiveType() { std::string rst::Parser::ParseDirectiveType() {
const char *s = ptr_; const char *s = ptr_;
if (!std::isalnum(*s)) if (!std::isalnum(*s) && *s != '|')
return std::string(); return std::string();
for (;;) { for (;;) {
++s; ++s;
if (std::isalnum(*s)) if (std::isalnum(*s))
continue; continue;
switch (*s) { switch (*s) {
case '-': case '_': case '+': case ':': case '.': case '-': case '_': case '+': case ':': case '.': case '|':
if (std::isalnum(s[1])) { if (std::isalnum(s[1]) || (*s == '|' && IsSpace(s[1]))) {
++s; ++s;
continue; continue;
} }
@@ -91,13 +92,28 @@ void rst::Parser::EnterBlock(rst::BlockType &prev_type, rst::BlockType type) {
void rst::Parser::ParseBlock( void rst::Parser::ParseBlock(
rst::BlockType type, rst::BlockType &prev_type, int indent) { rst::BlockType type, rst::BlockType &prev_type, int indent) {
std::string text; std::string text;
struct InlineTags {
rst::BlockType type;
std::size_t pos {};
std::string text;
std::string type_string;
};
std::vector<InlineTags> inline_tags;
bool have_h1 = false;
for (bool first = true; ; first = false) { for (bool first = true; ; first = false) {
const char *line_start = ptr_; const char *line_start = ptr_;
if (!first) { if (!first) {
// Check indentation. // Check indentation.
SkipSpace(); SkipSpace();
if (ptr_ - line_start != indent) const int new_indent = ptr_ - line_start;
if (new_indent < indent)
break; break;
// Restore the indent
if (new_indent > indent)
std::advance(ptr_, indent - new_indent);
if (*ptr_ == '\n') { if (*ptr_ == '\n') {
++ptr_; ++ptr_;
break; // Empty line ends the block. break; // Empty line ends the block.
@@ -119,9 +135,17 @@ void rst::Parser::ParseBlock(
// Copy text converting all whitespace characters to spaces. // Copy text converting all whitespace characters to spaces.
text.reserve(end - line_start + 1); text.reserve(end - line_start + 1);
if (!first) if (!first && !have_h1)
text.push_back('\n'); text.push_back('\n');
enum {TAB_WIDTH = 8}; enum {TAB_WIDTH = 8};
// Used the sections mapping from https://docs.anaconda.com/restructuredtext/index.html
struct {
BlockType type;
int count = 0;
char c = 0;
} hx[] = { {H1, 0, '=' }, {H2, 0, '='}, {H3, 0, '-'}, {H4, 0, '^'}, {H5, 0, '\"'}};
for (const char *s = line_start; s != end; ++s) { for (const char *s = line_start; s != end; ++s) {
char c = *s; char c = *s;
if (c == '\t') { if (c == '\t') {
@@ -129,10 +153,60 @@ void rst::Parser::ParseBlock(
TAB_WIDTH - ((indent + s - line_start) % TAB_WIDTH)); TAB_WIDTH - ((indent + s - line_start) % TAB_WIDTH));
} else if (IsSpace(c)) { } else if (IsSpace(c)) {
text.push_back(' '); text.push_back(' ');
} else if (c == hx[0].c) {
++hx[0].count;
++hx[1].count;
} else if (c == hx[2].c) {
++hx[2].count;
} else if (c == hx[3].c) {
++hx[3].count;
} else if (c == hx[4].c) {
++hx[4].count;
} else if (c == '`') {
std::string code_tag_text;
if (ParseCode(s, end - s, code_tag_text)) {
InlineTags code;
code.type = rst::CODE;
code.pos = text.size();
code.text = code_tag_text;
inline_tags.push_back(code);
const int tag_size = 4;
s = s + code_tag_text.size() + tag_size - 1;
} else {
text.push_back(*s);
}
} else if (c == ':') {
std::string link_type;
std::string link_text;
if (ParseReferenceLink(s, end - s, link_type, link_text)) {
InlineTags link;
link.type = rst::REFERENCE_LINK;
link.pos = text.size();
link.text = link_text;
link.type_string = link_type;
inline_tags.push_back(link);
const int tag_size = 4;
s = s + link_type.size() + link_text.size() + tag_size - 1;
} else {
text.push_back(*s);
}
} else { } else {
text.push_back(*s); text.push_back(*s);
} }
} }
for (int i = 0; i < 5; ++i) {
if (hx[i].count > 0 && hx[i].count == end - line_start) {
// h1 and h2 have the same underline character
// only if there was one ontop then is h1 otherwise h2
if (i == 0 && first)
have_h1 = true;
if ((i == 0 && !have_h1) || (i == 1 && have_h1))
continue;
type = hx[i].type;
}
}
if (*ptr_ == '\n') if (*ptr_ == '\n')
++ptr_; ++ptr_;
} }
@@ -144,11 +218,35 @@ void rst::Parser::ParseBlock(
bool literal = type == PARAGRAPH && EndsWith(text, "::"); bool literal = type == PARAGRAPH && EndsWith(text, "::");
if (!literal || text.size() != 2) { if (!literal || text.size() != 2) {
std::size_t size = text.size(); std::size_t size = text.size();
if (size == 0 && inline_tags.size() == 0)
return;
if (literal) if (literal)
--size; --size;
EnterBlock(prev_type, type); EnterBlock(prev_type, type);
handler_->StartBlock(type); handler_->StartBlock(type);
if (inline_tags.size() == 0) {
handler_->HandleText(text.c_str(), size); handler_->HandleText(text.c_str(), size);
} else {
std::size_t start = 0;
for (const InlineTags &in : inline_tags) {
if (in.pos > start)
handler_->HandleText(text.c_str() + start, in.pos - start);
if (in.type == rst::REFERENCE_LINK) {
handler_->HandleReferenceLink(in.type_string, in.text);
} else {
handler_->StartBlock(in.type);
handler_->HandleText(in.text.c_str(), in.text.size());
handler_->EndBlock();
}
start = in.pos;
}
if (start < size)
handler_->HandleText(text.c_str() + start, size - start);
}
handler_->EndBlock(); handler_->EndBlock();
} }
if (literal) { if (literal) {
@@ -191,6 +289,58 @@ void rst::Parser::ParseLineBlock(rst::BlockType &prev_type, int indent) {
handler_->EndBlock(); handler_->EndBlock();
} }
bool rst::Parser::ParseCode(const char *s, std::size_t size, std::string &code)
{
// It requires at least four ticks ``text``
if (s[0] != '`' || s[1] != '`')
return false;
if (size < 4)
return false;
std::size_t start_pos = 2;
std::size_t end_pos = 0;
for (std::size_t i = start_pos; i < size - 1; ++i) {
if (s[i] == '`' && s[i + 1] == '`') {
end_pos = i;
break;
}
}
if (end_pos == 0)
return false;
code.assign(s + start_pos, end_pos - start_pos);
return true;
}
bool rst::Parser::ParseReferenceLink(const char *s, std::size_t size, std::string &type, std::string &text)
{
// :type:`text`
if (size < 4)
return false;
auto start_type_tag = s + 1;
auto end_type_tag = std::find(start_type_tag, s + size, ':');
if (end_type_tag == s + size)
return false;
type.assign(start_type_tag, end_type_tag - start_type_tag);
if (*(end_type_tag + 1) != '`')
return false;
auto start_text_tag = end_type_tag + 2;
auto end_text_tag = std::find(start_text_tag, s + size, '`');
if (end_text_tag == s + size)
return false;
text.assign(start_text_tag, end_text_tag - start_text_tag);
return true;
}
void rst::Parser::Parse(const char *s) { void rst::Parser::Parse(const char *s) {
BlockType prev_type = PARAGRAPH; BlockType prev_type = PARAGRAPH;
ptr_ = s; ptr_ = s;
@@ -214,7 +364,28 @@ void rst::Parser::Parse(const char *s) {
std::string type = ParseDirectiveType(); std::string type = ParseDirectiveType();
if (!type.empty() && ptr_[0] == ':' && ptr_[1] == ':') { if (!type.empty() && ptr_[0] == ':' && ptr_[1] == ':') {
ptr_ += 2; ptr_ += 2;
handler_->HandleDirective(type.c_str());
const char* after_directive = ptr_;
// Get the name of the directive
std::string name;
while (*ptr_ && *ptr_ != '\n') {
c = *ptr_++;
if (!IsSpace(c))
name.push_back(c);
}
// Special case for ".. note::" which can start directly after the ::
if (type == "note" && name.size() > 0) {
ptr_ = after_directive;
SkipSpace();
handler_->HandleDirective(type, "");
ParseBlock(BLOCK_QUOTE, prev_type, 0);
break;
}
handler_->HandleDirective(type, name);
} }
// Skip everything till the end of the line. // Skip everything till the end of the line.
while (*ptr_ && *ptr_ != '\n') while (*ptr_ && *ptr_ != '\n')

View File

@@ -35,6 +35,13 @@
namespace rst { namespace rst {
enum BlockType { enum BlockType {
H1,
H2,
H3,
H4,
H5,
CODE,
REFERENCE_LINK,
PARAGRAPH, PARAGRAPH,
LINE_BLOCK, LINE_BLOCK,
BLOCK_QUOTE, BLOCK_QUOTE,
@@ -58,7 +65,10 @@ class ContentHandler {
virtual void HandleText(const char *text, std::size_t size) = 0; virtual void HandleText(const char *text, std::size_t size) = 0;
// Receives notification of a directive. // Receives notification of a directive.
virtual void HandleDirective(const char *type) = 0; virtual void HandleDirective(const std::string &type, const std::string &name) = 0;
// Receives notification of a link.
virtual void HandleReferenceLink(const std::string &type, const std::string &text) = 0;
}; };
// A parser for a subset of reStructuredText. // A parser for a subset of reStructuredText.
@@ -85,6 +95,12 @@ class Parser {
// Parses a line block. // Parses a line block.
void ParseLineBlock(rst::BlockType &prev_type, int indent); void ParseLineBlock(rst::BlockType &prev_type, int indent);
// Parses inline ``code``
bool ParseCode(const char* s, std::size_t size, std::string &code);
// Parses :reference:`link`
bool ParseReferenceLink(const char* s, std::size_t size, std::string &type, std::string &text);
public: public:
explicit Parser(ContentHandler *h) : handler_(h), ptr_(0) {} explicit Parser(ContentHandler *h) : handler_(h), ptr_(0) {}
@@ -94,4 +110,3 @@ class Parser {
} }
#endif // RSTPARSER_H_ #endif // RSTPARSER_H_

View File

@@ -46,4 +46,5 @@ add_qtc_plugin(CMakeProjectManager
3rdparty/cmake/cmListFileCache.cxx 3rdparty/cmake/cmListFileCache.cxx
3rdparty/cmake/cmListFileLexer.cxx 3rdparty/cmake/cmListFileLexer.cxx
3rdparty/cmake/cmListFileCache.h 3rdparty/cmake/cmListFileCache.h
3rdparty/rstparser/rstparser.cc 3rdparty/rstparser/rstparser.h
) )

View File

@@ -8,8 +8,6 @@
#include "cmakebuildsystem.h" #include "cmakebuildsystem.h"
#include "cmakefilecompletionassist.h" #include "cmakefilecompletionassist.h"
#include "cmakeindenter.h" #include "cmakeindenter.h"
#include "cmakekitaspect.h"
#include "cmakeproject.h"
#include "cmakeprojectconstants.h" #include "cmakeprojectconstants.h"
#include "3rdparty/cmake/cmListFileCache.h" #include "3rdparty/cmake/cmListFileCache.h"
@@ -54,13 +52,7 @@ public:
CMakeEditor::CMakeEditor() CMakeEditor::CMakeEditor()
{ {
CMakeTool *tool = nullptr; if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
if (auto bs = ProjectTree::currentBuildSystem())
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
if (!tool)
tool = CMakeToolManager::defaultCMakeTool();
if (tool)
m_keywords = tool->keywords(); m_keywords = tool->keywords();
} }
@@ -321,6 +313,7 @@ class CMakeHoverHandler : public TextEditor::BaseHoverHandler
{ {
mutable CMakeKeywords m_keywords; mutable CMakeKeywords m_keywords;
QString m_helpToolTip; QString m_helpToolTip;
QString m_contextHelp;
public: public:
const CMakeKeywords &keywords() const; const CMakeKeywords &keywords() const;
@@ -333,22 +326,13 @@ public:
const CMakeKeywords &CMakeHoverHandler::keywords() const const CMakeKeywords &CMakeHoverHandler::keywords() const
{ {
if (m_keywords.functions.isEmpty()) { if (m_keywords.functions.isEmpty())
CMakeTool *tool = nullptr; if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
if (auto bs = ProjectTree::currentBuildSystem())
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
if (!tool)
tool = CMakeToolManager::defaultCMakeTool();
if (tool)
m_keywords = tool->keywords(); m_keywords = tool->keywords();
}
return m_keywords; return m_keywords;
} }
QString readFirstParagraphs(const QString &element, const FilePath &helpFile);
void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget, void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
int pos, int pos,
ReportPriority report) ReportPriority report)
@@ -360,24 +344,39 @@ void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget
const QString word = Utils::Text::wordUnderCursor(cursor); const QString word = Utils::Text::wordUnderCursor(cursor);
FilePath helpFile; FilePath helpFile;
for (const auto &map : {keywords().functions, QString helpCategory;
keywords().variables, struct
keywords().directoryProperties, {
keywords().sourceProperties, const QMap<QString, Utils::FilePath> &map;
keywords().targetProperties, QString helpCategory;
keywords().testProperties, } keywordsListMaps[] = {{keywords().functions, "command"},
keywords().properties, {keywords().variables, "variable"},
keywords().includeStandardModules, {keywords().directoryProperties, "prop_dir"},
keywords().findModules, {keywords().sourceProperties, "prop_sf"},
keywords().policies}) { {keywords().targetProperties, "prop_tgt"},
if (map.contains(word)) { {keywords().testProperties, "prop_test"},
helpFile = map.value(word); {keywords().properties, "prop_gbl"},
{keywords().includeStandardModules, "module"},
{keywords().findModules, "module"},
{keywords().policies, "policy"}};
for (const auto &pair : keywordsListMaps) {
if (pair.map.contains(word)) {
helpFile = pair.map.value(word);
helpCategory = pair.helpCategory;
break; break;
} }
} }
m_helpToolTip.clear(); m_helpToolTip.clear();
if (!helpFile.isEmpty()) if (!helpFile.isEmpty())
m_helpToolTip = readFirstParagraphs(word, helpFile); m_helpToolTip = CMakeToolManager::toolTipForRstHelpFile(helpFile);
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
m_contextHelp = QString("%1/%2/%3")
.arg(tool->documentationUrl(tool->version(),
tool->qchFilePath().isEmpty()),
helpCategory,
word);
setPriority(m_helpToolTip.isEmpty() ? Priority_Tooltip : Priority_None); setPriority(m_helpToolTip.isEmpty() ? Priority_Tooltip : Priority_None);
} }
@@ -385,7 +384,7 @@ void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget
void CMakeHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) void CMakeHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
{ {
if (!m_helpToolTip.isEmpty()) if (!m_helpToolTip.isEmpty())
Utils::ToolTip::show(point, m_helpToolTip, Qt::MarkdownText, editorWidget); Utils::ToolTip::show(point, m_helpToolTip, Qt::MarkdownText, editorWidget, m_contextHelp);
else else
Utils::ToolTip::hide(); Utils::ToolTip::hide();
} }

View File

@@ -5,8 +5,6 @@
#include "cmakebuildsystem.h" #include "cmakebuildsystem.h"
#include "cmakebuildtarget.h" #include "cmakebuildtarget.h"
#include "cmakekitaspect.h"
#include "cmakeproject.h"
#include "cmakeprojectconstants.h" #include "cmakeprojectconstants.h"
#include "cmaketool.h" #include "cmaketool.h"
#include "cmaketoolmanager.h" #include "cmaketoolmanager.h"
@@ -143,7 +141,7 @@ static int findPathStart(const AssistInterface *interface)
} }
template<typename T> template<typename T>
QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &icon) static QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &icon)
{ {
return transform<QList>(words, [&icon](const QString &word) -> AssistProposalItemInterface * { return transform<QList>(words, [&icon](const QString &word) -> AssistProposalItemInterface * {
AssistProposalItem *item = new AssistProposalItem(); AssistProposalItem *item = new AssistProposalItem();
@@ -153,26 +151,12 @@ QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &i
}); });
} }
QString readFirstParagraphs(const QString &element, const FilePath &helpFile) static QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words,
{
static QMap<FilePath, QString> map;
if (map.contains(helpFile))
return map.value(helpFile);
auto content = helpFile.fileContents(1024).value_or(QByteArray());
const QString firstParagraphs
= QString("```\n%1\n```").arg(QString::fromUtf8(content.left(content.lastIndexOf("\n"))));
map[helpFile] = firstParagraphs;
return firstParagraphs;
}
QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words,
const QIcon &icon) const QIcon &icon)
{ {
struct MarkDownAssitProposalItem : public AssistProposalItem struct MarkDownAssitProposalItem : public AssistProposalItem
{ {
Qt::TextFormat detailFormat() const { return Qt::MarkdownText; } Qt::TextFormat detailFormat() const override { return Qt::MarkdownText; }
}; };
QList<AssistProposalItemInterface *> list; QList<AssistProposalItemInterface *> list;
@@ -180,10 +164,10 @@ QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath>
MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem(); MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem();
item->setText(it.key()); item->setText(it.key());
if (!it.value().isEmpty()) if (!it.value().isEmpty())
item->setDetail(readFirstParagraphs(it.key(), it.value())); item->setDetail(CMakeToolManager::toolTipForRstHelpFile(it.value()));
item->setIcon(icon); item->setIcon(icon);
list << item; list << item;
}; }
return list; return list;
} }
@@ -203,7 +187,7 @@ static int addFilePathItems(const AssistInterface *interface,
const QString word = interface->textAt(startPos, interface->position() - startPos); const QString word = interface->textAt(startPos, interface->position() - startPos);
FilePath baseDir = interface->filePath().absoluteFilePath().parentDir(); FilePath baseDir = interface->filePath().absoluteFilePath().parentDir();
const int lastSlashPos = word.lastIndexOf(QLatin1Char('/')); const qsizetype lastSlashPos = word.lastIndexOf(QLatin1Char('/'));
QString prefix = word; QString prefix = word;
if (lastSlashPos != -1) { if (lastSlashPos != -1) {
@@ -227,7 +211,7 @@ static int addFilePathItems(const AssistInterface *interface,
return startPos; return startPos;
} }
QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByteArray &content) static QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByteArray &content)
{ {
cmListFile cmakeListFile; cmListFile cmakeListFile;
std::string errorString; std::string errorString;
@@ -258,16 +242,8 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
Project *project = nullptr; Project *project = nullptr;
const FilePath &filePath = interface()->filePath(); const FilePath &filePath = interface()->filePath();
if (!filePath.isEmpty() && filePath.isFile()) { if (!filePath.isEmpty() && filePath.isFile()) {
CMakeTool *cmake = nullptr; if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
project = static_cast<CMakeProject *>(ProjectManager::projectForFile(filePath)); keywords = tool->keywords();
if (project && project->activeTarget())
cmake = CMakeKitAspect::cmakeTool(project->activeTarget()->kit());
if (!cmake)
cmake = CMakeToolManager::defaultCMakeTool();
if (cmake && cmake->isValid())
keywords = cmake->keywords();
} }
QStringList buildTargets; QStringList buildTargets;

View File

@@ -93,13 +93,15 @@ QtcPlugin {
name: "3rdparty" name: "3rdparty"
cpp.includePaths: base.concat("3rdparty/cmake") cpp.includePaths: base.concat("3rdparty/cmake")
prefix: "3rdparty/cmake/" prefix: "3rdparty/"
files: [ files: [
"cmListFileCache.cxx", "cmake/cmListFileCache.cxx",
"cmListFileCache.h", "cmake/cmListFileCache.h",
"cmListFileLexer.cxx", "cmake/cmListFileLexer.cxx",
"cmListFileLexer.h", "cmake/cmListFileLexer.h",
"cmStandardLexer.h", "cmake/cmStandardLexer.h",
"rstparser/rstparser.cc",
"rstparser/rstparser.h"
] ]
} }
} }

View File

@@ -8,11 +8,17 @@
#include "cmakespecificsettings.h" #include "cmakespecificsettings.h"
#include "cmaketoolsettingsaccessor.h" #include "cmaketoolsettingsaccessor.h"
#include "3rdparty/rstparser/rstparser.h"
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <coreplugin/helpmanager.h> #include <coreplugin/helpmanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/projecttree.h>
#include <projectexplorer/target.h>
#include <stack>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/pointeralgorithm.h> #include <utils/pointeralgorithm.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -32,6 +38,137 @@ public:
Internal::CMakeToolSettingsAccessor m_accessor; Internal::CMakeToolSettingsAccessor m_accessor;
}; };
class HtmlHandler : public rst::ContentHandler
{
private:
std::stack<QString> m_tags;
QStringList m_p;
QStringList m_h3;
QStringList m_cmake_code;
QString m_last_directive_type;
QString m_last_directive_class;
void StartBlock(rst::BlockType type) final
{
QString tag;
switch (type) {
case rst::REFERENCE_LINK:
// not used, HandleReferenceLink is used instead
break;
case rst::H1:
tag = "h1";
break;
case rst::H2:
tag = "h2";
break;
case rst::H3:
tag = "h3";
break;
case rst::H4:
tag = "h4";
break;
case rst::H5:
tag = "h5";
break;
case rst::CODE:
tag = "code";
break;
case rst::PARAGRAPH:
tag = "p";
break;
case rst::LINE_BLOCK:
tag = "pre";
break;
case rst::BLOCK_QUOTE:
if (m_last_directive_type == "code-block" && m_last_directive_class == "cmake")
tag = "cmake-code";
else
tag = "blockquote";
break;
case rst::BULLET_LIST:
tag = "ul";
break;
case rst::LIST_ITEM:
tag = "li";
break;
case rst::LITERAL_BLOCK:
tag = "pre";
break;
}
if (tag == "p")
m_p.push_back(QString());
if (tag == "h3")
m_h3.push_back(QString());
if (tag == "cmake-code")
m_cmake_code.push_back(QString());
if (tag == "code" && m_tags.top() == "p")
m_p.last().append("`");
m_tags.push(tag);
}
void EndBlock() final
{
// Add a new "p" collector for any `code` markup that comes afterwads
// since we are insterested only in the first paragraph.
if (m_tags.top() == "p")
m_p.push_back(QString());
if (m_tags.top() == "code" && !m_p.isEmpty()) {
m_tags.pop();
if (m_tags.size() > 0 && m_tags.top() == "p")
m_p.last().append("`");
} else {
m_tags.pop();
}
}
void HandleText(const char *text, std::size_t size) final
{
if (m_last_directive_type.endsWith("replace"))
return;
QString str = QString::fromUtf8(text, size);
if (m_tags.top() == "h3")
m_h3.last().append(str);
if (m_tags.top() == "p")
m_p.last().append(str);
if (m_tags.top() == "cmake-code")
m_cmake_code.last().append(str);
if (m_tags.top() == "code" && !m_p.isEmpty())
m_p.last().append(str);
}
void HandleDirective(const std::string &type, const std::string &name) final
{
m_last_directive_type = QString::fromStdString(type);
m_last_directive_class = QString::fromStdString(name);
}
void HandleReferenceLink(const std::string &type, const std::string &text) final
{
Q_UNUSED(type)
if (!m_p.isEmpty())
m_p.last().append(QString::fromStdString(text));
}
public:
QString content() const
{
const QString title = m_h3.isEmpty() ? QString() : m_h3.first();
const QString description = m_p.isEmpty() ? QString() : m_p.first();
const QString cmakeCode = m_cmake_code.isEmpty() ? QString() : m_cmake_code.first();
return QString("### %1\n\n%2\n\n````\n%3\n````").arg(title, description, cmakeCode);
}
};
static CMakeToolManagerPrivate *d = nullptr; static CMakeToolManagerPrivate *d = nullptr;
CMakeToolManager *CMakeToolManager::m_instance = nullptr; CMakeToolManager *CMakeToolManager::m_instance = nullptr;
@@ -108,6 +245,37 @@ void CMakeToolManager::deregisterCMakeTool(const Id &id)
} }
} }
CMakeTool *CMakeToolManager::defaultProjectOrDefaultCMakeTool()
{
static CMakeTool *tool = nullptr;
auto updateTool = [&] {
tool = nullptr;
if (auto bs = ProjectExplorer::ProjectTree::currentBuildSystem())
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
if (!tool)
tool = CMakeToolManager::defaultCMakeTool();
};
if (!tool)
updateTool();
QObject::connect(CMakeToolManager::instance(),
&CMakeToolManager::cmakeUpdated,
CMakeToolManager::instance(),
[&]() { updateTool(); });
QObject::connect(CMakeToolManager::instance(),
&CMakeToolManager::cmakeRemoved,
CMakeToolManager::instance(),
[&]() { updateTool(); });
QObject::connect(CMakeToolManager::instance(),
&CMakeToolManager::defaultCMakeChanged,
CMakeToolManager::instance(),
[&]() { updateTool(); });
return tool;
}
CMakeTool *CMakeToolManager::defaultCMakeTool() CMakeTool *CMakeToolManager::defaultCMakeTool()
{ {
return findById(d->m_defaultCMake); return findById(d->m_defaultCMake);
@@ -167,6 +335,25 @@ void CMakeToolManager::updateDocumentation()
Core::HelpManager::registerDocumentation(docs); Core::HelpManager::registerDocumentation(docs);
} }
QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile)
{
static QHash<FilePath, QString> map;
if (map.contains(helpFile))
return map.value(helpFile);
auto content = helpFile.fileContents(1024).value_or(QByteArray());
content.replace("\r\n", "\n");
HtmlHandler handler;
rst::Parser parser(&handler);
parser.Parse(content.left(content.lastIndexOf('\n')));
const QString tooltip = handler.content();
map[helpFile] = tooltip;
return tooltip;
}
QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths, QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths,
const QString &detectionSource, const QString &detectionSource,
QString *logMessage) QString *logMessage)

View File

@@ -30,6 +30,8 @@ public:
static bool registerCMakeTool(std::unique_ptr<CMakeTool> &&tool); static bool registerCMakeTool(std::unique_ptr<CMakeTool> &&tool);
static void deregisterCMakeTool(const Utils::Id &id); static void deregisterCMakeTool(const Utils::Id &id);
static CMakeTool *defaultProjectOrDefaultCMakeTool();
static CMakeTool *defaultCMakeTool(); static CMakeTool *defaultCMakeTool();
static void setDefaultCMakeTool(const Utils::Id &id); static void setDefaultCMakeTool(const Utils::Id &id);
static CMakeTool *findByCommand(const Utils::FilePath &command); static CMakeTool *findByCommand(const Utils::FilePath &command);
@@ -40,6 +42,8 @@ public:
static void updateDocumentation(); static void updateDocumentation();
static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile);
public slots: public slots:
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths, QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
const QString &detectionSource, const QString &detectionSource,

View File

@@ -8,17 +8,11 @@
#include "ctfvisualizertr.h" #include "ctfvisualizertr.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <tracing/timelinemodelaggregator.h> #include <tracing/timelinemodelaggregator.h>
#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QList>
#include <QMessageBox> #include <QMessageBox>
#include <fstream>
namespace CtfVisualizer { namespace CtfVisualizer {
namespace Internal { namespace Internal {
@@ -26,48 +20,6 @@ using json = nlohmann::json;
using namespace Constants; using namespace Constants;
class CtfJsonParserCallback
{
public:
explicit CtfJsonParserCallback(CtfTraceManager *traceManager)
: m_traceManager(traceManager)
{}
bool callback(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed)
{
if ((event == json::parse_event_t::array_start && depth == 0)
|| (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) {
m_isInTraceArray = true;
m_traceArrayDepth = depth;
return true;
}
if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) {
m_isInTraceArray = false;
return false;
}
if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) {
m_traceManager->addEvent(parsed);
return false;
}
if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) {
// keep outer object and values in trace objects:
return true;
}
// discard any objects outside of trace array:
// TODO: parse other data, e.g. stack frames
return false;
}
protected:
CtfTraceManager *m_traceManager;
bool m_isInTraceArray = false;
int m_traceArrayDepth = 0;
};
CtfTraceManager::CtfTraceManager(QObject *parent, CtfTraceManager::CtfTraceManager(QObject *parent,
Timeline::TimelineModelAggregator *modelAggregator, Timeline::TimelineModelAggregator *modelAggregator,
CtfStatisticsModel *statisticsModel) CtfStatisticsModel *statisticsModel)
@@ -75,7 +27,6 @@ CtfTraceManager::CtfTraceManager(QObject *parent,
, m_modelAggregator(modelAggregator) , m_modelAggregator(modelAggregator)
, m_statisticsModel(statisticsModel) , m_statisticsModel(statisticsModel)
{ {
} }
qint64 CtfTraceManager::traceDuration() const qint64 CtfTraceManager::traceDuration() const
@@ -142,26 +93,6 @@ void CtfTraceManager::addEvent(const json &event)
} }
} }
void CtfTraceManager::load(const QString &filename)
{
clearAll();
std::ifstream file(filename.toStdString());
if (!file.is_open()) {
QMessageBox::warning(Core::ICore::dialogParent(),
Tr::tr("CTF Visualizer"),
Tr::tr("Cannot read the CTF file."));
return;
}
CtfJsonParserCallback ctfParser(this);
json::parser_callback_t callback = [&ctfParser](int depth, json::parse_event_t event, json &parsed) {
return ctfParser.callback(depth, event, parsed);
};
json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false);
file.close();
updateStatistics();
}
void CtfTraceManager::finalize() void CtfTraceManager::finalize()
{ {
bool userConsentToIgnoreDeepTraces = false; bool userConsentToIgnoreDeepTraces = false;

View File

@@ -5,13 +5,11 @@
#include "json/json.hpp" #include "json/json.hpp"
#include <QHash> #include <QHash>
#include <QList>
#include <QMap> #include <QMap>
#include <QObject> #include <QObject>
#include <QVector>
namespace Timeline { namespace Timeline { class TimelineModelAggregator; }
class TimelineModelAggregator;
}
namespace CtfVisualizer { namespace CtfVisualizer {
namespace Internal { namespace Internal {
@@ -34,7 +32,6 @@ public:
void addEvent(const nlohmann::json &event); void addEvent(const nlohmann::json &event);
void load(const QString &filename);
void finalize(); void finalize();
bool isEmpty() const; bool isEmpty() const;
@@ -46,6 +43,9 @@ public:
void setThreadRestriction(const QString &tid, bool restrictToThisThread); void setThreadRestriction(const QString &tid, bool restrictToThisThread);
bool isRestrictedTo(const QString &tid) const; bool isRestrictedTo(const QString &tid) const;
void updateStatistics();
void clearAll();
signals: signals:
void detailsRequested(const QString &title); void detailsRequested(const QString &title);
@@ -53,10 +53,6 @@ protected:
void addModelForThread(const QString &threadId, const QString &processId); void addModelForThread(const QString &threadId, const QString &processId);
void addModelsToAggregator(); void addModelsToAggregator();
void updateStatistics();
void clearAll();
Timeline::TimelineModelAggregator *const m_modelAggregator; Timeline::TimelineModelAggregator *const m_modelAggregator;
CtfStatisticsModel *const m_statisticsModel; CtfStatisticsModel *const m_statisticsModel;

View File

@@ -13,30 +13,29 @@
#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/taskprogress.h>
#include <debugger/analyzer/analyzerconstants.h> #include <debugger/analyzer/analyzerconstants.h>
#include <utils/async.h>
#include <utils/stylehelper.h> #include <utils/stylehelper.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include <QAction>
#include <QApplication>
#include <QFileDialog> #include <QFileDialog>
#include <QFutureInterface>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <QThread>
#include <fstream>
using namespace Core; using namespace Core;
using namespace CtfVisualizer::Constants; using namespace CtfVisualizer::Constants;
using namespace Utils;
namespace CtfVisualizer { namespace CtfVisualizer {
namespace Internal { namespace Internal {
CtfVisualizerTool::CtfVisualizerTool() CtfVisualizerTool::CtfVisualizerTool()
: QObject (nullptr) : QObject (nullptr)
, m_isLoading(false)
, m_loadJson(nullptr) , m_loadJson(nullptr)
, m_traceView(nullptr) , m_traceView(nullptr)
, m_modelAggregator(new Timeline::TimelineModelAggregator(this)) , m_modelAggregator(new Timeline::TimelineModelAggregator(this))
@@ -150,34 +149,84 @@ Timeline::TimelineZoomControl *CtfVisualizerTool::zoomControl() const
return m_zoomControl.get(); return m_zoomControl.get();
} }
void CtfVisualizerTool::loadJson(const QString &filename) class CtfJsonParserFunctor
{ {
if (m_isLoading) public:
return; CtfJsonParserFunctor(QPromise<nlohmann::json> &promise)
: m_promise(promise) {}
if (filename.isEmpty()) { bool operator()(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed)
m_isLoading = false; {
using json = nlohmann::json;
if ((event == json::parse_event_t::array_start && depth == 0)
|| (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) {
m_isInTraceArray = true;
m_traceArrayDepth = depth;
return true;
}
if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) {
m_isInTraceArray = false;
return false;
}
if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) {
m_promise.addResult(parsed);
return false;
}
if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) {
// keep outer object and values in trace objects:
return true;
}
// discard any objects outside of trace array:
// TODO: parse other data, e.g. stack frames
return false;
}
protected:
QPromise<nlohmann::json> &m_promise;
bool m_isInTraceArray = false;
int m_traceArrayDepth = 0;
};
static void load(QPromise<nlohmann::json> &promise, const QString &fileName)
{
using json = nlohmann::json;
std::ifstream file(fileName.toStdString());
if (!file.is_open()) {
promise.future().cancel();
return; return;
} }
m_isLoading = true; CtfJsonParserFunctor functor(promise);
json::parser_callback_t callback = [&functor](int depth, json::parse_event_t event, json &parsed) {
return functor(depth, event, parsed);
};
auto *futureInterface = new QFutureInterface<void>();
auto *task = new QFuture<void>(futureInterface);
QThread *thread = QThread::create([this, filename, futureInterface]() {
try { try {
m_traceManager->load(filename); json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false);
} catch (...) { } catch (...) {
// nlohmann::json can throw exceptions when requesting type that is wrong // nlohmann::json can throw exceptions when requesting type that is wrong
} }
m_modelAggregator->moveToThread(QApplication::instance()->thread());
m_modelAggregator->setParent(this);
futureInterface->reportFinished();
});
connect(thread, &QThread::finished, this, [this, thread, task, futureInterface]() { file.close();
// in main thread: }
void CtfVisualizerTool::loadJson(const QString &fileName)
{
using namespace Tasking;
if (m_loader || fileName.isEmpty())
return;
const auto onSetup = [this, fileName](Async<nlohmann::json> &async) {
m_traceManager->clearAll();
async.setConcurrentCallData(load, fileName);
connect(&async, &AsyncBase::resultReadyAt, this, [this, asyncPtr = &async](int index) {
m_traceManager->addEvent(asyncPtr->resultAt(index));
});
};
const auto onDone = [this] {
m_traceManager->updateStatistics();
if (m_traceManager->isEmpty()) { if (m_traceManager->isEmpty()) {
QMessageBox::warning(Core::ICore::dialogParent(), QMessageBox::warning(Core::ICore::dialogParent(),
Tr::tr("CTF Visualizer"), Tr::tr("CTF Visualizer"),
@@ -189,17 +238,22 @@ void CtfVisualizerTool::loadJson(const QString &filename)
zoomControl()->setRange(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20); zoomControl()->setRange(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20);
} }
setAvailableThreads(m_traceManager->getSortedThreads()); setAvailableThreads(m_traceManager->getSortedThreads());
thread->deleteLater(); m_loader.release()->deleteLater();
delete task; };
delete futureInterface; const auto onError = [this] {
m_isLoading = false; QMessageBox::warning(Core::ICore::dialogParent(),
}, Qt::QueuedConnection); Tr::tr("CTF Visualizer"),
Tr::tr("Cannot read the CTF file."));
m_loader.release()->deleteLater();
};
m_modelAggregator->setParent(nullptr); const Group recipe { AsyncTask<nlohmann::json>(onSetup) };
m_modelAggregator->moveToThread(thread); m_loader.reset(new TaskTree(recipe));
connect(m_loader.get(), &TaskTree::done, this, onDone);
thread->start(); connect(m_loader.get(), &TaskTree::errorOccurred, this, onError);
Core::ProgressManager::addTask(*task, Tr::tr("Loading CTF File"), CtfVisualizerTaskLoadJson); auto progress = new TaskProgress(m_loader.get());
progress->setDisplayName(Tr::tr("Loading CTF File"));
m_loader->start();
} }
} // namespace Internal } // namespace Internal

View File

@@ -6,12 +6,15 @@
#include "ctfvisualizerconstants.h" #include "ctfvisualizerconstants.h"
#include <debugger/debuggermainwindow.h> #include <debugger/debuggermainwindow.h>
#include <tracing/timelinemodelaggregator.h> #include <tracing/timelinemodelaggregator.h>
#include <tracing/timelinezoomcontrol.h> #include <tracing/timelinezoomcontrol.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QScopedPointer> #include <QScopedPointer>
namespace Tasking { class TaskTree; }
namespace CtfVisualizer { namespace CtfVisualizer {
namespace Internal { namespace Internal {
@@ -21,7 +24,6 @@ class CtfStatisticsView;
class CtfTimelineModel; class CtfTimelineModel;
class CtfVisualizerTraceView; class CtfVisualizerTraceView;
class CtfVisualizerTool : public QObject class CtfVisualizerTool : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -34,7 +36,7 @@ public:
CtfTraceManager *traceManager() const; CtfTraceManager *traceManager() const;
Timeline::TimelineZoomControl *zoomControl() const; Timeline::TimelineZoomControl *zoomControl() const;
void loadJson(const QString &filename); void loadJson(const QString &fileName);
private: private:
void createViews(); void createViews();
@@ -45,11 +47,11 @@ private:
void setAvailableThreads(const QList<CtfTimelineModel *> &threads); void setAvailableThreads(const QList<CtfTimelineModel *> &threads);
void toggleThreadRestriction(QAction *action); void toggleThreadRestriction(QAction *action);
Utils::Perspective m_perspective{Constants::CtfVisualizerPerspectiveId, Utils::Perspective m_perspective{CtfVisualizer::Constants::CtfVisualizerPerspectiveId,
QCoreApplication::translate("QtC::CtfVisualizer", QCoreApplication::translate("QtC::CtfVisualizer",
"Chrome Trace Format Visualizer")}; "Chrome Trace Format Visualizer")};
bool m_isLoading; std::unique_ptr<Tasking::TaskTree> m_loader;
QScopedPointer<QAction> m_loadJson; QScopedPointer<QAction> m_loadJson;
CtfVisualizerTraceView *m_traceView; CtfVisualizerTraceView *m_traceView;

View File

@@ -179,6 +179,7 @@ void LldbEngine::setupEngine()
showMessage("STARTING LLDB: " + lldbCmd.toUserOutput()); showMessage("STARTING LLDB: " + lldbCmd.toUserOutput());
Environment environment = runParameters().debugger.environment; Environment environment = runParameters().debugger.environment;
environment.appendOrSet("QT_CREATOR_LLDB_PROCESS", "1");
environment.appendOrSet("PYTHONUNBUFFERED", "1"); // avoid flushing problem on macOS environment.appendOrSet("PYTHONUNBUFFERED", "1"); // avoid flushing problem on macOS
DebuggerItem::addAndroidLldbPythonEnv(lldbCmd, environment); DebuggerItem::addAndroidLldbPythonEnv(lldbCmd, environment);

View File

@@ -23,8 +23,6 @@ add_qtc_plugin(MesonProjectManager
mesoninfoparser.h mesoninfoparser.h
mesonoutputparser.cpp mesonoutputparser.cpp
mesonoutputparser.h mesonoutputparser.h
mesonprocess.cpp
mesonprocess.h
mesonproject.cpp mesonproject.cpp
mesonproject.h mesonproject.h
mesonprojectimporter.cpp mesonprojectimporter.cpp

View File

@@ -1,119 +0,0 @@
// Copyright (C) 2020 Alexis Jeandet.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "mesonprocess.h"
#include "mesonprojectmanagertr.h"
#include "toolwrapper.h"
#include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/processprogress.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h>
#include <utils/environment.h>
#include <utils/process.h>
#include <utils/stringutils.h>
#include <QLoggingCategory>
using namespace Core;
using namespace Utils;
namespace MesonProjectManager {
namespace Internal {
static Q_LOGGING_CATEGORY(mesonProcessLog, "qtc.meson.buildsystem", QtWarningMsg);
MesonProcess::MesonProcess() = default;
MesonProcess::~MesonProcess() = default;
bool MesonProcess::run(const Command &command,
const Environment &env,
const QString &projectName,
bool captureStdo)
{
if (!sanityCheck(command))
return false;
m_stdo.clear();
ProjectExplorer::TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
setupProcess(command, env, projectName, captureStdo);
m_elapsed.start();
m_process->start();
qCDebug(mesonProcessLog()) << "Starting:" << command.toUserOutput();
return true;
}
void MesonProcess::handleProcessDone()
{
if (m_process->result() != ProcessResult::FinishedWithSuccess) {
ProjectExplorer::TaskHub::addTask(ProjectExplorer::BuildSystemTask{
ProjectExplorer::Task::TaskType::Error, m_process->exitMessage()});
}
m_stdo = m_process->readAllRawStandardOutput();
m_stderr = m_process->readAllRawStandardError();
const QString elapsedTime = formatElapsedTime(m_elapsed.elapsed());
MessageManager::writeSilently(elapsedTime);
emit finished(m_process->exitCode(), m_process->exitStatus());
}
void MesonProcess::setupProcess(const Command &command, const Environment &env,
const QString &projectName, bool captureStdo)
{
if (m_process)
m_process.release()->deleteLater();
m_process.reset(new Process);
connect(m_process.get(), &Process::done, this, &MesonProcess::handleProcessDone);
if (!captureStdo) {
connect(m_process.get(), &Process::readyReadStandardOutput,
this, &MesonProcess::processStandardOutput);
connect(m_process.get(), &Process::readyReadStandardError,
this, &MesonProcess::processStandardError);
}
m_process->setWorkingDirectory(command.workDir());
m_process->setEnvironment(env);
MessageManager::writeFlashing(Tr::tr("Running %1 in %2.")
.arg(command.toUserOutput(), command.workDir().toUserOutput()));
m_process->setCommand(command.cmdLine());
m_process->setTimeoutS(10);
ProcessProgress *progress = new ProcessProgress(m_process.get());
progress->setDisplayName(Tr::tr("Configuring \"%1\".").arg(projectName));
}
bool MesonProcess::sanityCheck(const Command &command) const
{
const auto &exe = command.cmdLine().executable();
if (!exe.exists()) {
//Should only reach this point if Meson exe is removed while a Meson project is opened
ProjectExplorer::TaskHub::addTask(
ProjectExplorer::BuildSystemTask{ProjectExplorer::Task::TaskType::Error,
Tr::tr("Executable does not exist: %1")
.arg(exe.toUserOutput())});
return false;
}
if (!exe.toFileInfo().isExecutable()) {
ProjectExplorer::TaskHub::addTask(
ProjectExplorer::BuildSystemTask{ProjectExplorer::Task::TaskType::Error,
Tr::tr("Command is not executable: %1")
.arg(exe.toUserOutput())});
return false;
}
return true;
}
void MesonProcess::processStandardOutput()
{
const auto data = m_process->readAllRawStandardOutput();
MessageManager::writeSilently(QString::fromLocal8Bit(data));
emit readyReadStandardOutput(data);
}
void MesonProcess::processStandardError()
{
MessageManager::writeSilently(QString::fromLocal8Bit(m_process->readAllRawStandardError()));
}
} // namespace Internal
} // namespace MesonProjectManager

View File

@@ -1,54 +0,0 @@
// Copyright (C) 2020 Alexis Jeandet.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QByteArray>
#include <QElapsedTimer>
#include <QObject>
#include <QProcess>
#include <memory>
namespace Utils {
class Environment;
class Process;
}
namespace MesonProjectManager {
namespace Internal {
class Command;
class MesonProcess final : public QObject
{
Q_OBJECT
public:
MesonProcess();
~MesonProcess();
bool run(const Command &command, const Utils::Environment &env,
const QString &projectName, bool captureStdo = false);
const QByteArray &stdOut() const { return m_stdo; }
const QByteArray &stdErr() const { return m_stderr; }
signals:
void finished(int exitCode, QProcess::ExitStatus exitStatus);
void readyReadStandardOutput(const QByteArray &data);
private:
void handleProcessDone();
void setupProcess(const Command &command, const Utils::Environment &env,
const QString &projectName, bool captureStdo);
bool sanityCheck(const Command &command) const;
void processStandardOutput();
void processStandardError();
std::unique_ptr<Utils::Process> m_process;
QElapsedTimer m_elapsed;
QByteArray m_stdo;
QByteArray m_stderr;
};
} // namespace Internal
} // namespace MesonProjectManager

View File

@@ -49,8 +49,6 @@ Project {
"mesonbuildconfiguration.h", "mesonbuildconfiguration.h",
"mesonbuildsystem.cpp", "mesonbuildsystem.cpp",
"mesonbuildsystem.h", "mesonbuildsystem.h",
"mesonprocess.cpp",
"mesonprocess.h",
"mesonproject.cpp", "mesonproject.cpp",
"mesonproject.h", "mesonproject.h",
"mesonprojectimporter.cpp", "mesonprojectimporter.cpp",

View File

@@ -4,33 +4,44 @@
#include "mesonprojectparser.h" #include "mesonprojectparser.h"
#include "mesoninfoparser.h" #include "mesoninfoparser.h"
#include "mesonprojectmanagertr.h"
#include "mesonprojectnodes.h" #include "mesonprojectnodes.h"
#include "mesontools.h" #include "mesontools.h"
#include "projecttree.h" #include "projecttree.h"
#include <coreplugin/messagemanager.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/taskhub.h>
#include <utils/async.h> #include <utils/async.h>
#include <utils/environment.h>
#include <utils/fileinprojectfinder.h> #include <utils/fileinprojectfinder.h>
#include <utils/stringutils.h>
#include <QStringList>
#include <QTextStream>
#include <optional> #include <optional>
#include <coreplugin/progressmanager/processprogress.h>
using namespace Core;
using namespace ProjectExplorer;
using namespace Utils;
namespace MesonProjectManager { namespace MesonProjectManager {
namespace Internal { namespace Internal {
static Q_LOGGING_CATEGORY(mesonProcessLog, "qtc.meson.buildsystem", QtWarningMsg);
struct CompilerArgs struct CompilerArgs
{ {
QStringList args; QStringList args;
QStringList includePaths; QStringList includePaths;
ProjectExplorer::Macros macros; Macros macros;
}; };
inline std::optional<QString> extractValueIfMatches(const QString &arg, static std::optional<QString> extractValueIfMatches(const QString &arg,
const QStringList &candidates) const QStringList &candidates)
{ {
for (const auto &flag : candidates) { for (const auto &flag : candidates) {
@@ -40,22 +51,23 @@ inline std::optional<QString> extractValueIfMatches(const QString &arg,
return std::nullopt; return std::nullopt;
} }
inline std::optional<QString> extractInclude(const QString &arg) static std::optional<QString> extractInclude(const QString &arg)
{ {
return extractValueIfMatches(arg, {"-I", "/I", "-isystem", "-imsvc", "/imsvc"}); return extractValueIfMatches(arg, {"-I", "/I", "-isystem", "-imsvc", "/imsvc"});
} }
inline std::optional<ProjectExplorer::Macro> extractMacro(const QString &arg)
static std::optional<Macro> extractMacro(const QString &arg)
{ {
auto define = extractValueIfMatches(arg, {"-D", "/D"}); auto define = extractValueIfMatches(arg, {"-D", "/D"});
if (define) if (define)
return ProjectExplorer::Macro::fromKeyValue(define->toLatin1()); return Macro::fromKeyValue(define->toLatin1());
auto undef = extractValueIfMatches(arg, {"-U", "/U"}); auto undef = extractValueIfMatches(arg, {"-U", "/U"});
if (undef) if (undef)
return ProjectExplorer::Macro(undef->toLatin1(), ProjectExplorer::MacroType::Undefine); return Macro(undef->toLatin1(), MacroType::Undefine);
return std::nullopt; return std::nullopt;
} }
CompilerArgs splitArgs(const QStringList &args) static CompilerArgs splitArgs(const QStringList &args)
{ {
CompilerArgs splited; CompilerArgs splited;
for (const QString &arg : args) { for (const QString &arg : args) {
@@ -74,7 +86,7 @@ CompilerArgs splitArgs(const QStringList &args)
return splited; return splited;
} }
QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList) static QStringList toAbsolutePath(const FilePath &refPath, QStringList &pathList)
{ {
QStringList allAbs; QStringList allAbs;
std::transform(std::cbegin(pathList), std::transform(std::cbegin(pathList),
@@ -86,35 +98,22 @@ QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList
return allAbs; return allAbs;
} }
MesonProjectParser::MesonProjectParser(const Utils::Id &meson, MesonProjectParser::MesonProjectParser(const Id &meson, const Environment &env, Project *project)
Utils::Environment env,
ProjectExplorer::Project *project)
: m_env{env} : m_env{env}
, m_meson{meson} , m_meson{meson}
, m_projectName{project->displayName()} , m_projectName{project->displayName()}
{ {
connect(&m_process, &MesonProcess::finished, this, &MesonProjectParser::processFinished);
connect(&m_process,
&MesonProcess::readyReadStandardOutput,
&m_outputParser,
&MesonOutputParser::readStdo);
// TODO re-think the way all BuildSystem/ProjectParser are tied // TODO re-think the way all BuildSystem/ProjectParser are tied
// I take project info here, I also take build and src dir later from // I take project info here, I also take build and src dir later from
// functions args. // functions args.
auto fileFinder = new Utils::FileInProjectFinder; auto fileFinder = new FileInProjectFinder;
fileFinder->setProjectDirectory(project->projectDirectory()); fileFinder->setProjectDirectory(project->projectDirectory());
fileFinder->setProjectFiles(project->files(ProjectExplorer::Project::AllFiles)); fileFinder->setProjectFiles(project->files(Project::AllFiles));
m_outputParser.setFileFinder(fileFinder); m_outputParser.setFileFinder(fileFinder);
} }
void MesonProjectParser::setMesonTool(const Utils::Id &meson) bool MesonProjectParser::configure(const FilePath &sourcePath,
{ const FilePath &buildPath,
m_meson = meson;
}
bool MesonProjectParser::configure(const Utils::FilePath &sourcePath,
const Utils::FilePath &buildPath,
const QStringList &args) const QStringList &args)
{ {
m_introType = IntroDataType::file; m_introType = IntroDataType::file;
@@ -126,18 +125,18 @@ bool MesonProjectParser::configure(const Utils::FilePath &sourcePath,
m_pendingCommands.enqueue( m_pendingCommands.enqueue(
std::make_tuple(MesonTools::mesonWrapper(m_meson)->regenerate(sourcePath, buildPath), std::make_tuple(MesonTools::mesonWrapper(m_meson)->regenerate(sourcePath, buildPath),
false)); false));
return m_process.run(cmd, m_env, m_projectName); return run(cmd, m_env, m_projectName);
} }
bool MesonProjectParser::wipe(const Utils::FilePath &sourcePath, bool MesonProjectParser::wipe(const FilePath &sourcePath,
const Utils::FilePath &buildPath, const FilePath &buildPath,
const QStringList &args) const QStringList &args)
{ {
return setup(sourcePath, buildPath, args, true); return setup(sourcePath, buildPath, args, true);
} }
bool MesonProjectParser::setup(const Utils::FilePath &sourcePath, bool MesonProjectParser::setup(const FilePath &sourcePath,
const Utils::FilePath &buildPath, const FilePath &buildPath,
const QStringList &args, const QStringList &args,
bool forceWipe) bool forceWipe)
{ {
@@ -149,10 +148,10 @@ bool MesonProjectParser::setup(const Utils::FilePath &sourcePath,
if (forceWipe || isSetup(buildPath)) if (forceWipe || isSetup(buildPath))
cmdArgs << "--wipe"; cmdArgs << "--wipe";
auto cmd = MesonTools::mesonWrapper(m_meson)->setup(sourcePath, buildPath, cmdArgs); auto cmd = MesonTools::mesonWrapper(m_meson)->setup(sourcePath, buildPath, cmdArgs);
return m_process.run(cmd, m_env, m_projectName); return run(cmd, m_env, m_projectName);
} }
bool MesonProjectParser::parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath) bool MesonProjectParser::parse(const FilePath &sourcePath, const FilePath &buildPath)
{ {
m_srcDir = sourcePath; m_srcDir = sourcePath;
m_buildDir = buildPath; m_buildDir = buildPath;
@@ -165,29 +164,29 @@ bool MesonProjectParser::parse(const Utils::FilePath &sourcePath, const Utils::F
} }
} }
bool MesonProjectParser::parse(const Utils::FilePath &sourcePath) bool MesonProjectParser::parse(const FilePath &sourcePath)
{ {
m_srcDir = sourcePath; m_srcDir = sourcePath;
m_introType = IntroDataType::stdo; m_introType = IntroDataType::stdo;
m_outputParser.setSourceDirectory(sourcePath); m_outputParser.setSourceDirectory(sourcePath);
return m_process.run(MesonTools::mesonWrapper(m_meson)->introspect(sourcePath), return run(MesonTools::mesonWrapper(m_meson)->introspect(sourcePath),
m_env, m_env,
m_projectName, m_projectName,
true); true);
} }
QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const QList<BuildTargetInfo> MesonProjectParser::appsTargets() const
{ {
QList<ProjectExplorer::BuildTargetInfo> apps; QList<BuildTargetInfo> apps;
for (const Target &target : m_parserResult.targets) { for (const Target &target : m_parserResult.targets) {
if (target.type == Target::Type::executable) { if (target.type == Target::Type::executable) {
ProjectExplorer::BuildTargetInfo bti; BuildTargetInfo bti;
bti.displayName = target.name; bti.displayName = target.name;
bti.buildKey = Target::fullName(m_buildDir, target); bti.buildKey = Target::fullName(m_buildDir, target);
bti.displayNameUniquifier = bti.buildKey; bti.displayNameUniquifier = bti.buildKey;
bti.targetFilePath = Utils::FilePath::fromString(target.fileName.first()); bti.targetFilePath = FilePath::fromString(target.fileName.first());
bti.workingDirectory = Utils::FilePath::fromString(target.fileName.first()).absolutePath(); bti.workingDirectory = FilePath::fromString(target.fileName.first()).absolutePath();
bti.projectFilePath = Utils::FilePath::fromString(target.definedIn); bti.projectFilePath = FilePath::fromString(target.definedIn);
bti.usesTerminal = true; bti.usesTerminal = true;
apps.append(bti); apps.append(bti);
} }
@@ -198,8 +197,8 @@ QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const
bool MesonProjectParser::startParser() bool MesonProjectParser::startParser()
{ {
m_parserFutureResult = Utils::asyncRun( m_parserFutureResult = Utils::asyncRun(
ProjectExplorer::ProjectExplorerPlugin::sharedThreadPool(), ProjectExplorerPlugin::sharedThreadPool(),
[processOutput = m_process.stdOut(), introType = m_introType, [processOutput = m_stdo, introType = m_introType,
buildDir = m_buildDir, srcDir = m_srcDir] { buildDir = m_buildDir, srcDir = m_srcDir] {
if (introType == IntroDataType::file) if (introType == IntroDataType::file)
return extractParserResults(srcDir, MesonInfoParser::parse(buildDir)); return extractParserResults(srcDir, MesonInfoParser::parse(buildDir));
@@ -212,7 +211,7 @@ bool MesonProjectParser::startParser()
} }
MesonProjectParser::ParserData *MesonProjectParser::extractParserResults( MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
const Utils::FilePath &srcDir, MesonInfoParser::Result &&parserResult) const FilePath &srcDir, MesonInfoParser::Result &&parserResult)
{ {
auto rootNode = ProjectTree::buildTree(srcDir, auto rootNode = ProjectTree::buildTree(srcDir,
parserResult.targets, parserResult.targets,
@@ -220,15 +219,22 @@ MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
return new ParserData{std::move(parserResult), std::move(rootNode)}; return new ParserData{std::move(parserResult), std::move(rootNode)};
} }
void MesonProjectParser::addMissingTargets(QStringList &targetList) static void addMissingTargets(QStringList &targetList)
{ {
// Not all targets are listed in introspection data // Not all targets are listed in introspection data
for (const auto &target : additionalTargets()) { static const QString additionalTargets[] {
if (!targetList.contains(target)) { Constants::Targets::all,
Constants::Targets::clean,
Constants::Targets::install,
Constants::Targets::benchmark,
Constants::Targets::scan_build
};
for (const QString &target : additionalTargets) {
if (!targetList.contains(target))
targetList.append(target); targetList.append(target);
} }
} }
}
void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *> &data) void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *> &data)
{ {
@@ -245,17 +251,17 @@ void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *>
emit parsingCompleted(true); emit parsingCompleted(true);
} }
ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart( RawProjectPart MesonProjectParser::buildRawPart(
const Target &target, const Target &target,
const Target::SourceGroup &sources, const Target::SourceGroup &sources,
const ProjectExplorer::ToolChain *cxxToolChain, const ToolChain *cxxToolChain,
const ProjectExplorer::ToolChain *cToolChain) const ToolChain *cToolChain)
{ {
ProjectExplorer::RawProjectPart part; RawProjectPart part;
part.setDisplayName(target.name); part.setDisplayName(target.name);
part.setBuildSystemTarget(Target::fullName(m_buildDir, target)); part.setBuildSystemTarget(Target::fullName(m_buildDir, target));
part.setFiles(sources.sources + sources.generatedSources); part.setFiles(sources.sources + sources.generatedSources);
auto flags = splitArgs(sources.parameters); CompilerArgs flags = splitArgs(sources.parameters);
part.setMacros(flags.macros); part.setMacros(flags.macros);
part.setIncludePaths(toAbsolutePath(m_buildDir, flags.includePaths)); part.setIncludePaths(toAbsolutePath(m_buildDir, flags.includePaths));
part.setProjectFileLocation(target.definedIn); part.setProjectFileLocation(target.definedIn);
@@ -267,30 +273,10 @@ ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart(
return part; return part;
} }
void MesonProjectParser::processFinished(int exitCode, QProcess::ExitStatus exitStatus) RawProjectParts MesonProjectParser::buildProjectParts(
const ToolChain *cxxToolChain, const ToolChain *cToolChain)
{ {
if (exitCode == 0 && exitStatus == QProcess::NormalExit) { RawProjectParts parts;
if (m_pendingCommands.isEmpty())
startParser();
else {
// see comment near m_pendingCommands declaration
std::tuple<Command, bool> args = m_pendingCommands.dequeue();
m_process.run(std::get<0>(args), m_env, m_projectName, std::get<1>(args));
}
} else {
if (m_introType == IntroDataType::stdo) {
auto data = m_process.stdErr();
Core::MessageManager::writeSilently(QString::fromLocal8Bit(data));
m_outputParser.readStdo(data);
}
emit parsingCompleted(false);
}
}
ProjectExplorer::RawProjectParts MesonProjectParser::buildProjectParts(
const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cToolChain)
{
ProjectExplorer::RawProjectParts parts;
for_each_source_group(m_parserResult.targets, for_each_source_group(m_parserResult.targets,
[&parts, [&parts,
&cxxToolChain, &cxxToolChain,
@@ -321,11 +307,111 @@ bool MesonProjectParser::matchesKit(const KitData &kit)
return matches; return matches;
} }
bool MesonProjectParser::usesSameMesonVersion(const Utils::FilePath &buildPath) bool MesonProjectParser::usesSameMesonVersion(const FilePath &buildPath)
{ {
auto info = MesonInfoParser::mesonInfo(buildPath); auto info = MesonInfoParser::mesonInfo(buildPath);
auto meson = MesonTools::mesonWrapper(m_meson); auto meson = MesonTools::mesonWrapper(m_meson);
return info && meson && info->mesonVersion == meson->version(); return info && meson && info->mesonVersion == meson->version();
} }
bool MesonProjectParser::run(const Command &command,
const Environment &env,
const QString &projectName,
bool captureStdo)
{
if (!sanityCheck(command))
return false;
m_stdo.clear();
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
setupProcess(command, env, projectName, captureStdo);
m_elapsed.start();
m_process->start();
qCDebug(mesonProcessLog()) << "Starting:" << command.toUserOutput();
return true;
}
void MesonProjectParser::handleProcessDone()
{
if (m_process->result() != ProcessResult::FinishedWithSuccess)
TaskHub::addTask(BuildSystemTask{Task::TaskType::Error, m_process->exitMessage()});
m_stdo = m_process->readAllRawStandardOutput();
m_stderr = m_process->readAllRawStandardError();
const QString elapsedTime = formatElapsedTime(m_elapsed.elapsed());
MessageManager::writeSilently(elapsedTime);
if (m_process->exitCode() == 0 && m_process->exitStatus() == QProcess::NormalExit) {
if (m_pendingCommands.isEmpty())
startParser();
else {
// see comment near m_pendingCommands declaration
std::tuple<Command, bool> args = m_pendingCommands.dequeue();
run(std::get<0>(args), m_env, m_projectName, std::get<1>(args));
}
} else {
if (m_introType == IntroDataType::stdo) {
MessageManager::writeSilently(QString::fromLocal8Bit(m_stderr));
m_outputParser.readStdo(m_stderr);
}
emit parsingCompleted(false);
}
}
void MesonProjectParser::setupProcess(const Command &command, const Environment &env,
const QString &projectName, bool captureStdo)
{
if (m_process)
m_process.release()->deleteLater();
m_process.reset(new Process);
connect(m_process.get(), &Process::done, this, &MesonProjectParser::handleProcessDone);
if (!captureStdo) {
connect(m_process.get(), &Process::readyReadStandardOutput,
this, &MesonProjectParser::processStandardOutput);
connect(m_process.get(), &Process::readyReadStandardError,
this, &MesonProjectParser::processStandardError);
}
m_process->setWorkingDirectory(command.workDir());
m_process->setEnvironment(env);
MessageManager::writeFlashing(Tr::tr("Running %1 in %2.")
.arg(command.toUserOutput(), command.workDir().toUserOutput()));
m_process->setCommand(command.cmdLine());
m_process->setTimeoutS(10);
ProcessProgress *progress = new ProcessProgress(m_process.get());
progress->setDisplayName(Tr::tr("Configuring \"%1\".").arg(projectName));
}
bool MesonProjectParser::sanityCheck(const Command &command) const
{
const auto &exe = command.cmdLine().executable();
if (!exe.exists()) {
//Should only reach this point if Meson exe is removed while a Meson project is opened
TaskHub::addTask(
BuildSystemTask{Task::TaskType::Error,
Tr::tr("Executable does not exist: %1").arg(exe.toUserOutput())});
return false;
}
if (!exe.toFileInfo().isExecutable()) {
TaskHub::addTask(
BuildSystemTask{Task::TaskType::Error,
Tr::tr("Command is not executable: %1").arg(exe.toUserOutput())});
return false;
}
return true;
}
void MesonProjectParser::processStandardOutput()
{
const auto data = m_process->readAllRawStandardOutput();
MessageManager::writeSilently(QString::fromLocal8Bit(data));
m_outputParser.readStdo(data);
}
void MesonProjectParser::processStandardError()
{
MessageManager::writeSilently(QString::fromLocal8Bit(m_process->readAllRawStandardError()));
}
} // namespace Internal } // namespace Internal
} // namespace MesonProjectManager } // namespace MesonProjectManager

View File

@@ -6,7 +6,6 @@
#include "kitdata.h" #include "kitdata.h"
#include "mesoninfoparser.h" #include "mesoninfoparser.h"
#include "mesonoutputparser.h" #include "mesonoutputparser.h"
#include "mesonprocess.h"
#include "mesonprojectnodes.h" #include "mesonprojectnodes.h"
#include "mesonwrapper.h" #include "mesonwrapper.h"
@@ -14,9 +13,6 @@
#include <projectexplorer/kit.h> #include <projectexplorer/kit.h>
#include <projectexplorer/rawprojectpart.h> #include <projectexplorer/rawprojectpart.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <QFuture> #include <QFuture>
#include <QQueue> #include <QQueue>
@@ -26,6 +22,7 @@ namespace Internal {
class MesonProjectParser : public QObject class MesonProjectParser : public QObject
{ {
Q_OBJECT Q_OBJECT
enum class IntroDataType { file, stdo }; enum class IntroDataType { file, stdo };
struct ParserData struct ParserData
{ {
@@ -34,8 +31,10 @@ class MesonProjectParser : public QObject
}; };
public: public:
MesonProjectParser(const Utils::Id &meson, Utils::Environment env, ProjectExplorer::Project* project); MesonProjectParser(const Utils::Id &meson,
void setMesonTool(const Utils::Id &meson); const Utils::Environment &env,
ProjectExplorer::Project *project);
bool configure(const Utils::FilePath &sourcePath, bool configure(const Utils::FilePath &sourcePath,
const Utils::FilePath &buildPath, const Utils::FilePath &buildPath,
const QStringList &args); const QStringList &args);
@@ -49,22 +48,11 @@ public:
bool parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath); bool parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath);
bool parse(const Utils::FilePath &sourcePath); bool parse(const Utils::FilePath &sourcePath);
Q_SIGNAL void parsingCompleted(bool success);
std::unique_ptr<MesonProjectNode> takeProjectNode() { return std::move(m_rootNode); } std::unique_ptr<MesonProjectNode> takeProjectNode() { return std::move(m_rootNode); }
inline const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; }; const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; };
inline const TargetsList &targets() const { return m_parserResult.targets; } const TargetsList &targets() const { return m_parserResult.targets; }
inline const QStringList &targetsNames() const { return m_targetsNames; } const QStringList &targetsNames() const { return m_targetsNames; }
static inline QStringList additionalTargets()
{
return QStringList{Constants::Targets::all,
Constants::Targets::clean,
Constants::Targets::install,
Constants::Targets::benchmark,
Constants::Targets::scan_build};
}
QList<ProjectExplorer::BuildTargetInfo> appsTargets() const; QList<ProjectExplorer::BuildTargetInfo> appsTargets() const;
@@ -72,26 +60,27 @@ public:
const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cxxToolChain,
const ProjectExplorer::ToolChain *cToolChain); const ProjectExplorer::ToolChain *cToolChain);
inline void setEnvironment(const Utils::Environment &environment) { m_env = environment; } void setEnvironment(const Utils::Environment &environment) { m_env = environment; }
inline void setQtVersion(Utils::QtMajorVersion v) { m_qtVersion = v; } void setQtVersion(Utils::QtMajorVersion v) { m_qtVersion = v; }
bool matchesKit(const KitData &kit); bool matchesKit(const KitData &kit);
bool usesSameMesonVersion(const Utils::FilePath &buildPath); bool usesSameMesonVersion(const Utils::FilePath &buildPath);
signals:
void parsingCompleted(bool success);
private: private:
bool startParser(); bool startParser();
static ParserData *extractParserResults(const Utils::FilePath &srcDir, static ParserData *extractParserResults(const Utils::FilePath &srcDir,
MesonInfoParser::Result &&parserResult); MesonInfoParser::Result &&parserResult);
static void addMissingTargets(QStringList &targetList);
void update(const QFuture<ParserData *> &data); void update(const QFuture<ParserData *> &data);
ProjectExplorer::RawProjectPart buildRawPart(const Target &target, ProjectExplorer::RawProjectPart buildRawPart(const Target &target,
const Target::SourceGroup &sources, const Target::SourceGroup &sources,
const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cxxToolChain,
const ProjectExplorer::ToolChain *cToolChain); const ProjectExplorer::ToolChain *cToolChain);
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
MesonProcess m_process;
MesonOutputParser m_outputParser; MesonOutputParser m_outputParser;
Utils::Environment m_env; Utils::Environment m_env;
Utils::Id m_meson; Utils::Id m_meson;
@@ -108,6 +97,22 @@ private:
// maybe moving meson to build step could make this class simpler // maybe moving meson to build step could make this class simpler
// also this should ease command dependencies // also this should ease command dependencies
QQueue<std::tuple<Command, bool>> m_pendingCommands; QQueue<std::tuple<Command, bool>> m_pendingCommands;
bool run(const Command &command, const Utils::Environment &env,
const QString &projectName, bool captureStdo = false);
void handleProcessDone();
void setupProcess(const Command &command, const Utils::Environment &env,
const QString &projectName, bool captureStdo);
bool sanityCheck(const Command &command) const;
void processStandardOutput();
void processStandardError();
std::unique_ptr<Utils::Process> m_process;
QElapsedTimer m_elapsed;
QByteArray m_stdo;
QByteArray m_stderr;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -12,36 +12,20 @@
#include "toolssettingsaccessor.h" #include "toolssettingsaccessor.h"
#include "toolssettingspage.h" #include "toolssettingspage.h"
#include <coreplugin/icore.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmanager.h> #include <projectexplorer/projectmanager.h>
#include <projectexplorer/runcontrol.h> #include <projectexplorer/runcontrol.h>
#include <utils/fsengine/fileiconprovider.h> #include <utils/fsengine/fileiconprovider.h>
using namespace Core;
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Utils; using namespace Utils;
namespace MesonProjectManager::Internal { namespace MesonProjectManager::Internal {
class MesonProjectPluginPrivate : public QObject class MesonProjectPluginPrivate
{ {
Q_OBJECT
public: public:
MesonProjectPluginPrivate()
{
MesonTools::setTools(m_toolsSettings.loadMesonTools(ICore::dialogParent()));
connect(ICore::instance(),
&ICore::saveSettingsRequested,
this,
&MesonProjectPluginPrivate::saveAll);
}
~MesonProjectPluginPrivate() {}
private:
ToolsSettingsPage m_toolslSettingsPage; ToolsSettingsPage m_toolslSettingsPage;
ToolsSettingsAccessor m_toolsSettings; ToolsSettingsAccessor m_toolsSettings;
MesonBuildStepFactory m_buildStepFactory; MesonBuildStepFactory m_buildStepFactory;
@@ -50,11 +34,6 @@ private:
MesonActionsManager m_actions; MesonActionsManager m_actions;
MachineFileManager m_machineFilesManager; MachineFileManager m_machineFilesManager;
SimpleTargetRunnerFactory m_mesonRunWorkerFactory{{m_runConfigurationFactory.runConfigurationId()}}; SimpleTargetRunnerFactory m_mesonRunWorkerFactory{{m_runConfigurationFactory.runConfigurationId()}};
void saveAll()
{
m_toolsSettings.saveMesonTools(MesonTools::tools(), ICore::dialogParent());
}
}; };
MesonProjectPlugin::~MesonProjectPlugin() MesonProjectPlugin::~MesonProjectPlugin()
@@ -72,5 +51,3 @@ void MesonProjectPlugin::initialize()
} }
} // MesonProjectManager::Internal } // MesonProjectManager::Internal
#include "mesonprojectplugin.moc"

View File

@@ -6,6 +6,7 @@
#include "mesonpluginconstants.h" #include "mesonpluginconstants.h"
#include "mesonprojectmanagertr.h" #include "mesonprojectmanagertr.h"
#include <coreplugin/icore.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <utils/filepath.h> #include <utils/filepath.h>
@@ -16,6 +17,7 @@
#include <iterator> #include <iterator>
#include <vector> #include <vector>
using namespace Core;
using namespace Utils; using namespace Utils;
namespace MesonProjectManager { namespace MesonProjectManager {
@@ -30,11 +32,16 @@ ToolsSettingsAccessor::ToolsSettingsAccessor()
{ {
setDocType("QtCreatorMesonTools"); setDocType("QtCreatorMesonTools");
setApplicationDisplayName(QGuiApplication::applicationDisplayName()); setApplicationDisplayName(QGuiApplication::applicationDisplayName());
setBaseFilePath(Core::ICore::userResourcePath(Constants::ToolsSettings::FILENAME)); setBaseFilePath(ICore::userResourcePath(Constants::ToolsSettings::FILENAME));
MesonTools::setTools(loadMesonTools());
QObject::connect(ICore::instance(), &ICore::saveSettingsRequested, [this] {
saveMesonTools(MesonTools::tools());
});
} }
void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t> &tools, void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t> &tools)
QWidget *parent)
{ {
using namespace Constants; using namespace Constants;
Store data; Store data;
@@ -51,13 +58,13 @@ void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t>
entry_count++; entry_count++;
} }
data.insert(ToolsSettings::ENTRY_COUNT, entry_count); data.insert(ToolsSettings::ENTRY_COUNT, entry_count);
saveSettings(data, parent); saveSettings(data, ICore::dialogParent());
} }
std::vector<MesonTools::Tool_t> ToolsSettingsAccessor::loadMesonTools(QWidget *parent) std::vector<MesonTools::Tool_t> ToolsSettingsAccessor::loadMesonTools()
{ {
using namespace Constants; using namespace Constants;
auto data = restoreSettings(parent); auto data = restoreSettings(ICore::dialogParent());
auto entry_count = data.value(ToolsSettings::ENTRY_COUNT, 0).toInt(); auto entry_count = data.value(ToolsSettings::ENTRY_COUNT, 0).toInt();
std::vector<MesonTools::Tool_t> result; std::vector<MesonTools::Tool_t> result;
for (auto toolIndex = 0; toolIndex < entry_count; toolIndex++) { for (auto toolIndex = 0; toolIndex < entry_count; toolIndex++) {

View File

@@ -14,8 +14,9 @@ class ToolsSettingsAccessor final : public Utils::UpgradingSettingsAccessor
{ {
public: public:
ToolsSettingsAccessor(); ToolsSettingsAccessor();
void saveMesonTools(const std::vector<MesonTools::Tool_t> &tools, QWidget *parent);
std::vector<MesonTools::Tool_t> loadMesonTools(QWidget *parent); void saveMesonTools(const std::vector<MesonTools::Tool_t> &tools);
std::vector<MesonTools::Tool_t> loadMesonTools();
}; };
} // namespace Internal } // namespace Internal

View File

@@ -77,6 +77,7 @@ bool SshParameters::setupSshEnvironment(Process *process)
const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0")); const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0"));
if (SshSettings::askpassFilePath().exists()) { if (SshSettings::askpassFilePath().exists()) {
env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput()); env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput());
env.set("SSH_ASKPASS_REQUIRE", "force");
// OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform. // OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform.
if (!env.hasKey("DISPLAY")) if (!env.hasKey("DISPLAY"))

View File

@@ -86,20 +86,36 @@ void PySideInstaller::installPyside(const FilePath &python,
{ {
QMap<QVersionNumber, Utils::FilePath> availablePySides; QMap<QVersionNumber, Utils::FilePath> availablePySides;
const QString hostQtTail = HostOsInfo::isMacHost() ? QString("Tools/sdktool") const Utils::QtcSettings *settings = Core::ICore::settings(QSettings::SystemScope);
const FilePaths requirementsList
= Utils::transform(settings->value("Python/PySideWheelsRequirements").toList(),
&FilePath::fromSettings);
for (const FilePath &requirements : requirementsList) {
if (requirements.exists()) {
auto version = QVersionNumber::fromString(requirements.parentDir().fileName());
availablePySides[version] = requirements;
}
}
if (requirementsList.isEmpty()) { // fallback remove in Qt Creator 13
const QString hostQtTail = HostOsInfo::isMacHost()
? QString("Tools/sdktool")
: QString("Tools/sdktool/share/qtcreator"); : QString("Tools/sdktool/share/qtcreator");
const std::optional<FilePath> qtInstallDir const std::optional<FilePath> qtInstallDir
= QtSupport::LinkWithQtSupport::linkedQt().tailRemoved(hostQtTail); = QtSupport::LinkWithQtSupport::linkedQt().tailRemoved(hostQtTail);
if (qtInstallDir) { if (qtInstallDir) {
const FilePath qtForPythonDir = qtInstallDir->pathAppended("QtForPython"); const FilePath qtForPythonDir = qtInstallDir->pathAppended("QtForPython");
for (const FilePath &versionDir : qtForPythonDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot)) { for (const FilePath &versionDir :
qtForPythonDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot)) {
FilePath requirements = versionDir.pathAppended("requirements.txt"); FilePath requirements = versionDir.pathAppended("requirements.txt");
if (requirements.exists()) if (!requirementsList.contains(requirements) && requirements.exists())
availablePySides[QVersionNumber::fromString(versionDir.fileName())] = requirements; availablePySides[QVersionNumber::fromString(versionDir.fileName())]
= requirements;
}
} }
} }
auto install = new PipInstallTask(python); auto install = new PipInstallTask(python);
connect(install, &PipInstallTask::finished, install, &QObject::deleteLater); connect(install, &PipInstallTask::finished, install, &QObject::deleteLater);

View File

@@ -21,13 +21,14 @@
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/qtcassert.h> #include <utils/async.h>
#include <utils/detailswidget.h> #include <utils/detailswidget.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/listmodel.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/listmodel.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/process.h> #include <utils/process.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h> #include <utils/treemodel.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
@@ -366,14 +367,6 @@ private:
InterpreterOptionsWidget *m_widget = nullptr; InterpreterOptionsWidget *m_widget = nullptr;
}; };
static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable)
{
return Utils::anyOf(pythons, [pythonExecutable](const Interpreter &interpreter) {
return interpreter.command.toFileInfo().canonicalFilePath()
== pythonExecutable.toFileInfo().canonicalFilePath();
});
}
static InterpreterOptionsPage &interpreterOptionsPage() static InterpreterOptionsPage &interpreterOptionsPage()
{ {
static InterpreterOptionsPage page; static InterpreterOptionsPage page;
@@ -626,8 +619,9 @@ static void disableOutdatedPyls()
} }
} }
static void addPythonsFromRegistry(QList<Interpreter> &pythons) static QList<Interpreter> pythonsFromRegistry()
{ {
QList<Interpreter> pythons;
QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore", QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore",
QSettings::NativeFormat); QSettings::NativeFormat);
for (const QString &versionGroup : pythonRegistry.childGroups()) { for (const QString &versionGroup : pythonRegistry.childGroups()) {
@@ -636,7 +630,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath"); QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath");
if (regVal.isValid()) { if (regVal.isValid()) {
const FilePath &executable = FilePath::fromUserInput(regVal.toString()); const FilePath &executable = FilePath::fromUserInput(regVal.toString());
if (executable.exists() && !alreadyRegistered(pythons, executable)) { if (executable.exists()) {
pythons << Interpreter{QUuid::createUuid().toString(), pythons << Interpreter{QUuid::createUuid().toString(),
name, name,
FilePath::fromUserInput(regVal.toString())}; FilePath::fromUserInput(regVal.toString())};
@@ -645,7 +639,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath"); regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath");
if (regVal.isValid()) { if (regVal.isValid()) {
const FilePath &executable = FilePath::fromUserInput(regVal.toString()); const FilePath &executable = FilePath::fromUserInput(regVal.toString());
if (executable.exists() && !alreadyRegistered(pythons, executable)) { if (executable.exists()) {
pythons << Interpreter{QUuid::createUuid().toString(), pythons << Interpreter{QUuid::createUuid().toString(),
//: <python display name> (Windowed) //: <python display name> (Windowed)
Tr::tr("%1 (Windowed)").arg(name), Tr::tr("%1 (Windowed)").arg(name),
@@ -656,28 +650,30 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
if (regVal.isValid()) { if (regVal.isValid()) {
const FilePath &path = FilePath::fromUserInput(regVal.toString()); const FilePath &path = FilePath::fromUserInput(regVal.toString());
const FilePath python = path.pathAppended("python").withExecutableSuffix(); const FilePath python = path.pathAppended("python").withExecutableSuffix();
if (python.exists() && !alreadyRegistered(pythons, python)) if (python.exists())
pythons << createInterpreter(python, "Python " + versionGroup); pythons << createInterpreter(python, "Python " + versionGroup);
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix(); const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
if (pythonw.exists() && !alreadyRegistered(pythons, pythonw)) if (pythonw.exists())
pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)"); pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)");
} }
pythonRegistry.endGroup(); pythonRegistry.endGroup();
} }
return pythons;
} }
static void addPythonsFromPath(QList<Interpreter> &pythons) static QList<Interpreter> pythonsFromPath()
{ {
QList<Interpreter> pythons;
if (HostOsInfo::isWindowsHost()) { if (HostOsInfo::isWindowsHost()) {
for (const FilePath &executable : FilePath("python").searchAllInPath()) { for (const FilePath &executable : FilePath("python").searchAllInPath()) {
// Windows creates empty redirector files that may interfere // Windows creates empty redirector files that may interfere
if (executable.toFileInfo().size() == 0) if (executable.toFileInfo().size() == 0)
continue; continue;
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists())
pythons << createInterpreter(executable, "Python from Path"); pythons << createInterpreter(executable, "Python from Path");
} }
for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) { for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) {
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists())
pythons << createInterpreter(executable, "Python from Path", "(Windowed)"); pythons << createInterpreter(executable, "Python from Path", "(Windowed)");
} }
} else { } else {
@@ -690,11 +686,12 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
const QDir dir(path.toString()); const QDir dir(path.toString());
for (const QFileInfo &fi : dir.entryInfoList(filters)) { for (const QFileInfo &fi : dir.entryInfoList(filters)) {
const FilePath executable = Utils::FilePath::fromFileInfo(fi); const FilePath executable = Utils::FilePath::fromFileInfo(fi);
if (executable.exists() && !alreadyRegistered(pythons, executable)) if (executable.exists())
pythons << createInterpreter(executable, "Python from Path"); pythons << createInterpreter(executable, "Python from Path");
} }
} }
} }
return pythons;
} }
static QString idForPythonFromPath(const QList<Interpreter> &pythons) static QString idForPythonFromPath(const QList<Interpreter> &pythons)
@@ -713,6 +710,51 @@ static QString idForPythonFromPath(const QList<Interpreter> &pythons)
static PythonSettings *settingsInstance = nullptr; static PythonSettings *settingsInstance = nullptr;
static bool alreadyRegistered(const Interpreter &candidate)
{
return Utils::anyOf(settingsInstance->interpreters(),
[candidate = candidate.command](const Interpreter &interpreter) {
return interpreter.command.isSameDevice(candidate)
&& interpreter.command.resolveSymlinks()
== candidate.resolveSymlinks();
});
}
static void scanPath()
{
auto watcher = new QFutureWatcher<QList<Interpreter>>();
QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() {
for (const Interpreter &interpreter : watcher->result()) {
if (!alreadyRegistered(interpreter))
settingsInstance->addInterpreter(interpreter);
}
watcher->deleteLater();
});
watcher->setFuture(Utils::asyncRun(pythonsFromPath));
}
static void scanRegistry()
{
auto watcher = new QFutureWatcher<QList<Interpreter>>();
QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() {
for (const Interpreter &interpreter : watcher->result()) {
if (!alreadyRegistered(interpreter))
settingsInstance->addInterpreter(interpreter);
}
watcher->deleteLater();
scanPath();
});
watcher->setFuture(Utils::asyncRun(pythonsFromRegistry));
}
static void scanSystemForInterpreters()
{
if (Utils::HostOsInfo::isWindowsHost())
scanRegistry();
else
scanPath();
}
PythonSettings::PythonSettings() PythonSettings::PythonSettings()
{ {
QTC_ASSERT(!settingsInstance, return); QTC_ASSERT(!settingsInstance, return);
@@ -723,9 +765,7 @@ PythonSettings::PythonSettings()
initFromSettings(Core::ICore::settings()); initFromSettings(Core::ICore::settings());
if (HostOsInfo::isWindowsHost()) scanSystemForInterpreters();
addPythonsFromRegistry(m_interpreters);
addPythonsFromPath(m_interpreters);
if (m_defaultInterpreterId.isEmpty()) if (m_defaultInterpreterId.isEmpty())
m_defaultInterpreterId = idForPythonFromPath(m_interpreters); m_defaultInterpreterId = idForPythonFromPath(m_interpreters);

View File

@@ -751,8 +751,10 @@ FilePath source(IDocument *document)
void setProcessEnvironment(Environment *e) void setProcessEnvironment(Environment *e)
{ {
const QString prompt = Internal::commonSettings().sshPasswordPrompt().path(); const QString prompt = Internal::commonSettings().sshPasswordPrompt().path();
if (!prompt.isEmpty()) if (!prompt.isEmpty()) {
e->set("SSH_ASKPASS", prompt); e->set("SSH_ASKPASS", prompt);
e->set("SSH_ASKPASS_REQUIRE", "force");
}
} }
} // namespace VcsBase } // namespace VcsBase

View File

@@ -4,7 +4,7 @@ project('mesonsampleproject', 'cpp',default_options : ['cpp_std=c++11'])
qt5 = import('qt5') qt5 = import('qt5')
qt5dep = dependency('qt5', modules : ['Core', 'Widgets']) qt5dep = dependency('qt5', modules : ['Core', 'Widgets'])
translations = qt5.compile_translations(ts_files : 'mesonsampleproject_fr_FR.ts', build_by_default : true) #translations = qt5.compile_translations(ts_files : 'mesonsampleproject_fr_FR.ts', build_by_default : true)
generated_files = qt5.preprocess( generated_files = qt5.preprocess(
moc_headers : 'mesonsampleproject.h', moc_headers : 'mesonsampleproject.h',

View File

@@ -126,7 +126,7 @@
:Qt Creator.DragDoc_QToolButton {toolTip='Drag to drag documents between splits' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.DragDoc_QToolButton {toolTip='Drag to drag documents between splits' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:Qt Creator.Events_QDockWidget {name='QmlProfiler.Statistics.DockDockWidget' type='QDockWidget' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.Events_QDockWidget {name='QmlProfiler.Statistics.DockDockWidget' type='QDockWidget' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:Qt Creator.Events_QTabBar {aboveWidget=':Qt Creator.Events_QDockWidget' type='QTabBar' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.Events_QTabBar {aboveWidget=':Qt Creator.Events_QDockWidget' type='QTabBar' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:Qt Creator.Issues_QListView {type='Utils::TreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'} :Qt Creator.Issues_QListView {type='QTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'}
:Qt Creator.Project.Menu.File_QMenu {name='Project.Menu.File' type='QMenu'} :Qt Creator.Project.Menu.File_QMenu {name='Project.Menu.File' type='QMenu'}
:Qt Creator.Project.Menu.Folder_QMenu {name='Project.Menu.Folder' type='QMenu' visible='1'} :Qt Creator.Project.Menu.Folder_QMenu {name='Project.Menu.Folder' type='QMenu' visible='1'}
:Qt Creator.QML debugging and profiling:_QComboBox {leftWidget=':Qt Creator.QML debugging and profiling:_QLabel' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.QML debugging and profiling:_QComboBox {leftWidget=':Qt Creator.QML debugging and profiling:_QLabel' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}

View File

@@ -81,8 +81,8 @@ def __createProjectOrFileSelectType__(category, template, fromWelcome = False, i
return __getSupportedPlatforms__(str(text), template)[0] return __getSupportedPlatforms__(str(text), template)[0]
def __createProjectSetNameAndPath__(path, projectName = None, checks = True): def __createProjectSetNameAndPath__(path, projectName = None, checks = True):
directoryEdit = waitForObject("{type='Utils::FancyLineEdit' unnamed='1' visible='1' " pathChooser = waitForObject("{type='Utils::PathChooser' name='baseFolder' visible='1'}")
"toolTip~='Full path: .*'}") directoryEdit = getChildByClass(pathChooser, "Utils::FancyLineEdit")
replaceEditorContent(directoryEdit, path) replaceEditorContent(directoryEdit, path)
projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' " projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' "
"type='Utils::FancyLineEdit'}") "type='Utils::FancyLineEdit'}")