forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/12.0'
Conflicts: src/plugins/python/pyside.cpp Change-Id: I1f84ed56d38355cef6076797c72693fff4c1aa78
This commit is contained in:
2
.github/workflows/build_cmake.yml
vendored
2
.github/workflows/build_cmake.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
env:
|
||||
QT_VERSION: 6.5.2
|
||||
MACOS_DEPLOYMENT_TARGET: 10.15
|
||||
CLANG_VERSION: 17.0.0-rc4
|
||||
CLANG_VERSION: 17.0.1
|
||||
ELFUTILS_VERSION: 0.175
|
||||
CMAKE_VERSION: 3.21.1
|
||||
NINJA_VERSION: 1.10.2
|
||||
|
||||
@@ -7,7 +7,7 @@ instructions:
|
||||
variableValue: "RelWithDebInfo"
|
||||
- type: EnvironmentVariable
|
||||
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
|
||||
variableName: QTC_QT_BASE_URL
|
||||
variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.2-released/Qt"
|
||||
|
||||
@@ -2068,20 +2068,23 @@ class Tester(Dumper):
|
||||
|
||||
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 ------------------------------
|
||||
|
||||
debug = print if 'QT_LLDB_SUMMARY_PROVIDER_DEBUG' in os.environ \
|
||||
else lambda *a, **k: None
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
__module__ = sys.modules[__name__]
|
||||
DEBUG = False if not hasattr(__module__, 'DEBUG') else DEBUG
|
||||
|
||||
debug(f"Loading lldbbridge.py from {__file__}")
|
||||
|
||||
class LogMixin():
|
||||
@staticmethod
|
||||
def log(message='', log_caller=False, frame=1, args=''):
|
||||
if not DEBUG:
|
||||
return
|
||||
if log_caller:
|
||||
message = ": " + message if len(message) else ''
|
||||
# FIXME: Compute based on first frame not in this class?
|
||||
@@ -2090,7 +2093,7 @@ class LogMixin():
|
||||
localz = frame.f_locals
|
||||
instance = str(localz["self"]) + "." if 'self' in localz else ''
|
||||
message = "%s%s(%s)%s" % (instance, fn, args, message)
|
||||
print(message)
|
||||
debug(message)
|
||||
|
||||
@staticmethod
|
||||
def log_fn(arg_str=''):
|
||||
@@ -2437,6 +2440,11 @@ class SyntheticChildrenProvider(SummaryProvider):
|
||||
|
||||
def __lldb_init_module(debugger, internal_dict):
|
||||
# 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':
|
||||
# Make available under global 'qt' name for consistency,
|
||||
@@ -2471,10 +2479,3 @@ def __lldb_init_module(debugger, internal_dict):
|
||||
% ("qt.SyntheticChildrenProvider", 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))
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
|
||||
"id": "R.AutoTest",
|
||||
"category": "H.Project",
|
||||
"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.",
|
||||
"trDisplayName": "Auto Test Project",
|
||||
"trDisplayCategory": "Other Project",
|
||||
"icon": "autotest.png",
|
||||
"id": "M.BoostAutoTest",
|
||||
"category": "I.TestProject",
|
||||
"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": "Boost Test Project",
|
||||
"trDisplayCategory": "Test Project",
|
||||
"icon": "../autotest.png",
|
||||
"iconKind": "Themed",
|
||||
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
|
||||
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
|
||||
@@ -33,10 +33,6 @@
|
||||
{ "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('.', '_') }"
|
||||
@@ -44,10 +40,6 @@
|
||||
{
|
||||
"key": "TestCaseFileWithCppSuffix",
|
||||
"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",
|
||||
"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,
|
||||
"items":
|
||||
[
|
||||
{
|
||||
"trKey": "Qt Test",
|
||||
"value": "QtTest"
|
||||
},
|
||||
{
|
||||
"trKey": "Google Test",
|
||||
"value": "GTest"
|
||||
},
|
||||
{
|
||||
"trKey": "Qt Quick Test",
|
||||
"value": "QtQuickTest"
|
||||
},
|
||||
{
|
||||
"trKey": "Boost Test (header only)",
|
||||
"value": "BoostTest"
|
||||
@@ -96,28 +76,13 @@
|
||||
{
|
||||
"trKey": "Boost Test (shared libraries)",
|
||||
"value": "BoostTest_dyn"
|
||||
},
|
||||
{
|
||||
"trKey": "Catch2",
|
||||
"value": "Catch2"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "RequireGUI",
|
||||
"trDisplayName": "GUI Application",
|
||||
"visible": "%{JS: value('TestFrameWork') === 'QtTest'}",
|
||||
"type": "CheckBox",
|
||||
"data": {
|
||||
"checked": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "TestSuiteName",
|
||||
"trDisplayName": "Test suite name:",
|
||||
"visible": "%{JS: ['BoostTest', 'BoostTest_dyn', 'GTest'].indexOf(value('TestFrameWork')) >= 0}",
|
||||
"mandatory": true,
|
||||
"type": "LineEdit",
|
||||
"data": { "validator": "^[a-zA-Z_0-9]+$" }
|
||||
@@ -129,43 +94,6 @@
|
||||
"type": "LineEdit",
|
||||
"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",
|
||||
"trDisplayName": "Boost include directory (optional):",
|
||||
@@ -186,25 +114,6 @@
|
||||
"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",
|
||||
"trDisplayName": "Build system:",
|
||||
@@ -242,7 +151,7 @@
|
||||
"enabled": "%{IsTopLevelProject}",
|
||||
"data": {
|
||||
"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":
|
||||
[
|
||||
{
|
||||
"source": "files/gtest_dependency.pri",
|
||||
"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",
|
||||
"source": "../files/tst.pro",
|
||||
"target": "%{ProjectFilePath}",
|
||||
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
|
||||
"openInEditor": false,
|
||||
"openAsProject": true
|
||||
},
|
||||
{
|
||||
"source": "files/tst.qbs",
|
||||
"source": "../files/tst.qbs",
|
||||
"target": "%{ProjectFilePath}",
|
||||
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
|
||||
"openInEditor": false,
|
||||
"openAsProject": true
|
||||
},
|
||||
{
|
||||
"source": "files/tst.txt",
|
||||
"source": "../files/tst.txt",
|
||||
"target": "CMakeLists.txt",
|
||||
"condition": "%{JS: value('BuildSystem') == 'cmake'}",
|
||||
"openInEditor": false,
|
||||
"openAsProject": true
|
||||
},
|
||||
{
|
||||
"source": "files/tst_src_gt.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",
|
||||
"source": "../files/tst_main.cpp",
|
||||
"target": "%{MainCppName}",
|
||||
"condition": "%{JS: ['GTest', 'QtQuickTest', 'BoostTest', 'BoostTest_dyn', 'Catch2'].indexOf(value('TestFrameWork')) >= 0}",
|
||||
"openInEditor": true
|
||||
},
|
||||
{
|
||||
"source": "files/tst_src_boost.cpp",
|
||||
"source": "../files/tst_src_boost.cpp",
|
||||
"target": "%{TestCaseFileWithCppSuffix}",
|
||||
"condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}"
|
||||
},
|
||||
{
|
||||
"source": "files/tst_qml.tmpl",
|
||||
"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",
|
||||
"source": "../../projects/git.ignore",
|
||||
"target": ".gitignore",
|
||||
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
|
||||
}
|
||||
184
share/qtcreator/templates/wizards/autotest/catch/wizard.json
Normal file
184
share/qtcreator/templates/wizards/autotest/catch/wizard.json
Normal 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' )}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -150,7 +150,7 @@ find_package(Boost COMPONENTS unit_test_framework REQUIRED)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileGTestWithCppSuffix})
|
||||
add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileWithCppSuffix})
|
||||
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
|
||||
if (Boost_FOUND)
|
||||
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)
|
||||
@endif
|
||||
|
||||
add_executable(${PROJECT_NAME} %{TestCaseFileWithCppSuffix} main.cpp)
|
||||
add_executable(%{TestCaseName} %{TestCaseFileWithCppSuffix} main.cpp)
|
||||
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
|
||||
|
||||
@if "%{Catch2NeedsQt}" == "true"
|
||||
target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::Gui)
|
||||
|
||||
192
share/qtcreator/templates/wizards/autotest/gtest/wizard.json
Normal file
192
share/qtcreator/templates/wizards/autotest/gtest/wizard.json
Normal 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' )}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
180
share/qtcreator/templates/wizards/autotest/qttest/wizard.json
Normal file
180
share/qtcreator/templates/wizards/autotest/qttest/wizard.json
Normal 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' )}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
192
share/qtcreator/templates/wizards/autotest/quicktest/wizard.json
Normal file
192
share/qtcreator/templates/wizards/autotest/quicktest/wizard.json
Normal 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' )}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
#include "execmenu.h"
|
||||
#include "historycompleter.h"
|
||||
#include "hostosinfo.h"
|
||||
#include "icon.h"
|
||||
#include "qtcassert.h"
|
||||
#include "utilsicons.h"
|
||||
#include "utilstr.h"
|
||||
|
||||
#include <solutions/spinner/spinner.h>
|
||||
@@ -621,7 +621,6 @@ QString FancyLineEdit::fixInputString(const QString &string)
|
||||
|
||||
FancyIconButton::FancyIconButton(QWidget *parent)
|
||||
: QAbstractButton(parent)
|
||||
, m_autoHide(false)
|
||||
{
|
||||
setCursor(Qt::ArrowCursor);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
|
||||
@@ -45,8 +45,8 @@ protected:
|
||||
void keyReleaseEvent(QKeyEvent *ke) override;
|
||||
|
||||
private:
|
||||
float m_iconOpacity;
|
||||
bool m_autoHide;
|
||||
float m_iconOpacity = 1.0f;
|
||||
bool m_autoHide = false;
|
||||
QIcon m_icon;
|
||||
};
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
|
||||
d->m_nameLineEdit = new Utils::FancyLineEdit(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->setDisabled(d->m_forceSubProject);
|
||||
|
||||
|
||||
@@ -261,7 +261,10 @@ bool QuickTestParser::handleQtQuickTest(QPromise<TestParseResultPtr> &promise,
|
||||
return false;
|
||||
const FilePath cppFileName = document->filePath();
|
||||
const FilePath proFile = FilePath::fromString(ppList.at(0)->projectFile);
|
||||
{
|
||||
QWriteLocker lock(&m_parseLock);
|
||||
m_mainCppFiles.insert(cppFileName, proFile);
|
||||
}
|
||||
const FilePath srcDir = FilePath::fromString(quickTestSrcDir(cppFileName));
|
||||
if (srcDir.isEmpty())
|
||||
return false;
|
||||
@@ -340,13 +343,13 @@ QuickTestParser::QuickTestParser(ITestFramework *framework)
|
||||
void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
|
||||
{
|
||||
m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
|
||||
QWriteLocker lock(&m_parseLock); // should not be necessary
|
||||
if (!fullParse) {
|
||||
// in a full parse we get the correct entry points by the respective main
|
||||
m_proFilesForQmlFiles = QuickTestUtils::proFilesForQmlFiles(framework(), filesToParse);
|
||||
// get rid of cached main cpp files that are going to get processed anyhow
|
||||
for (const FilePath &file : filesToParse) {
|
||||
if (m_mainCppFiles.contains(file)) {
|
||||
m_mainCppFiles.remove(file);
|
||||
if (m_mainCppFiles.remove(file) == 1) {
|
||||
if (m_mainCppFiles.isEmpty())
|
||||
break;
|
||||
}
|
||||
@@ -355,6 +358,7 @@ void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
|
||||
// get rid of all cached main cpp files
|
||||
m_mainCppFiles.clear();
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
m_checkForDerivedTests = theQtTestFramework().quickCheckForDerivedTests();
|
||||
|
||||
@@ -399,9 +403,10 @@ bool QuickTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
|
||||
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
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
@@ -28,7 +29,7 @@ public:
|
||||
void release() override;
|
||||
bool processDocument(QPromise<TestParseResultPtr> &promise,
|
||||
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"}; };
|
||||
|
||||
private:
|
||||
@@ -45,6 +46,7 @@ private:
|
||||
QMap<QString, QMap<QString, QDateTime> > m_watchedFiles;
|
||||
QMap<Utils::FilePath, Utils::FilePath> m_mainCppFiles;
|
||||
QSet<Utils::FilePath> m_prefilteredFiles;
|
||||
QReadWriteLock m_parseLock; // guard for m_mainCppFiles
|
||||
bool m_checkForDerivedTests = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -611,7 +611,8 @@ void TestTreeModel::insertItemInParent(TestTreeItem *item, TestTreeItem *root, b
|
||||
delete item;
|
||||
} else {
|
||||
// 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())
|
||||
item->setData(0, cached.value(), Qt::CheckStateRole);
|
||||
else
|
||||
|
||||
@@ -48,6 +48,27 @@ class TestHandler : public rst::ContentHandler {
|
||||
void StartBlock(rst::BlockType type) {
|
||||
std::string 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;
|
||||
@@ -80,8 +101,12 @@ class TestHandler : public rst::ContentHandler {
|
||||
content_.append(text, size);
|
||||
}
|
||||
|
||||
void HandleDirective(const char *type) {
|
||||
content_ += std::string("<") + type + " />";
|
||||
void HandleDirective(const std::string &type, const std::string &name) {
|
||||
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) {
|
||||
EXPECT_EQ("<p>test</p>", Parse("test"));
|
||||
EXPECT_EQ("<p>test</p>", Parse("\ntest"));
|
||||
@@ -143,6 +176,14 @@ TEST(ParserTest, Literal) {
|
||||
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) {
|
||||
EXPECT_EQ("", Parse(".."));
|
||||
EXPECT_EQ("", Parse("..\n"));
|
||||
@@ -151,11 +192,49 @@ TEST(ParserTest, Comment) {
|
||||
}
|
||||
|
||||
TEST(ParserTest, Directive) {
|
||||
EXPECT_EQ("<test />", Parse(".. test::"));
|
||||
EXPECT_EQ("<test />", Parse(".. test::"));
|
||||
EXPECT_EQ("<test />", Parse("..\ttest::"));
|
||||
EXPECT_EQ("<div class=\"\">test</div>", Parse(".. test::"));
|
||||
EXPECT_EQ("<div class=\"name\">test</div>", Parse(".. test:: name"));
|
||||
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) {
|
||||
#ifdef _WIN32
|
||||
// Disable message boxes on assertion failures.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "rstparser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
@@ -55,15 +56,15 @@ void rst::Parser::SkipSpace() {
|
||||
|
||||
std::string rst::Parser::ParseDirectiveType() {
|
||||
const char *s = ptr_;
|
||||
if (!std::isalnum(*s))
|
||||
if (!std::isalnum(*s) && *s != '|')
|
||||
return std::string();
|
||||
for (;;) {
|
||||
++s;
|
||||
if (std::isalnum(*s))
|
||||
continue;
|
||||
switch (*s) {
|
||||
case '-': case '_': case '+': case ':': case '.':
|
||||
if (std::isalnum(s[1])) {
|
||||
case '-': case '_': case '+': case ':': case '.': case '|':
|
||||
if (std::isalnum(s[1]) || (*s == '|' && IsSpace(s[1]))) {
|
||||
++s;
|
||||
continue;
|
||||
}
|
||||
@@ -91,13 +92,28 @@ void rst::Parser::EnterBlock(rst::BlockType &prev_type, rst::BlockType type) {
|
||||
void rst::Parser::ParseBlock(
|
||||
rst::BlockType type, rst::BlockType &prev_type, int indent) {
|
||||
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) {
|
||||
const char *line_start = ptr_;
|
||||
if (!first) {
|
||||
// Check indentation.
|
||||
SkipSpace();
|
||||
if (ptr_ - line_start != indent)
|
||||
const int new_indent = ptr_ - line_start;
|
||||
if (new_indent < indent)
|
||||
break;
|
||||
// Restore the indent
|
||||
if (new_indent > indent)
|
||||
std::advance(ptr_, indent - new_indent);
|
||||
|
||||
if (*ptr_ == '\n') {
|
||||
++ptr_;
|
||||
break; // Empty line ends the block.
|
||||
@@ -119,9 +135,17 @@ void rst::Parser::ParseBlock(
|
||||
|
||||
// Copy text converting all whitespace characters to spaces.
|
||||
text.reserve(end - line_start + 1);
|
||||
if (!first)
|
||||
if (!first && !have_h1)
|
||||
text.push_back('\n');
|
||||
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) {
|
||||
char c = *s;
|
||||
if (c == '\t') {
|
||||
@@ -129,10 +153,60 @@ void rst::Parser::ParseBlock(
|
||||
TAB_WIDTH - ((indent + s - line_start) % TAB_WIDTH));
|
||||
} else if (IsSpace(c)) {
|
||||
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 {
|
||||
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')
|
||||
++ptr_;
|
||||
}
|
||||
@@ -144,11 +218,35 @@ void rst::Parser::ParseBlock(
|
||||
bool literal = type == PARAGRAPH && EndsWith(text, "::");
|
||||
if (!literal || text.size() != 2) {
|
||||
std::size_t size = text.size();
|
||||
if (size == 0 && inline_tags.size() == 0)
|
||||
return;
|
||||
|
||||
if (literal)
|
||||
--size;
|
||||
EnterBlock(prev_type, type);
|
||||
handler_->StartBlock(type);
|
||||
|
||||
if (inline_tags.size() == 0) {
|
||||
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();
|
||||
}
|
||||
if (literal) {
|
||||
@@ -191,6 +289,58 @@ void rst::Parser::ParseLineBlock(rst::BlockType &prev_type, int indent) {
|
||||
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) {
|
||||
BlockType prev_type = PARAGRAPH;
|
||||
ptr_ = s;
|
||||
@@ -214,7 +364,28 @@ void rst::Parser::Parse(const char *s) {
|
||||
std::string type = ParseDirectiveType();
|
||||
if (!type.empty() && ptr_[0] == ':' && ptr_[1] == ':') {
|
||||
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.
|
||||
while (*ptr_ && *ptr_ != '\n')
|
||||
|
||||
@@ -35,6 +35,13 @@
|
||||
namespace rst {
|
||||
|
||||
enum BlockType {
|
||||
H1,
|
||||
H2,
|
||||
H3,
|
||||
H4,
|
||||
H5,
|
||||
CODE,
|
||||
REFERENCE_LINK,
|
||||
PARAGRAPH,
|
||||
LINE_BLOCK,
|
||||
BLOCK_QUOTE,
|
||||
@@ -58,7 +65,10 @@ class ContentHandler {
|
||||
virtual void HandleText(const char *text, std::size_t size) = 0;
|
||||
|
||||
// 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.
|
||||
@@ -85,6 +95,12 @@ class Parser {
|
||||
// Parses a line block.
|
||||
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:
|
||||
explicit Parser(ContentHandler *h) : handler_(h), ptr_(0) {}
|
||||
|
||||
@@ -94,4 +110,3 @@ class Parser {
|
||||
}
|
||||
|
||||
#endif // RSTPARSER_H_
|
||||
|
||||
|
||||
@@ -46,4 +46,5 @@ add_qtc_plugin(CMakeProjectManager
|
||||
3rdparty/cmake/cmListFileCache.cxx
|
||||
3rdparty/cmake/cmListFileLexer.cxx
|
||||
3rdparty/cmake/cmListFileCache.h
|
||||
3rdparty/rstparser/rstparser.cc 3rdparty/rstparser/rstparser.h
|
||||
)
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#include "cmakebuildsystem.h"
|
||||
#include "cmakefilecompletionassist.h"
|
||||
#include "cmakeindenter.h"
|
||||
#include "cmakekitaspect.h"
|
||||
#include "cmakeproject.h"
|
||||
#include "cmakeprojectconstants.h"
|
||||
|
||||
#include "3rdparty/cmake/cmListFileCache.h"
|
||||
@@ -54,13 +52,7 @@ public:
|
||||
|
||||
CMakeEditor::CMakeEditor()
|
||||
{
|
||||
CMakeTool *tool = nullptr;
|
||||
if (auto bs = ProjectTree::currentBuildSystem())
|
||||
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
|
||||
if (!tool)
|
||||
tool = CMakeToolManager::defaultCMakeTool();
|
||||
|
||||
if (tool)
|
||||
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||
m_keywords = tool->keywords();
|
||||
}
|
||||
|
||||
@@ -321,6 +313,7 @@ class CMakeHoverHandler : public TextEditor::BaseHoverHandler
|
||||
{
|
||||
mutable CMakeKeywords m_keywords;
|
||||
QString m_helpToolTip;
|
||||
QString m_contextHelp;
|
||||
|
||||
public:
|
||||
const CMakeKeywords &keywords() const;
|
||||
@@ -333,22 +326,13 @@ public:
|
||||
|
||||
const CMakeKeywords &CMakeHoverHandler::keywords() const
|
||||
{
|
||||
if (m_keywords.functions.isEmpty()) {
|
||||
CMakeTool *tool = nullptr;
|
||||
if (auto bs = ProjectTree::currentBuildSystem())
|
||||
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
|
||||
if (!tool)
|
||||
tool = CMakeToolManager::defaultCMakeTool();
|
||||
|
||||
if (tool)
|
||||
if (m_keywords.functions.isEmpty())
|
||||
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||
m_keywords = tool->keywords();
|
||||
}
|
||||
|
||||
return m_keywords;
|
||||
}
|
||||
|
||||
QString readFirstParagraphs(const QString &element, const FilePath &helpFile);
|
||||
|
||||
void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
||||
int pos,
|
||||
ReportPriority report)
|
||||
@@ -360,24 +344,39 @@ void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget
|
||||
const QString word = Utils::Text::wordUnderCursor(cursor);
|
||||
|
||||
FilePath helpFile;
|
||||
for (const auto &map : {keywords().functions,
|
||||
keywords().variables,
|
||||
keywords().directoryProperties,
|
||||
keywords().sourceProperties,
|
||||
keywords().targetProperties,
|
||||
keywords().testProperties,
|
||||
keywords().properties,
|
||||
keywords().includeStandardModules,
|
||||
keywords().findModules,
|
||||
keywords().policies}) {
|
||||
if (map.contains(word)) {
|
||||
helpFile = map.value(word);
|
||||
QString helpCategory;
|
||||
struct
|
||||
{
|
||||
const QMap<QString, Utils::FilePath> ↦
|
||||
QString helpCategory;
|
||||
} keywordsListMaps[] = {{keywords().functions, "command"},
|
||||
{keywords().variables, "variable"},
|
||||
{keywords().directoryProperties, "prop_dir"},
|
||||
{keywords().sourceProperties, "prop_sf"},
|
||||
{keywords().targetProperties, "prop_tgt"},
|
||||
{keywords().testProperties, "prop_test"},
|
||||
{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;
|
||||
}
|
||||
}
|
||||
m_helpToolTip.clear();
|
||||
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);
|
||||
}
|
||||
@@ -385,7 +384,7 @@ void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget
|
||||
void CMakeHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
|
||||
{
|
||||
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
|
||||
Utils::ToolTip::hide();
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
#include "cmakebuildsystem.h"
|
||||
#include "cmakebuildtarget.h"
|
||||
#include "cmakekitaspect.h"
|
||||
#include "cmakeproject.h"
|
||||
#include "cmakeprojectconstants.h"
|
||||
#include "cmaketool.h"
|
||||
#include "cmaketoolmanager.h"
|
||||
@@ -143,7 +141,7 @@ static int findPathStart(const AssistInterface *interface)
|
||||
}
|
||||
|
||||
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 * {
|
||||
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 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,
|
||||
static QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words,
|
||||
const QIcon &icon)
|
||||
{
|
||||
struct MarkDownAssitProposalItem : public AssistProposalItem
|
||||
{
|
||||
Qt::TextFormat detailFormat() const { return Qt::MarkdownText; }
|
||||
Qt::TextFormat detailFormat() const override { return Qt::MarkdownText; }
|
||||
};
|
||||
|
||||
QList<AssistProposalItemInterface *> list;
|
||||
@@ -180,10 +164,10 @@ QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath>
|
||||
MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem();
|
||||
item->setText(it.key());
|
||||
if (!it.value().isEmpty())
|
||||
item->setDetail(readFirstParagraphs(it.key(), it.value()));
|
||||
item->setDetail(CMakeToolManager::toolTipForRstHelpFile(it.value()));
|
||||
item->setIcon(icon);
|
||||
list << item;
|
||||
};
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -203,7 +187,7 @@ static int addFilePathItems(const AssistInterface *interface,
|
||||
|
||||
const QString word = interface->textAt(startPos, interface->position() - startPos);
|
||||
FilePath baseDir = interface->filePath().absoluteFilePath().parentDir();
|
||||
const int lastSlashPos = word.lastIndexOf(QLatin1Char('/'));
|
||||
const qsizetype lastSlashPos = word.lastIndexOf(QLatin1Char('/'));
|
||||
|
||||
QString prefix = word;
|
||||
if (lastSlashPos != -1) {
|
||||
@@ -227,7 +211,7 @@ static int addFilePathItems(const AssistInterface *interface,
|
||||
return startPos;
|
||||
}
|
||||
|
||||
QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByteArray &content)
|
||||
static QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByteArray &content)
|
||||
{
|
||||
cmListFile cmakeListFile;
|
||||
std::string errorString;
|
||||
@@ -258,16 +242,8 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
||||
Project *project = nullptr;
|
||||
const FilePath &filePath = interface()->filePath();
|
||||
if (!filePath.isEmpty() && filePath.isFile()) {
|
||||
CMakeTool *cmake = nullptr;
|
||||
project = static_cast<CMakeProject *>(ProjectManager::projectForFile(filePath));
|
||||
if (project && project->activeTarget())
|
||||
cmake = CMakeKitAspect::cmakeTool(project->activeTarget()->kit());
|
||||
|
||||
if (!cmake)
|
||||
cmake = CMakeToolManager::defaultCMakeTool();
|
||||
|
||||
if (cmake && cmake->isValid())
|
||||
keywords = cmake->keywords();
|
||||
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||
keywords = tool->keywords();
|
||||
}
|
||||
|
||||
QStringList buildTargets;
|
||||
|
||||
@@ -93,13 +93,15 @@ QtcPlugin {
|
||||
name: "3rdparty"
|
||||
cpp.includePaths: base.concat("3rdparty/cmake")
|
||||
|
||||
prefix: "3rdparty/cmake/"
|
||||
prefix: "3rdparty/"
|
||||
files: [
|
||||
"cmListFileCache.cxx",
|
||||
"cmListFileCache.h",
|
||||
"cmListFileLexer.cxx",
|
||||
"cmListFileLexer.h",
|
||||
"cmStandardLexer.h",
|
||||
"cmake/cmListFileCache.cxx",
|
||||
"cmake/cmListFileCache.h",
|
||||
"cmake/cmListFileLexer.cxx",
|
||||
"cmake/cmListFileLexer.h",
|
||||
"cmake/cmStandardLexer.h",
|
||||
"rstparser/rstparser.cc",
|
||||
"rstparser/rstparser.h"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,17 @@
|
||||
#include "cmakespecificsettings.h"
|
||||
#include "cmaketoolsettingsaccessor.h"
|
||||
|
||||
#include "3rdparty/rstparser/rstparser.h"
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <coreplugin/helpmanager.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/pointeralgorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -32,6 +38,137 @@ public:
|
||||
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;
|
||||
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()
|
||||
{
|
||||
return findById(d->m_defaultCMake);
|
||||
@@ -167,6 +335,25 @@ void CMakeToolManager::updateDocumentation()
|
||||
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,
|
||||
const QString &detectionSource,
|
||||
QString *logMessage)
|
||||
|
||||
@@ -30,6 +30,8 @@ public:
|
||||
static bool registerCMakeTool(std::unique_ptr<CMakeTool> &&tool);
|
||||
static void deregisterCMakeTool(const Utils::Id &id);
|
||||
|
||||
static CMakeTool *defaultProjectOrDefaultCMakeTool();
|
||||
|
||||
static CMakeTool *defaultCMakeTool();
|
||||
static void setDefaultCMakeTool(const Utils::Id &id);
|
||||
static CMakeTool *findByCommand(const Utils::FilePath &command);
|
||||
@@ -40,6 +42,8 @@ public:
|
||||
|
||||
static void updateDocumentation();
|
||||
|
||||
static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile);
|
||||
|
||||
public slots:
|
||||
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
|
||||
const QString &detectionSource,
|
||||
|
||||
@@ -8,17 +8,11 @@
|
||||
#include "ctfvisualizertr.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <tracing/timelinemodelaggregator.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
@@ -26,48 +20,6 @@ using json = nlohmann::json;
|
||||
|
||||
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,
|
||||
Timeline::TimelineModelAggregator *modelAggregator,
|
||||
CtfStatisticsModel *statisticsModel)
|
||||
@@ -75,7 +27,6 @@ CtfTraceManager::CtfTraceManager(QObject *parent,
|
||||
, m_modelAggregator(modelAggregator)
|
||||
, m_statisticsModel(statisticsModel)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
bool userConsentToIgnoreDeepTraces = false;
|
||||
|
||||
@@ -5,13 +5,11 @@
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
namespace Timeline {
|
||||
class TimelineModelAggregator;
|
||||
}
|
||||
namespace Timeline { class TimelineModelAggregator; }
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
@@ -34,7 +32,6 @@ public:
|
||||
|
||||
void addEvent(const nlohmann::json &event);
|
||||
|
||||
void load(const QString &filename);
|
||||
void finalize();
|
||||
|
||||
bool isEmpty() const;
|
||||
@@ -46,6 +43,9 @@ public:
|
||||
void setThreadRestriction(const QString &tid, bool restrictToThisThread);
|
||||
bool isRestrictedTo(const QString &tid) const;
|
||||
|
||||
void updateStatistics();
|
||||
void clearAll();
|
||||
|
||||
signals:
|
||||
void detailsRequested(const QString &title);
|
||||
|
||||
@@ -53,10 +53,6 @@ protected:
|
||||
void addModelForThread(const QString &threadId, const QString &processId);
|
||||
void addModelsToAggregator();
|
||||
|
||||
void updateStatistics();
|
||||
|
||||
void clearAll();
|
||||
|
||||
Timeline::TimelineModelAggregator *const m_modelAggregator;
|
||||
CtfStatisticsModel *const m_statisticsModel;
|
||||
|
||||
|
||||
@@ -13,30 +13,29 @@
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <coreplugin/progressmanager/taskprogress.h>
|
||||
|
||||
#include <debugger/analyzer/analyzerconstants.h>
|
||||
|
||||
#include <utils/async.h>
|
||||
#include <utils/stylehelper.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
#include <QFutureInterface>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QThread>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using namespace Core;
|
||||
using namespace CtfVisualizer::Constants;
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
CtfVisualizerTool::CtfVisualizerTool()
|
||||
: QObject (nullptr)
|
||||
, m_isLoading(false)
|
||||
, m_loadJson(nullptr)
|
||||
, m_traceView(nullptr)
|
||||
, m_modelAggregator(new Timeline::TimelineModelAggregator(this))
|
||||
@@ -150,34 +149,84 @@ Timeline::TimelineZoomControl *CtfVisualizerTool::zoomControl() const
|
||||
return m_zoomControl.get();
|
||||
}
|
||||
|
||||
void CtfVisualizerTool::loadJson(const QString &filename)
|
||||
class CtfJsonParserFunctor
|
||||
{
|
||||
if (m_isLoading)
|
||||
return;
|
||||
public:
|
||||
CtfJsonParserFunctor(QPromise<nlohmann::json> &promise)
|
||||
: m_promise(promise) {}
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
m_isLoading = false;
|
||||
bool operator()(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
m_traceManager->load(filename);
|
||||
json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false);
|
||||
} catch (...) {
|
||||
// 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]() {
|
||||
// in main thread:
|
||||
file.close();
|
||||
}
|
||||
|
||||
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()) {
|
||||
QMessageBox::warning(Core::ICore::dialogParent(),
|
||||
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);
|
||||
}
|
||||
setAvailableThreads(m_traceManager->getSortedThreads());
|
||||
thread->deleteLater();
|
||||
delete task;
|
||||
delete futureInterface;
|
||||
m_isLoading = false;
|
||||
}, Qt::QueuedConnection);
|
||||
m_loader.release()->deleteLater();
|
||||
};
|
||||
const auto onError = [this] {
|
||||
QMessageBox::warning(Core::ICore::dialogParent(),
|
||||
Tr::tr("CTF Visualizer"),
|
||||
Tr::tr("Cannot read the CTF file."));
|
||||
m_loader.release()->deleteLater();
|
||||
};
|
||||
|
||||
m_modelAggregator->setParent(nullptr);
|
||||
m_modelAggregator->moveToThread(thread);
|
||||
|
||||
thread->start();
|
||||
Core::ProgressManager::addTask(*task, Tr::tr("Loading CTF File"), CtfVisualizerTaskLoadJson);
|
||||
const Group recipe { AsyncTask<nlohmann::json>(onSetup) };
|
||||
m_loader.reset(new TaskTree(recipe));
|
||||
connect(m_loader.get(), &TaskTree::done, this, onDone);
|
||||
connect(m_loader.get(), &TaskTree::errorOccurred, this, onError);
|
||||
auto progress = new TaskProgress(m_loader.get());
|
||||
progress->setDisplayName(Tr::tr("Loading CTF File"));
|
||||
m_loader->start();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
#include "ctfvisualizerconstants.h"
|
||||
|
||||
#include <debugger/debuggermainwindow.h>
|
||||
|
||||
#include <tracing/timelinemodelaggregator.h>
|
||||
#include <tracing/timelinezoomcontrol.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QScopedPointer>
|
||||
|
||||
namespace Tasking { class TaskTree; }
|
||||
|
||||
namespace CtfVisualizer {
|
||||
namespace Internal {
|
||||
|
||||
@@ -21,7 +24,6 @@ class CtfStatisticsView;
|
||||
class CtfTimelineModel;
|
||||
class CtfVisualizerTraceView;
|
||||
|
||||
|
||||
class CtfVisualizerTool : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -34,7 +36,7 @@ public:
|
||||
CtfTraceManager *traceManager() const;
|
||||
Timeline::TimelineZoomControl *zoomControl() const;
|
||||
|
||||
void loadJson(const QString &filename);
|
||||
void loadJson(const QString &fileName);
|
||||
|
||||
private:
|
||||
void createViews();
|
||||
@@ -45,11 +47,11 @@ private:
|
||||
void setAvailableThreads(const QList<CtfTimelineModel *> &threads);
|
||||
void toggleThreadRestriction(QAction *action);
|
||||
|
||||
Utils::Perspective m_perspective{Constants::CtfVisualizerPerspectiveId,
|
||||
Utils::Perspective m_perspective{CtfVisualizer::Constants::CtfVisualizerPerspectiveId,
|
||||
QCoreApplication::translate("QtC::CtfVisualizer",
|
||||
"Chrome Trace Format Visualizer")};
|
||||
|
||||
bool m_isLoading;
|
||||
std::unique_ptr<Tasking::TaskTree> m_loader;
|
||||
QScopedPointer<QAction> m_loadJson;
|
||||
|
||||
CtfVisualizerTraceView *m_traceView;
|
||||
|
||||
@@ -179,6 +179,7 @@ void LldbEngine::setupEngine()
|
||||
|
||||
showMessage("STARTING LLDB: " + lldbCmd.toUserOutput());
|
||||
Environment environment = runParameters().debugger.environment;
|
||||
environment.appendOrSet("QT_CREATOR_LLDB_PROCESS", "1");
|
||||
environment.appendOrSet("PYTHONUNBUFFERED", "1"); // avoid flushing problem on macOS
|
||||
DebuggerItem::addAndroidLldbPythonEnv(lldbCmd, environment);
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ add_qtc_plugin(MesonProjectManager
|
||||
mesoninfoparser.h
|
||||
mesonoutputparser.cpp
|
||||
mesonoutputparser.h
|
||||
mesonprocess.cpp
|
||||
mesonprocess.h
|
||||
mesonproject.cpp
|
||||
mesonproject.h
|
||||
mesonprojectimporter.cpp
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -49,8 +49,6 @@ Project {
|
||||
"mesonbuildconfiguration.h",
|
||||
"mesonbuildsystem.cpp",
|
||||
"mesonbuildsystem.h",
|
||||
"mesonprocess.cpp",
|
||||
"mesonprocess.h",
|
||||
"mesonproject.cpp",
|
||||
"mesonproject.h",
|
||||
"mesonprojectimporter.cpp",
|
||||
|
||||
@@ -4,33 +4,44 @@
|
||||
#include "mesonprojectparser.h"
|
||||
|
||||
#include "mesoninfoparser.h"
|
||||
#include "mesonprojectmanagertr.h"
|
||||
#include "mesonprojectnodes.h"
|
||||
#include "mesontools.h"
|
||||
#include "projecttree.h"
|
||||
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/taskhub.h>
|
||||
|
||||
#include <utils/async.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/fileinprojectfinder.h>
|
||||
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <utils/stringutils.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <coreplugin/progressmanager/processprogress.h>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace MesonProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
static Q_LOGGING_CATEGORY(mesonProcessLog, "qtc.meson.buildsystem", QtWarningMsg);
|
||||
|
||||
struct CompilerArgs
|
||||
{
|
||||
QStringList args;
|
||||
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)
|
||||
{
|
||||
for (const auto &flag : candidates) {
|
||||
@@ -40,22 +51,23 @@ inline std::optional<QString> extractValueIfMatches(const QString &arg,
|
||||
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"});
|
||||
}
|
||||
inline std::optional<ProjectExplorer::Macro> extractMacro(const QString &arg)
|
||||
|
||||
static std::optional<Macro> extractMacro(const QString &arg)
|
||||
{
|
||||
auto define = extractValueIfMatches(arg, {"-D", "/D"});
|
||||
if (define)
|
||||
return ProjectExplorer::Macro::fromKeyValue(define->toLatin1());
|
||||
return Macro::fromKeyValue(define->toLatin1());
|
||||
auto undef = extractValueIfMatches(arg, {"-U", "/U"});
|
||||
if (undef)
|
||||
return ProjectExplorer::Macro(undef->toLatin1(), ProjectExplorer::MacroType::Undefine);
|
||||
return Macro(undef->toLatin1(), MacroType::Undefine);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CompilerArgs splitArgs(const QStringList &args)
|
||||
static CompilerArgs splitArgs(const QStringList &args)
|
||||
{
|
||||
CompilerArgs splited;
|
||||
for (const QString &arg : args) {
|
||||
@@ -74,7 +86,7 @@ CompilerArgs splitArgs(const QStringList &args)
|
||||
return splited;
|
||||
}
|
||||
|
||||
QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList)
|
||||
static QStringList toAbsolutePath(const FilePath &refPath, QStringList &pathList)
|
||||
{
|
||||
QStringList allAbs;
|
||||
std::transform(std::cbegin(pathList),
|
||||
@@ -86,35 +98,22 @@ QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList
|
||||
return allAbs;
|
||||
}
|
||||
|
||||
MesonProjectParser::MesonProjectParser(const Utils::Id &meson,
|
||||
Utils::Environment env,
|
||||
ProjectExplorer::Project *project)
|
||||
MesonProjectParser::MesonProjectParser(const Id &meson, const Environment &env, Project *project)
|
||||
: m_env{env}
|
||||
, m_meson{meson}
|
||||
, 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
|
||||
// I take project info here, I also take build and src dir later from
|
||||
// functions args.
|
||||
auto fileFinder = new Utils::FileInProjectFinder;
|
||||
auto fileFinder = new FileInProjectFinder;
|
||||
fileFinder->setProjectDirectory(project->projectDirectory());
|
||||
fileFinder->setProjectFiles(project->files(ProjectExplorer::Project::AllFiles));
|
||||
fileFinder->setProjectFiles(project->files(Project::AllFiles));
|
||||
m_outputParser.setFileFinder(fileFinder);
|
||||
}
|
||||
|
||||
void MesonProjectParser::setMesonTool(const Utils::Id &meson)
|
||||
{
|
||||
m_meson = meson;
|
||||
}
|
||||
|
||||
bool MesonProjectParser::configure(const Utils::FilePath &sourcePath,
|
||||
const Utils::FilePath &buildPath,
|
||||
bool MesonProjectParser::configure(const FilePath &sourcePath,
|
||||
const FilePath &buildPath,
|
||||
const QStringList &args)
|
||||
{
|
||||
m_introType = IntroDataType::file;
|
||||
@@ -126,18 +125,18 @@ bool MesonProjectParser::configure(const Utils::FilePath &sourcePath,
|
||||
m_pendingCommands.enqueue(
|
||||
std::make_tuple(MesonTools::mesonWrapper(m_meson)->regenerate(sourcePath, buildPath),
|
||||
false));
|
||||
return m_process.run(cmd, m_env, m_projectName);
|
||||
return run(cmd, m_env, m_projectName);
|
||||
}
|
||||
|
||||
bool MesonProjectParser::wipe(const Utils::FilePath &sourcePath,
|
||||
const Utils::FilePath &buildPath,
|
||||
bool MesonProjectParser::wipe(const FilePath &sourcePath,
|
||||
const FilePath &buildPath,
|
||||
const QStringList &args)
|
||||
{
|
||||
return setup(sourcePath, buildPath, args, true);
|
||||
}
|
||||
|
||||
bool MesonProjectParser::setup(const Utils::FilePath &sourcePath,
|
||||
const Utils::FilePath &buildPath,
|
||||
bool MesonProjectParser::setup(const FilePath &sourcePath,
|
||||
const FilePath &buildPath,
|
||||
const QStringList &args,
|
||||
bool forceWipe)
|
||||
{
|
||||
@@ -149,10 +148,10 @@ bool MesonProjectParser::setup(const Utils::FilePath &sourcePath,
|
||||
if (forceWipe || isSetup(buildPath))
|
||||
cmdArgs << "--wipe";
|
||||
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_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_introType = IntroDataType::stdo;
|
||||
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_projectName,
|
||||
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) {
|
||||
if (target.type == Target::Type::executable) {
|
||||
ProjectExplorer::BuildTargetInfo bti;
|
||||
BuildTargetInfo bti;
|
||||
bti.displayName = target.name;
|
||||
bti.buildKey = Target::fullName(m_buildDir, target);
|
||||
bti.displayNameUniquifier = bti.buildKey;
|
||||
bti.targetFilePath = Utils::FilePath::fromString(target.fileName.first());
|
||||
bti.workingDirectory = Utils::FilePath::fromString(target.fileName.first()).absolutePath();
|
||||
bti.projectFilePath = Utils::FilePath::fromString(target.definedIn);
|
||||
bti.targetFilePath = FilePath::fromString(target.fileName.first());
|
||||
bti.workingDirectory = FilePath::fromString(target.fileName.first()).absolutePath();
|
||||
bti.projectFilePath = FilePath::fromString(target.definedIn);
|
||||
bti.usesTerminal = true;
|
||||
apps.append(bti);
|
||||
}
|
||||
@@ -198,8 +197,8 @@ QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const
|
||||
bool MesonProjectParser::startParser()
|
||||
{
|
||||
m_parserFutureResult = Utils::asyncRun(
|
||||
ProjectExplorer::ProjectExplorerPlugin::sharedThreadPool(),
|
||||
[processOutput = m_process.stdOut(), introType = m_introType,
|
||||
ProjectExplorerPlugin::sharedThreadPool(),
|
||||
[processOutput = m_stdo, introType = m_introType,
|
||||
buildDir = m_buildDir, srcDir = m_srcDir] {
|
||||
if (introType == IntroDataType::file)
|
||||
return extractParserResults(srcDir, MesonInfoParser::parse(buildDir));
|
||||
@@ -212,7 +211,7 @@ bool MesonProjectParser::startParser()
|
||||
}
|
||||
|
||||
MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
|
||||
const Utils::FilePath &srcDir, MesonInfoParser::Result &&parserResult)
|
||||
const FilePath &srcDir, MesonInfoParser::Result &&parserResult)
|
||||
{
|
||||
auto rootNode = ProjectTree::buildTree(srcDir,
|
||||
parserResult.targets,
|
||||
@@ -220,14 +219,21 @@ MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
|
||||
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
|
||||
for (const auto &target : additionalTargets()) {
|
||||
if (!targetList.contains(target)) {
|
||||
static const QString additionalTargets[] {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *> &data)
|
||||
@@ -245,17 +251,17 @@ void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *>
|
||||
emit parsingCompleted(true);
|
||||
}
|
||||
|
||||
ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart(
|
||||
RawProjectPart MesonProjectParser::buildRawPart(
|
||||
const Target &target,
|
||||
const Target::SourceGroup &sources,
|
||||
const ProjectExplorer::ToolChain *cxxToolChain,
|
||||
const ProjectExplorer::ToolChain *cToolChain)
|
||||
const ToolChain *cxxToolChain,
|
||||
const ToolChain *cToolChain)
|
||||
{
|
||||
ProjectExplorer::RawProjectPart part;
|
||||
RawProjectPart part;
|
||||
part.setDisplayName(target.name);
|
||||
part.setBuildSystemTarget(Target::fullName(m_buildDir, target));
|
||||
part.setFiles(sources.sources + sources.generatedSources);
|
||||
auto flags = splitArgs(sources.parameters);
|
||||
CompilerArgs flags = splitArgs(sources.parameters);
|
||||
part.setMacros(flags.macros);
|
||||
part.setIncludePaths(toAbsolutePath(m_buildDir, flags.includePaths));
|
||||
part.setProjectFileLocation(target.definedIn);
|
||||
@@ -267,30 +273,10 @@ ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart(
|
||||
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) {
|
||||
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;
|
||||
RawProjectParts parts;
|
||||
for_each_source_group(m_parserResult.targets,
|
||||
[&parts,
|
||||
&cxxToolChain,
|
||||
@@ -321,11 +307,111 @@ bool MesonProjectParser::matchesKit(const KitData &kit)
|
||||
return matches;
|
||||
}
|
||||
|
||||
bool MesonProjectParser::usesSameMesonVersion(const Utils::FilePath &buildPath)
|
||||
bool MesonProjectParser::usesSameMesonVersion(const FilePath &buildPath)
|
||||
{
|
||||
auto info = MesonInfoParser::mesonInfo(buildPath);
|
||||
auto meson = MesonTools::mesonWrapper(m_meson);
|
||||
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 MesonProjectManager
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "kitdata.h"
|
||||
#include "mesoninfoparser.h"
|
||||
#include "mesonoutputparser.h"
|
||||
#include "mesonprocess.h"
|
||||
#include "mesonprojectnodes.h"
|
||||
#include "mesonwrapper.h"
|
||||
|
||||
@@ -14,9 +13,6 @@
|
||||
#include <projectexplorer/kit.h>
|
||||
#include <projectexplorer/rawprojectpart.h>
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QQueue>
|
||||
|
||||
@@ -26,6 +22,7 @@ namespace Internal {
|
||||
class MesonProjectParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum class IntroDataType { file, stdo };
|
||||
struct ParserData
|
||||
{
|
||||
@@ -34,8 +31,10 @@ class MesonProjectParser : public QObject
|
||||
};
|
||||
|
||||
public:
|
||||
MesonProjectParser(const Utils::Id &meson, Utils::Environment env, ProjectExplorer::Project* project);
|
||||
void setMesonTool(const Utils::Id &meson);
|
||||
MesonProjectParser(const Utils::Id &meson,
|
||||
const Utils::Environment &env,
|
||||
ProjectExplorer::Project *project);
|
||||
|
||||
bool configure(const Utils::FilePath &sourcePath,
|
||||
const Utils::FilePath &buildPath,
|
||||
const QStringList &args);
|
||||
@@ -49,22 +48,11 @@ public:
|
||||
bool parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath);
|
||||
bool parse(const Utils::FilePath &sourcePath);
|
||||
|
||||
Q_SIGNAL void parsingCompleted(bool success);
|
||||
|
||||
std::unique_ptr<MesonProjectNode> takeProjectNode() { return std::move(m_rootNode); }
|
||||
|
||||
inline const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; };
|
||||
inline const TargetsList &targets() const { return m_parserResult.targets; }
|
||||
inline 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};
|
||||
}
|
||||
const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; };
|
||||
const TargetsList &targets() const { return m_parserResult.targets; }
|
||||
const QStringList &targetsNames() const { return m_targetsNames; }
|
||||
|
||||
QList<ProjectExplorer::BuildTargetInfo> appsTargets() const;
|
||||
|
||||
@@ -72,26 +60,27 @@ public:
|
||||
const ProjectExplorer::ToolChain *cxxToolChain,
|
||||
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 usesSameMesonVersion(const Utils::FilePath &buildPath);
|
||||
|
||||
signals:
|
||||
void parsingCompleted(bool success);
|
||||
|
||||
private:
|
||||
bool startParser();
|
||||
static ParserData *extractParserResults(const Utils::FilePath &srcDir,
|
||||
MesonInfoParser::Result &&parserResult);
|
||||
static void addMissingTargets(QStringList &targetList);
|
||||
void update(const QFuture<ParserData *> &data);
|
||||
ProjectExplorer::RawProjectPart buildRawPart(const Target &target,
|
||||
const Target::SourceGroup &sources,
|
||||
const ProjectExplorer::ToolChain *cxxToolChain,
|
||||
const ProjectExplorer::ToolChain *cToolChain);
|
||||
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
MesonProcess m_process;
|
||||
|
||||
MesonOutputParser m_outputParser;
|
||||
Utils::Environment m_env;
|
||||
Utils::Id m_meson;
|
||||
@@ -108,6 +97,22 @@ private:
|
||||
// maybe moving meson to build step could make this class simpler
|
||||
// also this should ease command dependencies
|
||||
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
|
||||
|
||||
@@ -12,36 +12,20 @@
|
||||
#include "toolssettingsaccessor.h"
|
||||
#include "toolssettingspage.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/projectmanager.h>
|
||||
#include <projectexplorer/runcontrol.h>
|
||||
|
||||
#include <utils/fsengine/fileiconprovider.h>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace MesonProjectManager::Internal {
|
||||
|
||||
class MesonProjectPluginPrivate : public QObject
|
||||
class MesonProjectPluginPrivate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MesonProjectPluginPrivate()
|
||||
{
|
||||
MesonTools::setTools(m_toolsSettings.loadMesonTools(ICore::dialogParent()));
|
||||
connect(ICore::instance(),
|
||||
&ICore::saveSettingsRequested,
|
||||
this,
|
||||
&MesonProjectPluginPrivate::saveAll);
|
||||
}
|
||||
|
||||
~MesonProjectPluginPrivate() {}
|
||||
|
||||
private:
|
||||
ToolsSettingsPage m_toolslSettingsPage;
|
||||
ToolsSettingsAccessor m_toolsSettings;
|
||||
MesonBuildStepFactory m_buildStepFactory;
|
||||
@@ -50,11 +34,6 @@ private:
|
||||
MesonActionsManager m_actions;
|
||||
MachineFileManager m_machineFilesManager;
|
||||
SimpleTargetRunnerFactory m_mesonRunWorkerFactory{{m_runConfigurationFactory.runConfigurationId()}};
|
||||
|
||||
void saveAll()
|
||||
{
|
||||
m_toolsSettings.saveMesonTools(MesonTools::tools(), ICore::dialogParent());
|
||||
}
|
||||
};
|
||||
|
||||
MesonProjectPlugin::~MesonProjectPlugin()
|
||||
@@ -72,5 +51,3 @@ void MesonProjectPlugin::initialize()
|
||||
}
|
||||
|
||||
} // MesonProjectManager::Internal
|
||||
|
||||
#include "mesonprojectplugin.moc"
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "mesonpluginconstants.h"
|
||||
#include "mesonprojectmanagertr.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/filepath.h>
|
||||
@@ -16,6 +17,7 @@
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
using namespace Core;
|
||||
using namespace Utils;
|
||||
|
||||
namespace MesonProjectManager {
|
||||
@@ -30,11 +32,16 @@ ToolsSettingsAccessor::ToolsSettingsAccessor()
|
||||
{
|
||||
setDocType("QtCreatorMesonTools");
|
||||
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,
|
||||
QWidget *parent)
|
||||
void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t> &tools)
|
||||
{
|
||||
using namespace Constants;
|
||||
Store data;
|
||||
@@ -51,13 +58,13 @@ void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t>
|
||||
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;
|
||||
auto data = restoreSettings(parent);
|
||||
auto data = restoreSettings(ICore::dialogParent());
|
||||
auto entry_count = data.value(ToolsSettings::ENTRY_COUNT, 0).toInt();
|
||||
std::vector<MesonTools::Tool_t> result;
|
||||
for (auto toolIndex = 0; toolIndex < entry_count; toolIndex++) {
|
||||
|
||||
@@ -14,8 +14,9 @@ class ToolsSettingsAccessor final : public Utils::UpgradingSettingsAccessor
|
||||
{
|
||||
public:
|
||||
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
|
||||
|
||||
@@ -77,6 +77,7 @@ bool SshParameters::setupSshEnvironment(Process *process)
|
||||
const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0"));
|
||||
if (SshSettings::askpassFilePath().exists()) {
|
||||
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.
|
||||
if (!env.hasKey("DISPLAY"))
|
||||
|
||||
@@ -86,20 +86,36 @@ void PySideInstaller::installPyside(const FilePath &python,
|
||||
{
|
||||
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");
|
||||
|
||||
const std::optional<FilePath> qtInstallDir
|
||||
= QtSupport::LinkWithQtSupport::linkedQt().tailRemoved(hostQtTail);
|
||||
if (qtInstallDir) {
|
||||
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");
|
||||
if (requirements.exists())
|
||||
availablePySides[QVersionNumber::fromString(versionDir.fileName())] = requirements;
|
||||
if (!requirementsList.contains(requirements) && requirements.exists())
|
||||
availablePySides[QVersionNumber::fromString(versionDir.fileName())]
|
||||
= requirements;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto install = new PipInstallTask(python);
|
||||
connect(install, &PipInstallTask::finished, install, &QObject::deleteLater);
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/async.h>
|
||||
#include <utils/detailswidget.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/listmodel.h>
|
||||
#include <utils/layoutbuilder.h>
|
||||
#include <utils/listmodel.h>
|
||||
#include <utils/pathchooser.h>
|
||||
#include <utils/process.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/treemodel.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
@@ -366,14 +367,6 @@ private:
|
||||
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 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::NativeFormat);
|
||||
for (const QString &versionGroup : pythonRegistry.childGroups()) {
|
||||
@@ -636,7 +630,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
|
||||
QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath");
|
||||
if (regVal.isValid()) {
|
||||
const FilePath &executable = FilePath::fromUserInput(regVal.toString());
|
||||
if (executable.exists() && !alreadyRegistered(pythons, executable)) {
|
||||
if (executable.exists()) {
|
||||
pythons << Interpreter{QUuid::createUuid().toString(),
|
||||
name,
|
||||
FilePath::fromUserInput(regVal.toString())};
|
||||
@@ -645,7 +639,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
|
||||
regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath");
|
||||
if (regVal.isValid()) {
|
||||
const FilePath &executable = FilePath::fromUserInput(regVal.toString());
|
||||
if (executable.exists() && !alreadyRegistered(pythons, executable)) {
|
||||
if (executable.exists()) {
|
||||
pythons << Interpreter{QUuid::createUuid().toString(),
|
||||
//: <python display name> (Windowed)
|
||||
Tr::tr("%1 (Windowed)").arg(name),
|
||||
@@ -656,28 +650,30 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
|
||||
if (regVal.isValid()) {
|
||||
const FilePath &path = FilePath::fromUserInput(regVal.toString());
|
||||
const FilePath python = path.pathAppended("python").withExecutableSuffix();
|
||||
if (python.exists() && !alreadyRegistered(pythons, python))
|
||||
if (python.exists())
|
||||
pythons << createInterpreter(python, "Python " + versionGroup);
|
||||
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
|
||||
if (pythonw.exists() && !alreadyRegistered(pythons, pythonw))
|
||||
if (pythonw.exists())
|
||||
pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)");
|
||||
}
|
||||
pythonRegistry.endGroup();
|
||||
}
|
||||
return pythons;
|
||||
}
|
||||
|
||||
static void addPythonsFromPath(QList<Interpreter> &pythons)
|
||||
static QList<Interpreter> pythonsFromPath()
|
||||
{
|
||||
QList<Interpreter> pythons;
|
||||
if (HostOsInfo::isWindowsHost()) {
|
||||
for (const FilePath &executable : FilePath("python").searchAllInPath()) {
|
||||
// Windows creates empty redirector files that may interfere
|
||||
if (executable.toFileInfo().size() == 0)
|
||||
continue;
|
||||
if (executable.exists() && !alreadyRegistered(pythons, executable))
|
||||
if (executable.exists())
|
||||
pythons << createInterpreter(executable, "Python from Path");
|
||||
}
|
||||
for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) {
|
||||
if (executable.exists() && !alreadyRegistered(pythons, executable))
|
||||
if (executable.exists())
|
||||
pythons << createInterpreter(executable, "Python from Path", "(Windowed)");
|
||||
}
|
||||
} else {
|
||||
@@ -690,11 +686,12 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
|
||||
const QDir dir(path.toString());
|
||||
for (const QFileInfo &fi : dir.entryInfoList(filters)) {
|
||||
const FilePath executable = Utils::FilePath::fromFileInfo(fi);
|
||||
if (executable.exists() && !alreadyRegistered(pythons, executable))
|
||||
if (executable.exists())
|
||||
pythons << createInterpreter(executable, "Python from Path");
|
||||
}
|
||||
}
|
||||
}
|
||||
return pythons;
|
||||
}
|
||||
|
||||
static QString idForPythonFromPath(const QList<Interpreter> &pythons)
|
||||
@@ -713,6 +710,51 @@ static QString idForPythonFromPath(const QList<Interpreter> &pythons)
|
||||
|
||||
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()
|
||||
{
|
||||
QTC_ASSERT(!settingsInstance, return);
|
||||
@@ -723,9 +765,7 @@ PythonSettings::PythonSettings()
|
||||
|
||||
initFromSettings(Core::ICore::settings());
|
||||
|
||||
if (HostOsInfo::isWindowsHost())
|
||||
addPythonsFromRegistry(m_interpreters);
|
||||
addPythonsFromPath(m_interpreters);
|
||||
scanSystemForInterpreters();
|
||||
|
||||
if (m_defaultInterpreterId.isEmpty())
|
||||
m_defaultInterpreterId = idForPythonFromPath(m_interpreters);
|
||||
|
||||
@@ -751,8 +751,10 @@ FilePath source(IDocument *document)
|
||||
void setProcessEnvironment(Environment *e)
|
||||
{
|
||||
const QString prompt = Internal::commonSettings().sshPasswordPrompt().path();
|
||||
if (!prompt.isEmpty())
|
||||
if (!prompt.isEmpty()) {
|
||||
e->set("SSH_ASKPASS", prompt);
|
||||
e->set("SSH_ASKPASS_REQUIRE", "force");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VcsBase
|
||||
|
||||
@@ -4,7 +4,7 @@ project('mesonsampleproject', 'cpp',default_options : ['cpp_std=c++11'])
|
||||
qt5 = import('qt5')
|
||||
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(
|
||||
moc_headers : 'mesonsampleproject.h',
|
||||
|
||||
@@ -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.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.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.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'}
|
||||
|
||||
@@ -81,8 +81,8 @@ def __createProjectOrFileSelectType__(category, template, fromWelcome = False, i
|
||||
return __getSupportedPlatforms__(str(text), template)[0]
|
||||
|
||||
def __createProjectSetNameAndPath__(path, projectName = None, checks = True):
|
||||
directoryEdit = waitForObject("{type='Utils::FancyLineEdit' unnamed='1' visible='1' "
|
||||
"toolTip~='Full path: .*'}")
|
||||
pathChooser = waitForObject("{type='Utils::PathChooser' name='baseFolder' visible='1'}")
|
||||
directoryEdit = getChildByClass(pathChooser, "Utils::FancyLineEdit")
|
||||
replaceEditorContent(directoryEdit, path)
|
||||
projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' "
|
||||
"type='Utils::FancyLineEdit'}")
|
||||
|
||||
Reference in New Issue
Block a user