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:
|
env:
|
||||||
QT_VERSION: 6.5.2
|
QT_VERSION: 6.5.2
|
||||||
MACOS_DEPLOYMENT_TARGET: 10.15
|
MACOS_DEPLOYMENT_TARGET: 10.15
|
||||||
CLANG_VERSION: 17.0.0-rc4
|
CLANG_VERSION: 17.0.1
|
||||||
ELFUTILS_VERSION: 0.175
|
ELFUTILS_VERSION: 0.175
|
||||||
CMAKE_VERSION: 3.21.1
|
CMAKE_VERSION: 3.21.1
|
||||||
NINJA_VERSION: 1.10.2
|
NINJA_VERSION: 1.10.2
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ instructions:
|
|||||||
variableValue: "RelWithDebInfo"
|
variableValue: "RelWithDebInfo"
|
||||||
- type: EnvironmentVariable
|
- type: EnvironmentVariable
|
||||||
variableName: LLVM_BASE_URL
|
variableName: LLVM_BASE_URL
|
||||||
variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_17.0.0-rc4-based
|
variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_17.0.1-based
|
||||||
- type: EnvironmentVariable
|
- type: EnvironmentVariable
|
||||||
variableName: QTC_QT_BASE_URL
|
variableName: QTC_QT_BASE_URL
|
||||||
variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.2-released/Qt"
|
variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.2-released/Qt"
|
||||||
|
|||||||
@@ -2068,20 +2068,23 @@ class Tester(Dumper):
|
|||||||
|
|
||||||
lldb.SBDebugger.Destroy(self.debugger)
|
lldb.SBDebugger.Destroy(self.debugger)
|
||||||
|
|
||||||
|
if 'QT_CREATOR_LLDB_PROCESS' in os.environ:
|
||||||
|
# Initialize Qt Creator dumper
|
||||||
|
try:
|
||||||
|
theDumper = Dumper()
|
||||||
|
except Exception as error:
|
||||||
|
print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error))
|
||||||
|
|
||||||
# ------------------------------ For use in LLDB ------------------------------
|
# ------------------------------ For use in LLDB ------------------------------
|
||||||
|
|
||||||
|
debug = print if 'QT_LLDB_SUMMARY_PROVIDER_DEBUG' in os.environ \
|
||||||
|
else lambda *a, **k: None
|
||||||
|
|
||||||
from pprint import pprint
|
debug(f"Loading lldbbridge.py from {__file__}")
|
||||||
|
|
||||||
__module__ = sys.modules[__name__]
|
|
||||||
DEBUG = False if not hasattr(__module__, 'DEBUG') else DEBUG
|
|
||||||
|
|
||||||
|
|
||||||
class LogMixin():
|
class LogMixin():
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log(message='', log_caller=False, frame=1, args=''):
|
def log(message='', log_caller=False, frame=1, args=''):
|
||||||
if not DEBUG:
|
|
||||||
return
|
|
||||||
if log_caller:
|
if log_caller:
|
||||||
message = ": " + message if len(message) else ''
|
message = ": " + message if len(message) else ''
|
||||||
# FIXME: Compute based on first frame not in this class?
|
# FIXME: Compute based on first frame not in this class?
|
||||||
@@ -2090,7 +2093,7 @@ class LogMixin():
|
|||||||
localz = frame.f_locals
|
localz = frame.f_locals
|
||||||
instance = str(localz["self"]) + "." if 'self' in localz else ''
|
instance = str(localz["self"]) + "." if 'self' in localz else ''
|
||||||
message = "%s%s(%s)%s" % (instance, fn, args, message)
|
message = "%s%s(%s)%s" % (instance, fn, args, message)
|
||||||
print(message)
|
debug(message)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def log_fn(arg_str=''):
|
def log_fn(arg_str=''):
|
||||||
@@ -2437,6 +2440,11 @@ class SyntheticChildrenProvider(SummaryProvider):
|
|||||||
|
|
||||||
def __lldb_init_module(debugger, internal_dict):
|
def __lldb_init_module(debugger, internal_dict):
|
||||||
# Module is being imported in an LLDB session
|
# Module is being imported in an LLDB session
|
||||||
|
if 'QT_CREATOR_LLDB_PROCESS' in os.environ:
|
||||||
|
# Let Qt Creator take care of its own dumper
|
||||||
|
return
|
||||||
|
|
||||||
|
debug("Initializing module with", debugger)
|
||||||
|
|
||||||
if not __name__ == 'qt':
|
if not __name__ == 'qt':
|
||||||
# Make available under global 'qt' name for consistency,
|
# Make available under global 'qt' name for consistency,
|
||||||
@@ -2471,10 +2479,3 @@ def __lldb_init_module(debugger, internal_dict):
|
|||||||
% ("qt.SyntheticChildrenProvider", type_category))
|
% ("qt.SyntheticChildrenProvider", type_category))
|
||||||
|
|
||||||
debugger.HandleCommand('type category enable %s' % type_category)
|
debugger.HandleCommand('type category enable %s' % type_category)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "lldbbridge":
|
|
||||||
try:
|
|
||||||
theDumper = Dumper()
|
|
||||||
except Exception as error:
|
|
||||||
print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error))
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
|
"supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ],
|
||||||
"id": "R.AutoTest",
|
"id": "M.BoostAutoTest",
|
||||||
"category": "H.Project",
|
"category": "I.TestProject",
|
||||||
"trDescription": "Creates a new unit test project. Unit tests allow you to verify that the code is fit for use and that there are no regressions.",
|
"trDescription": "Creates a new unit test project using Boost. Unit tests allow you to verify that the code is fit for use and that there are no regressions.",
|
||||||
"trDisplayName": "Auto Test Project",
|
"trDisplayName": "Boost Test Project",
|
||||||
"trDisplayCategory": "Other Project",
|
"trDisplayCategory": "Test Project",
|
||||||
"icon": "autotest.png",
|
"icon": "../autotest.png",
|
||||||
"iconKind": "Themed",
|
"iconKind": "Themed",
|
||||||
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
|
"featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ],
|
||||||
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
|
"enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}",
|
||||||
@@ -33,10 +33,6 @@
|
|||||||
{ "key": "MainCppName",
|
{ "key": "MainCppName",
|
||||||
"value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }"
|
"value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"key": "TestCaseFileGTestWithCppSuffix",
|
|
||||||
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "GUARD",
|
"key": "GUARD",
|
||||||
"value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }"
|
"value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }"
|
||||||
@@ -44,10 +40,6 @@
|
|||||||
{
|
{
|
||||||
"key": "TestCaseFileWithCppSuffix",
|
"key": "TestCaseFileWithCppSuffix",
|
||||||
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
|
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }"
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "TestCaseFileWithQmlSuffix",
|
|
||||||
"value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.qml' }"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -59,7 +51,7 @@
|
|||||||
"typeId": "Project",
|
"typeId": "Project",
|
||||||
"data":
|
"data":
|
||||||
{
|
{
|
||||||
"trDescription": "This wizard creates a simple unit test project."
|
"trDescription": "This wizard creates a simple unit test project using Boost."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -77,18 +69,6 @@
|
|||||||
"index": 0,
|
"index": 0,
|
||||||
"items":
|
"items":
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"trKey": "Qt Test",
|
|
||||||
"value": "QtTest"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"trKey": "Google Test",
|
|
||||||
"value": "GTest"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"trKey": "Qt Quick Test",
|
|
||||||
"value": "QtQuickTest"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"trKey": "Boost Test (header only)",
|
"trKey": "Boost Test (header only)",
|
||||||
"value": "BoostTest"
|
"value": "BoostTest"
|
||||||
@@ -96,28 +76,13 @@
|
|||||||
{
|
{
|
||||||
"trKey": "Boost Test (shared libraries)",
|
"trKey": "Boost Test (shared libraries)",
|
||||||
"value": "BoostTest_dyn"
|
"value": "BoostTest_dyn"
|
||||||
},
|
|
||||||
{
|
|
||||||
"trKey": "Catch2",
|
|
||||||
"value": "Catch2"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "RequireGUI",
|
|
||||||
"trDisplayName": "GUI Application",
|
|
||||||
"visible": "%{JS: value('TestFrameWork') === 'QtTest'}",
|
|
||||||
"type": "CheckBox",
|
|
||||||
"data": {
|
|
||||||
"checked": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "TestSuiteName",
|
"name": "TestSuiteName",
|
||||||
"trDisplayName": "Test suite name:",
|
"trDisplayName": "Test suite name:",
|
||||||
"visible": "%{JS: ['BoostTest', 'BoostTest_dyn', 'GTest'].indexOf(value('TestFrameWork')) >= 0}",
|
|
||||||
"mandatory": true,
|
"mandatory": true,
|
||||||
"type": "LineEdit",
|
"type": "LineEdit",
|
||||||
"data": { "validator": "^[a-zA-Z_0-9]+$" }
|
"data": { "validator": "^[a-zA-Z_0-9]+$" }
|
||||||
@@ -129,43 +94,6 @@
|
|||||||
"type": "LineEdit",
|
"type": "LineEdit",
|
||||||
"data": { "validator": "^[a-zA-Z_0-9]+$" }
|
"data": { "validator": "^[a-zA-Z_0-9]+$" }
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "RequireApplication",
|
|
||||||
"trDisplayName": "Requires QApplication",
|
|
||||||
"visible": "%{JS: value('TestFrameWork') === 'QtTest'}",
|
|
||||||
"type": "CheckBox",
|
|
||||||
"data": {
|
|
||||||
"checked": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "UseSetupCode",
|
|
||||||
"trDisplayName": "Generate setup code",
|
|
||||||
"visible": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
|
|
||||||
"type": "CheckBox",
|
|
||||||
"data": {
|
|
||||||
"checked": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "GenerateInitAndCleanup",
|
|
||||||
"trDisplayName": "Generate initialization and cleanup code",
|
|
||||||
"visible": "%{JS: [ 'QtTest', 'QtQuickTest' ].indexOf(value('TestFrameWork')) >= 0 }",
|
|
||||||
"type": "CheckBox",
|
|
||||||
"data": {
|
|
||||||
"checked": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "GTestRepository",
|
|
||||||
"trDisplayName": "Googletest source directory (optional):",
|
|
||||||
"visible": "%{JS: value('TestFrameWork') === 'GTest'}",
|
|
||||||
"mandatory": false,
|
|
||||||
"type": "PathChooser",
|
|
||||||
"data": {
|
|
||||||
"kind": "existingDirectory"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "BoostIncDir",
|
"name": "BoostIncDir",
|
||||||
"trDisplayName": "Boost include directory (optional):",
|
"trDisplayName": "Boost include directory (optional):",
|
||||||
@@ -186,25 +114,6 @@
|
|||||||
"kind": "existingDirectory"
|
"kind": "existingDirectory"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "CatchIncDir",
|
|
||||||
"trDisplayName": "Catch2 include directory (optional):",
|
|
||||||
"visible": "%{JS: value('TestFrameWork') === 'Catch2'}",
|
|
||||||
"mandatory": false,
|
|
||||||
"type": "PathChooser",
|
|
||||||
"data": {
|
|
||||||
"kind": "existingDirectory"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Catch2NeedsQt",
|
|
||||||
"trDisplayName": "Use Qt libraries",
|
|
||||||
"visible": "%{JS: '%{TestFrameWork}' === 'Catch2'}",
|
|
||||||
"type": "CheckBox",
|
|
||||||
"data": {
|
|
||||||
"checked": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "BuildSystem",
|
"name": "BuildSystem",
|
||||||
"trDisplayName": "Build system:",
|
"trDisplayName": "Build system:",
|
||||||
@@ -242,7 +151,7 @@
|
|||||||
"enabled": "%{IsTopLevelProject}",
|
"enabled": "%{IsTopLevelProject}",
|
||||||
"data": {
|
"data": {
|
||||||
"projectFilePath": "%{ProjectFilePath}",
|
"projectFilePath": "%{ProjectFilePath}",
|
||||||
"requiredFeatures": [ "%{JS: (value('TestFrameWork') === 'QtQuickTest' ? 'QtSupport.Wizards.FeatureQtQuick.2' : ((value('BuildSystem') === 'qmake' || value('TestFrameWork') === 'QtTest') ? 'QtSupport.Wizards.FeatureQt' : 'QtSupport.Wizards.FeatureDesktop' )) }" ]
|
"requiredFeatures": [ "%{JS: value('BuildSystem') === 'qmake' ? 'QtSupport.Wizards.FeatureQt' : 'QtSupport.Wizards.FeatureDesktop' }" ]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -258,87 +167,38 @@
|
|||||||
"data":
|
"data":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"source": "files/gtest_dependency.pri",
|
"source": "../files/tst.pro",
|
||||||
"target": "gtest_dependency.pri",
|
|
||||||
"condition": "%{JS: value('TestFrameWork') == 'GTest' && value('BuildSystem') == 'qmake'}",
|
|
||||||
"openInEditor": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "files/googlecommon.js",
|
|
||||||
"target": "googlecommon.js",
|
|
||||||
"condition": "%{JS: value('TestFrameWork') == 'GTest' && value('BuildSystem') == 'qbs'}",
|
|
||||||
"openInEditor": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "files/tst.pro",
|
|
||||||
"target": "%{ProjectFilePath}",
|
"target": "%{ProjectFilePath}",
|
||||||
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
|
"condition": "%{JS: value('BuildSystem') == 'qmake'}",
|
||||||
"openInEditor": false,
|
"openInEditor": false,
|
||||||
"openAsProject": true
|
"openAsProject": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "files/tst.qbs",
|
"source": "../files/tst.qbs",
|
||||||
"target": "%{ProjectFilePath}",
|
"target": "%{ProjectFilePath}",
|
||||||
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
|
"condition": "%{JS: value('BuildSystem') == 'qbs'}",
|
||||||
"openInEditor": false,
|
"openInEditor": false,
|
||||||
"openAsProject": true
|
"openAsProject": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "files/tst.txt",
|
"source": "../files/tst.txt",
|
||||||
"target": "CMakeLists.txt",
|
"target": "CMakeLists.txt",
|
||||||
"condition": "%{JS: value('BuildSystem') == 'cmake'}",
|
"condition": "%{JS: value('BuildSystem') == 'cmake'}",
|
||||||
"openInEditor": false,
|
"openInEditor": false,
|
||||||
"openAsProject": true
|
"openAsProject": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "files/tst_src_gt.cpp",
|
"source": "../files/tst_main.cpp",
|
||||||
"target": "%{TestCaseFileGTestWithCppSuffix}",
|
|
||||||
"condition": "%{JS: value('TestFrameWork') == 'GTest'}",
|
|
||||||
"openInEditor": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "files/tst_src.cpp",
|
|
||||||
"target": "%{TestCaseFileWithCppSuffix}",
|
|
||||||
"condition": "%{JS: value('TestFrameWork') == 'QtTest'}",
|
|
||||||
"openInEditor": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "files/tst_main.cpp",
|
|
||||||
"target": "%{MainCppName}",
|
"target": "%{MainCppName}",
|
||||||
"condition": "%{JS: ['GTest', 'QtQuickTest', 'BoostTest', 'BoostTest_dyn', 'Catch2'].indexOf(value('TestFrameWork')) >= 0}",
|
|
||||||
"openInEditor": true
|
"openInEditor": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "files/tst_src_boost.cpp",
|
"source": "../files/tst_src_boost.cpp",
|
||||||
"target": "%{TestCaseFileWithCppSuffix}",
|
"target": "%{TestCaseFileWithCppSuffix}",
|
||||||
"condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}"
|
"condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "files/tst_qml.tmpl",
|
"source": "../../projects/git.ignore",
|
||||||
"target": "%{TestCaseFileWithQmlSuffix}",
|
|
||||||
"condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
|
|
||||||
"openInEditor": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "files/setup.cpp",
|
|
||||||
"target": "setup.cpp",
|
|
||||||
"condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
|
|
||||||
"openInEditor": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "files/setup.h",
|
|
||||||
"target": "setup.h",
|
|
||||||
"condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}",
|
|
||||||
"openInEditor": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "files/catch2_tst.cpp",
|
|
||||||
"target": "%{TestCaseFileWithCppSuffix}",
|
|
||||||
"condition": "%{JS: '%{TestFrameWork}' === 'Catch2'}",
|
|
||||||
"openInEditor": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "../projects/git.ignore",
|
|
||||||
"target": ".gitignore",
|
"target": ".gitignore",
|
||||||
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
|
"condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}"
|
||||||
}
|
}
|
||||||
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 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileGTestWithCppSuffix})
|
add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileWithCppSuffix})
|
||||||
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
|
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
|
||||||
if (Boost_FOUND)
|
if (Boost_FOUND)
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
@@ -165,7 +165,8 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Gui)
|
|||||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui)
|
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui)
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} %{TestCaseFileWithCppSuffix} main.cpp)
|
add_executable(%{TestCaseName} %{TestCaseFileWithCppSuffix} main.cpp)
|
||||||
|
add_test(NAME %{TestCaseName} COMMAND %{TestCaseName})
|
||||||
|
|
||||||
@if "%{Catch2NeedsQt}" == "true"
|
@if "%{Catch2NeedsQt}" == "true"
|
||||||
target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::Gui)
|
target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::Gui)
|
||||||
|
|||||||
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 "execmenu.h"
|
||||||
#include "historycompleter.h"
|
#include "historycompleter.h"
|
||||||
#include "hostosinfo.h"
|
#include "hostosinfo.h"
|
||||||
|
#include "icon.h"
|
||||||
#include "qtcassert.h"
|
#include "qtcassert.h"
|
||||||
#include "utilsicons.h"
|
|
||||||
#include "utilstr.h"
|
#include "utilstr.h"
|
||||||
|
|
||||||
#include <solutions/spinner/spinner.h>
|
#include <solutions/spinner/spinner.h>
|
||||||
@@ -621,7 +621,6 @@ QString FancyLineEdit::fixInputString(const QString &string)
|
|||||||
|
|
||||||
FancyIconButton::FancyIconButton(QWidget *parent)
|
FancyIconButton::FancyIconButton(QWidget *parent)
|
||||||
: QAbstractButton(parent)
|
: QAbstractButton(parent)
|
||||||
, m_autoHide(false)
|
|
||||||
{
|
{
|
||||||
setCursor(Qt::ArrowCursor);
|
setCursor(Qt::ArrowCursor);
|
||||||
setFocusPolicy(Qt::NoFocus);
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ protected:
|
|||||||
void keyReleaseEvent(QKeyEvent *ke) override;
|
void keyReleaseEvent(QKeyEvent *ke) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float m_iconOpacity;
|
float m_iconOpacity = 1.0f;
|
||||||
bool m_autoHide;
|
bool m_autoHide = false;
|
||||||
QIcon m_icon;
|
QIcon m_icon;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
|
|||||||
d->m_nameLineEdit = new Utils::FancyLineEdit(frame);
|
d->m_nameLineEdit = new Utils::FancyLineEdit(frame);
|
||||||
|
|
||||||
d->m_pathChooser = new Utils::PathChooser(frame);
|
d->m_pathChooser = new Utils::PathChooser(frame);
|
||||||
|
d->m_pathChooser->setObjectName("baseFolder"); // used by Squish
|
||||||
d->m_pathChooser->setExpectedKind(PathChooser::Directory);
|
d->m_pathChooser->setExpectedKind(PathChooser::Directory);
|
||||||
d->m_pathChooser->setDisabled(d->m_forceSubProject);
|
d->m_pathChooser->setDisabled(d->m_forceSubProject);
|
||||||
|
|
||||||
|
|||||||
@@ -261,7 +261,10 @@ bool QuickTestParser::handleQtQuickTest(QPromise<TestParseResultPtr> &promise,
|
|||||||
return false;
|
return false;
|
||||||
const FilePath cppFileName = document->filePath();
|
const FilePath cppFileName = document->filePath();
|
||||||
const FilePath proFile = FilePath::fromString(ppList.at(0)->projectFile);
|
const FilePath proFile = FilePath::fromString(ppList.at(0)->projectFile);
|
||||||
|
{
|
||||||
|
QWriteLocker lock(&m_parseLock);
|
||||||
m_mainCppFiles.insert(cppFileName, proFile);
|
m_mainCppFiles.insert(cppFileName, proFile);
|
||||||
|
}
|
||||||
const FilePath srcDir = FilePath::fromString(quickTestSrcDir(cppFileName));
|
const FilePath srcDir = FilePath::fromString(quickTestSrcDir(cppFileName));
|
||||||
if (srcDir.isEmpty())
|
if (srcDir.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
@@ -340,13 +343,13 @@ QuickTestParser::QuickTestParser(ITestFramework *framework)
|
|||||||
void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
|
void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
|
||||||
{
|
{
|
||||||
m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
|
m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot();
|
||||||
|
QWriteLocker lock(&m_parseLock); // should not be necessary
|
||||||
if (!fullParse) {
|
if (!fullParse) {
|
||||||
// in a full parse we get the correct entry points by the respective main
|
// in a full parse we get the correct entry points by the respective main
|
||||||
m_proFilesForQmlFiles = QuickTestUtils::proFilesForQmlFiles(framework(), filesToParse);
|
m_proFilesForQmlFiles = QuickTestUtils::proFilesForQmlFiles(framework(), filesToParse);
|
||||||
// get rid of cached main cpp files that are going to get processed anyhow
|
// get rid of cached main cpp files that are going to get processed anyhow
|
||||||
for (const FilePath &file : filesToParse) {
|
for (const FilePath &file : filesToParse) {
|
||||||
if (m_mainCppFiles.contains(file)) {
|
if (m_mainCppFiles.remove(file) == 1) {
|
||||||
m_mainCppFiles.remove(file);
|
|
||||||
if (m_mainCppFiles.isEmpty())
|
if (m_mainCppFiles.isEmpty())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -355,6 +358,7 @@ void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse)
|
|||||||
// get rid of all cached main cpp files
|
// get rid of all cached main cpp files
|
||||||
m_mainCppFiles.clear();
|
m_mainCppFiles.clear();
|
||||||
}
|
}
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
m_checkForDerivedTests = theQtTestFramework().quickCheckForDerivedTests();
|
m_checkForDerivedTests = theQtTestFramework().quickCheckForDerivedTests();
|
||||||
|
|
||||||
@@ -399,9 +403,10 @@ bool QuickTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
|
|||||||
return handleQtQuickTest(promise, cppdoc, framework());
|
return handleQtQuickTest(promise, cppdoc, framework());
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName) const
|
FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName)
|
||||||
{
|
{
|
||||||
return m_mainCppFiles.contains(fileName) ? m_mainCppFiles.value(fileName) : FilePath();
|
QReadLocker lock(&m_parseLock);
|
||||||
|
return m_mainCppFiles.value(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Autotest::Internal
|
} // namespace Autotest::Internal
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <qmljs/qmljsdocument.h>
|
#include <qmljs/qmljsdocument.h>
|
||||||
|
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -28,7 +29,7 @@ public:
|
|||||||
void release() override;
|
void release() override;
|
||||||
bool processDocument(QPromise<TestParseResultPtr> &promise,
|
bool processDocument(QPromise<TestParseResultPtr> &promise,
|
||||||
const Utils::FilePath &fileName) override;
|
const Utils::FilePath &fileName) override;
|
||||||
Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName) const;
|
Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName);
|
||||||
QStringList supportedExtensions() const override { return {"qml"}; };
|
QStringList supportedExtensions() const override { return {"qml"}; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -45,6 +46,7 @@ private:
|
|||||||
QMap<QString, QMap<QString, QDateTime> > m_watchedFiles;
|
QMap<QString, QMap<QString, QDateTime> > m_watchedFiles;
|
||||||
QMap<Utils::FilePath, Utils::FilePath> m_mainCppFiles;
|
QMap<Utils::FilePath, Utils::FilePath> m_mainCppFiles;
|
||||||
QSet<Utils::FilePath> m_prefilteredFiles;
|
QSet<Utils::FilePath> m_prefilteredFiles;
|
||||||
|
QReadWriteLock m_parseLock; // guard for m_mainCppFiles
|
||||||
bool m_checkForDerivedTests = false;
|
bool m_checkForDerivedTests = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -611,7 +611,8 @@ void TestTreeModel::insertItemInParent(TestTreeItem *item, TestTreeItem *root, b
|
|||||||
delete item;
|
delete item;
|
||||||
} else {
|
} else {
|
||||||
// restore former check state if available
|
// restore former check state if available
|
||||||
std::optional<Qt::CheckState> cached = m_checkStateCache->get(item);
|
std::optional<Qt::CheckState> cached = m_checkStateCache ? m_checkStateCache->get(item)
|
||||||
|
: std::optional<Qt::CheckState>{};
|
||||||
if (cached.has_value())
|
if (cached.has_value())
|
||||||
item->setData(0, cached.value(), Qt::CheckStateRole);
|
item->setData(0, cached.value(), Qt::CheckStateRole);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -48,6 +48,27 @@ class TestHandler : public rst::ContentHandler {
|
|||||||
void StartBlock(rst::BlockType type) {
|
void StartBlock(rst::BlockType type) {
|
||||||
std::string tag;
|
std::string tag;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case rst::REFERENCE_LINK:
|
||||||
|
// not used, HandleReferenceLink is used instead
|
||||||
|
break;
|
||||||
|
case rst::H1:
|
||||||
|
tag = "h1";
|
||||||
|
break;
|
||||||
|
case rst::H2:
|
||||||
|
tag = "h2";
|
||||||
|
break;
|
||||||
|
case rst::H3:
|
||||||
|
tag = "h3";
|
||||||
|
break;
|
||||||
|
case rst::H4:
|
||||||
|
tag = "h4";
|
||||||
|
break;
|
||||||
|
case rst::H5:
|
||||||
|
tag = "h5";
|
||||||
|
break;
|
||||||
|
case rst::CODE:
|
||||||
|
tag = "code";
|
||||||
|
break;
|
||||||
case rst::PARAGRAPH:
|
case rst::PARAGRAPH:
|
||||||
tag = "p";
|
tag = "p";
|
||||||
break;
|
break;
|
||||||
@@ -80,8 +101,12 @@ class TestHandler : public rst::ContentHandler {
|
|||||||
content_.append(text, size);
|
content_.append(text, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleDirective(const char *type) {
|
void HandleDirective(const std::string &type, const std::string &name) {
|
||||||
content_ += std::string("<") + type + " />";
|
content_ += std::string("<div class=\"") + name + "\">" + type + "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReferenceLink(const std::string &type, const std::string &text) {
|
||||||
|
content_ += std::string("<a href=\"#") + type + "\">" + text + "</a>";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,6 +118,14 @@ std::string Parse(const char *s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ParserTest, HX) {
|
||||||
|
EXPECT_EQ("<h1>test</h1>", Parse("====\ntest\n===="));
|
||||||
|
EXPECT_EQ("<h2>test</h2>", Parse("test\n===="));
|
||||||
|
EXPECT_EQ("<h3>test</h3>", Parse("test\n----"));
|
||||||
|
EXPECT_EQ("<h4>test</h4>", Parse("test\n^^^^"));
|
||||||
|
EXPECT_EQ("<h5>test</h5>", Parse("test\n\"\"\"\""));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ParserTest, Paragraph) {
|
TEST(ParserTest, Paragraph) {
|
||||||
EXPECT_EQ("<p>test</p>", Parse("test"));
|
EXPECT_EQ("<p>test</p>", Parse("test"));
|
||||||
EXPECT_EQ("<p>test</p>", Parse("\ntest"));
|
EXPECT_EQ("<p>test</p>", Parse("\ntest"));
|
||||||
@@ -143,6 +176,14 @@ TEST(ParserTest, Literal) {
|
|||||||
EXPECT_EQ("<p>::\nabc\ndef</p>", Parse("::\nabc\ndef"));
|
EXPECT_EQ("<p>::\nabc\ndef</p>", Parse("::\nabc\ndef"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ParserTest, InlineCode) {
|
||||||
|
EXPECT_EQ("<p><code>code</code></p>", Parse("``code``"));
|
||||||
|
EXPECT_EQ("<p>`code``</p>", Parse("`code``"));
|
||||||
|
EXPECT_EQ("<p>some <code>code</code></p>", Parse("some ``code``"));
|
||||||
|
EXPECT_EQ("<p><code>code</code> some</p>", Parse("``code`` some"));
|
||||||
|
EXPECT_EQ("<p>some <code>code</code> and more</p>", Parse("some ``code`` and more"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ParserTest, Comment) {
|
TEST(ParserTest, Comment) {
|
||||||
EXPECT_EQ("", Parse(".."));
|
EXPECT_EQ("", Parse(".."));
|
||||||
EXPECT_EQ("", Parse("..\n"));
|
EXPECT_EQ("", Parse("..\n"));
|
||||||
@@ -151,11 +192,49 @@ TEST(ParserTest, Comment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParserTest, Directive) {
|
TEST(ParserTest, Directive) {
|
||||||
EXPECT_EQ("<test />", Parse(".. test::"));
|
EXPECT_EQ("<div class=\"\">test</div>", Parse(".. test::"));
|
||||||
EXPECT_EQ("<test />", Parse(".. test::"));
|
EXPECT_EQ("<div class=\"name\">test</div>", Parse(".. test:: name"));
|
||||||
EXPECT_EQ("<test />", Parse("..\ttest::"));
|
EXPECT_EQ("<div class=\"\">test</div>", Parse(".. test::"));
|
||||||
|
EXPECT_EQ("<div class=\"\">test</div>", Parse("..\ttest::"));
|
||||||
|
|
||||||
|
EXPECT_EQ("<div class=\"to-text\">|from-text| replace</div>", Parse(".. |from-text| replace:: to-text"));
|
||||||
|
|
||||||
|
std::string rst =
|
||||||
|
R"(.. code-block:: c++
|
||||||
|
int main() {
|
||||||
|
if (false)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
})";
|
||||||
|
|
||||||
|
std::string html =
|
||||||
|
R"(<div class="c++">code-block</div><blockquote>int main() {
|
||||||
|
if (false)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}</blockquote>)";
|
||||||
|
|
||||||
|
EXPECT_EQ(html, Parse(rst.c_str()));
|
||||||
|
|
||||||
|
rst =
|
||||||
|
R"(.. note:: This is a cool
|
||||||
|
note. Such a cool note.)";
|
||||||
|
|
||||||
|
html =
|
||||||
|
R"(<div class="">note</div><blockquote>This is a cool
|
||||||
|
note. Such a cool note.</blockquote>)";
|
||||||
|
|
||||||
|
EXPECT_EQ(html, Parse(rst.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ParserTest, ReferenceLinks) {
|
||||||
|
EXPECT_EQ("<p><a href=\"#ref\">info</a></p>", Parse(":ref:`info`"));
|
||||||
|
EXPECT_EQ("<p>some <a href=\"#ref\">info</a></p>", Parse("some :ref:`info`"));
|
||||||
|
EXPECT_EQ("<p>some <a href=\"#ref\">info</a> and more</p>", Parse("some :ref:`info` and more"));
|
||||||
|
EXPECT_EQ("<p><a href=\"#ref\">info</a>.</p>", Parse(":ref:`info`."));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Disable message boxes on assertion failures.
|
// Disable message boxes on assertion failures.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "rstparser.h"
|
#include "rstparser.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -55,15 +56,15 @@ void rst::Parser::SkipSpace() {
|
|||||||
|
|
||||||
std::string rst::Parser::ParseDirectiveType() {
|
std::string rst::Parser::ParseDirectiveType() {
|
||||||
const char *s = ptr_;
|
const char *s = ptr_;
|
||||||
if (!std::isalnum(*s))
|
if (!std::isalnum(*s) && *s != '|')
|
||||||
return std::string();
|
return std::string();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
++s;
|
++s;
|
||||||
if (std::isalnum(*s))
|
if (std::isalnum(*s))
|
||||||
continue;
|
continue;
|
||||||
switch (*s) {
|
switch (*s) {
|
||||||
case '-': case '_': case '+': case ':': case '.':
|
case '-': case '_': case '+': case ':': case '.': case '|':
|
||||||
if (std::isalnum(s[1])) {
|
if (std::isalnum(s[1]) || (*s == '|' && IsSpace(s[1]))) {
|
||||||
++s;
|
++s;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -91,13 +92,28 @@ void rst::Parser::EnterBlock(rst::BlockType &prev_type, rst::BlockType type) {
|
|||||||
void rst::Parser::ParseBlock(
|
void rst::Parser::ParseBlock(
|
||||||
rst::BlockType type, rst::BlockType &prev_type, int indent) {
|
rst::BlockType type, rst::BlockType &prev_type, int indent) {
|
||||||
std::string text;
|
std::string text;
|
||||||
|
|
||||||
|
struct InlineTags {
|
||||||
|
rst::BlockType type;
|
||||||
|
std::size_t pos {};
|
||||||
|
std::string text;
|
||||||
|
std::string type_string;
|
||||||
|
};
|
||||||
|
std::vector<InlineTags> inline_tags;
|
||||||
|
|
||||||
|
bool have_h1 = false;
|
||||||
for (bool first = true; ; first = false) {
|
for (bool first = true; ; first = false) {
|
||||||
const char *line_start = ptr_;
|
const char *line_start = ptr_;
|
||||||
if (!first) {
|
if (!first) {
|
||||||
// Check indentation.
|
// Check indentation.
|
||||||
SkipSpace();
|
SkipSpace();
|
||||||
if (ptr_ - line_start != indent)
|
const int new_indent = ptr_ - line_start;
|
||||||
|
if (new_indent < indent)
|
||||||
break;
|
break;
|
||||||
|
// Restore the indent
|
||||||
|
if (new_indent > indent)
|
||||||
|
std::advance(ptr_, indent - new_indent);
|
||||||
|
|
||||||
if (*ptr_ == '\n') {
|
if (*ptr_ == '\n') {
|
||||||
++ptr_;
|
++ptr_;
|
||||||
break; // Empty line ends the block.
|
break; // Empty line ends the block.
|
||||||
@@ -119,9 +135,17 @@ void rst::Parser::ParseBlock(
|
|||||||
|
|
||||||
// Copy text converting all whitespace characters to spaces.
|
// Copy text converting all whitespace characters to spaces.
|
||||||
text.reserve(end - line_start + 1);
|
text.reserve(end - line_start + 1);
|
||||||
if (!first)
|
if (!first && !have_h1)
|
||||||
text.push_back('\n');
|
text.push_back('\n');
|
||||||
enum {TAB_WIDTH = 8};
|
enum {TAB_WIDTH = 8};
|
||||||
|
|
||||||
|
// Used the sections mapping from https://docs.anaconda.com/restructuredtext/index.html
|
||||||
|
struct {
|
||||||
|
BlockType type;
|
||||||
|
int count = 0;
|
||||||
|
char c = 0;
|
||||||
|
} hx[] = { {H1, 0, '=' }, {H2, 0, '='}, {H3, 0, '-'}, {H4, 0, '^'}, {H5, 0, '\"'}};
|
||||||
|
|
||||||
for (const char *s = line_start; s != end; ++s) {
|
for (const char *s = line_start; s != end; ++s) {
|
||||||
char c = *s;
|
char c = *s;
|
||||||
if (c == '\t') {
|
if (c == '\t') {
|
||||||
@@ -129,10 +153,60 @@ void rst::Parser::ParseBlock(
|
|||||||
TAB_WIDTH - ((indent + s - line_start) % TAB_WIDTH));
|
TAB_WIDTH - ((indent + s - line_start) % TAB_WIDTH));
|
||||||
} else if (IsSpace(c)) {
|
} else if (IsSpace(c)) {
|
||||||
text.push_back(' ');
|
text.push_back(' ');
|
||||||
|
} else if (c == hx[0].c) {
|
||||||
|
++hx[0].count;
|
||||||
|
++hx[1].count;
|
||||||
|
} else if (c == hx[2].c) {
|
||||||
|
++hx[2].count;
|
||||||
|
} else if (c == hx[3].c) {
|
||||||
|
++hx[3].count;
|
||||||
|
} else if (c == hx[4].c) {
|
||||||
|
++hx[4].count;
|
||||||
|
} else if (c == '`') {
|
||||||
|
std::string code_tag_text;
|
||||||
|
if (ParseCode(s, end - s, code_tag_text)) {
|
||||||
|
InlineTags code;
|
||||||
|
code.type = rst::CODE;
|
||||||
|
code.pos = text.size();
|
||||||
|
code.text = code_tag_text;
|
||||||
|
inline_tags.push_back(code);
|
||||||
|
const int tag_size = 4;
|
||||||
|
s = s + code_tag_text.size() + tag_size - 1;
|
||||||
|
} else {
|
||||||
|
text.push_back(*s);
|
||||||
|
}
|
||||||
|
} else if (c == ':') {
|
||||||
|
std::string link_type;
|
||||||
|
std::string link_text;
|
||||||
|
if (ParseReferenceLink(s, end - s, link_type, link_text)) {
|
||||||
|
InlineTags link;
|
||||||
|
link.type = rst::REFERENCE_LINK;
|
||||||
|
link.pos = text.size();
|
||||||
|
link.text = link_text;
|
||||||
|
link.type_string = link_type;
|
||||||
|
inline_tags.push_back(link);
|
||||||
|
const int tag_size = 4;
|
||||||
|
s = s + link_type.size() + link_text.size() + tag_size - 1;
|
||||||
|
} else {
|
||||||
|
text.push_back(*s);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
text.push_back(*s);
|
text.push_back(*s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
if (hx[i].count > 0 && hx[i].count == end - line_start) {
|
||||||
|
// h1 and h2 have the same underline character
|
||||||
|
// only if there was one ontop then is h1 otherwise h2
|
||||||
|
if (i == 0 && first)
|
||||||
|
have_h1 = true;
|
||||||
|
if ((i == 0 && !have_h1) || (i == 1 && have_h1))
|
||||||
|
continue;
|
||||||
|
type = hx[i].type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (*ptr_ == '\n')
|
if (*ptr_ == '\n')
|
||||||
++ptr_;
|
++ptr_;
|
||||||
}
|
}
|
||||||
@@ -144,11 +218,35 @@ void rst::Parser::ParseBlock(
|
|||||||
bool literal = type == PARAGRAPH && EndsWith(text, "::");
|
bool literal = type == PARAGRAPH && EndsWith(text, "::");
|
||||||
if (!literal || text.size() != 2) {
|
if (!literal || text.size() != 2) {
|
||||||
std::size_t size = text.size();
|
std::size_t size = text.size();
|
||||||
|
if (size == 0 && inline_tags.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (literal)
|
if (literal)
|
||||||
--size;
|
--size;
|
||||||
EnterBlock(prev_type, type);
|
EnterBlock(prev_type, type);
|
||||||
handler_->StartBlock(type);
|
handler_->StartBlock(type);
|
||||||
|
|
||||||
|
if (inline_tags.size() == 0) {
|
||||||
handler_->HandleText(text.c_str(), size);
|
handler_->HandleText(text.c_str(), size);
|
||||||
|
} else {
|
||||||
|
std::size_t start = 0;
|
||||||
|
for (const InlineTags &in : inline_tags) {
|
||||||
|
if (in.pos > start)
|
||||||
|
handler_->HandleText(text.c_str() + start, in.pos - start);
|
||||||
|
if (in.type == rst::REFERENCE_LINK) {
|
||||||
|
handler_->HandleReferenceLink(in.type_string, in.text);
|
||||||
|
} else {
|
||||||
|
handler_->StartBlock(in.type);
|
||||||
|
handler_->HandleText(in.text.c_str(), in.text.size());
|
||||||
|
handler_->EndBlock();
|
||||||
|
}
|
||||||
|
start = in.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < size)
|
||||||
|
handler_->HandleText(text.c_str() + start, size - start);
|
||||||
|
}
|
||||||
|
|
||||||
handler_->EndBlock();
|
handler_->EndBlock();
|
||||||
}
|
}
|
||||||
if (literal) {
|
if (literal) {
|
||||||
@@ -191,6 +289,58 @@ void rst::Parser::ParseLineBlock(rst::BlockType &prev_type, int indent) {
|
|||||||
handler_->EndBlock();
|
handler_->EndBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rst::Parser::ParseCode(const char *s, std::size_t size, std::string &code)
|
||||||
|
{
|
||||||
|
// It requires at least four ticks ``text``
|
||||||
|
if (s[0] != '`' || s[1] != '`')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (size < 4)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::size_t start_pos = 2;
|
||||||
|
std::size_t end_pos = 0;
|
||||||
|
for (std::size_t i = start_pos; i < size - 1; ++i) {
|
||||||
|
if (s[i] == '`' && s[i + 1] == '`') {
|
||||||
|
end_pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_pos == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
code.assign(s + start_pos, end_pos - start_pos);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rst::Parser::ParseReferenceLink(const char *s, std::size_t size, std::string &type, std::string &text)
|
||||||
|
{
|
||||||
|
// :type:`text`
|
||||||
|
if (size < 4)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto start_type_tag = s + 1;
|
||||||
|
auto end_type_tag = std::find(start_type_tag, s + size, ':');
|
||||||
|
if (end_type_tag == s + size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
type.assign(start_type_tag, end_type_tag - start_type_tag);
|
||||||
|
|
||||||
|
if (*(end_type_tag + 1) != '`')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto start_text_tag = end_type_tag + 2;
|
||||||
|
auto end_text_tag = std::find(start_text_tag, s + size, '`');
|
||||||
|
if (end_text_tag == s + size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
text.assign(start_text_tag, end_text_tag - start_text_tag);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void rst::Parser::Parse(const char *s) {
|
void rst::Parser::Parse(const char *s) {
|
||||||
BlockType prev_type = PARAGRAPH;
|
BlockType prev_type = PARAGRAPH;
|
||||||
ptr_ = s;
|
ptr_ = s;
|
||||||
@@ -214,7 +364,28 @@ void rst::Parser::Parse(const char *s) {
|
|||||||
std::string type = ParseDirectiveType();
|
std::string type = ParseDirectiveType();
|
||||||
if (!type.empty() && ptr_[0] == ':' && ptr_[1] == ':') {
|
if (!type.empty() && ptr_[0] == ':' && ptr_[1] == ':') {
|
||||||
ptr_ += 2;
|
ptr_ += 2;
|
||||||
handler_->HandleDirective(type.c_str());
|
|
||||||
|
const char* after_directive = ptr_;
|
||||||
|
|
||||||
|
// Get the name of the directive
|
||||||
|
std::string name;
|
||||||
|
while (*ptr_ && *ptr_ != '\n') {
|
||||||
|
c = *ptr_++;
|
||||||
|
if (!IsSpace(c))
|
||||||
|
name.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special case for ".. note::" which can start directly after the ::
|
||||||
|
if (type == "note" && name.size() > 0) {
|
||||||
|
ptr_ = after_directive;
|
||||||
|
SkipSpace();
|
||||||
|
handler_->HandleDirective(type, "");
|
||||||
|
|
||||||
|
ParseBlock(BLOCK_QUOTE, prev_type, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler_->HandleDirective(type, name);
|
||||||
}
|
}
|
||||||
// Skip everything till the end of the line.
|
// Skip everything till the end of the line.
|
||||||
while (*ptr_ && *ptr_ != '\n')
|
while (*ptr_ && *ptr_ != '\n')
|
||||||
|
|||||||
@@ -35,6 +35,13 @@
|
|||||||
namespace rst {
|
namespace rst {
|
||||||
|
|
||||||
enum BlockType {
|
enum BlockType {
|
||||||
|
H1,
|
||||||
|
H2,
|
||||||
|
H3,
|
||||||
|
H4,
|
||||||
|
H5,
|
||||||
|
CODE,
|
||||||
|
REFERENCE_LINK,
|
||||||
PARAGRAPH,
|
PARAGRAPH,
|
||||||
LINE_BLOCK,
|
LINE_BLOCK,
|
||||||
BLOCK_QUOTE,
|
BLOCK_QUOTE,
|
||||||
@@ -58,7 +65,10 @@ class ContentHandler {
|
|||||||
virtual void HandleText(const char *text, std::size_t size) = 0;
|
virtual void HandleText(const char *text, std::size_t size) = 0;
|
||||||
|
|
||||||
// Receives notification of a directive.
|
// Receives notification of a directive.
|
||||||
virtual void HandleDirective(const char *type) = 0;
|
virtual void HandleDirective(const std::string &type, const std::string &name) = 0;
|
||||||
|
|
||||||
|
// Receives notification of a link.
|
||||||
|
virtual void HandleReferenceLink(const std::string &type, const std::string &text) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A parser for a subset of reStructuredText.
|
// A parser for a subset of reStructuredText.
|
||||||
@@ -85,6 +95,12 @@ class Parser {
|
|||||||
// Parses a line block.
|
// Parses a line block.
|
||||||
void ParseLineBlock(rst::BlockType &prev_type, int indent);
|
void ParseLineBlock(rst::BlockType &prev_type, int indent);
|
||||||
|
|
||||||
|
// Parses inline ``code``
|
||||||
|
bool ParseCode(const char* s, std::size_t size, std::string &code);
|
||||||
|
|
||||||
|
// Parses :reference:`link`
|
||||||
|
bool ParseReferenceLink(const char* s, std::size_t size, std::string &type, std::string &text);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Parser(ContentHandler *h) : handler_(h), ptr_(0) {}
|
explicit Parser(ContentHandler *h) : handler_(h), ptr_(0) {}
|
||||||
|
|
||||||
@@ -94,4 +110,3 @@ class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // RSTPARSER_H_
|
#endif // RSTPARSER_H_
|
||||||
|
|
||||||
|
|||||||
@@ -46,4 +46,5 @@ add_qtc_plugin(CMakeProjectManager
|
|||||||
3rdparty/cmake/cmListFileCache.cxx
|
3rdparty/cmake/cmListFileCache.cxx
|
||||||
3rdparty/cmake/cmListFileLexer.cxx
|
3rdparty/cmake/cmListFileLexer.cxx
|
||||||
3rdparty/cmake/cmListFileCache.h
|
3rdparty/cmake/cmListFileCache.h
|
||||||
|
3rdparty/rstparser/rstparser.cc 3rdparty/rstparser/rstparser.h
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
#include "cmakebuildsystem.h"
|
#include "cmakebuildsystem.h"
|
||||||
#include "cmakefilecompletionassist.h"
|
#include "cmakefilecompletionassist.h"
|
||||||
#include "cmakeindenter.h"
|
#include "cmakeindenter.h"
|
||||||
#include "cmakekitaspect.h"
|
|
||||||
#include "cmakeproject.h"
|
|
||||||
#include "cmakeprojectconstants.h"
|
#include "cmakeprojectconstants.h"
|
||||||
|
|
||||||
#include "3rdparty/cmake/cmListFileCache.h"
|
#include "3rdparty/cmake/cmListFileCache.h"
|
||||||
@@ -54,13 +52,7 @@ public:
|
|||||||
|
|
||||||
CMakeEditor::CMakeEditor()
|
CMakeEditor::CMakeEditor()
|
||||||
{
|
{
|
||||||
CMakeTool *tool = nullptr;
|
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||||
if (auto bs = ProjectTree::currentBuildSystem())
|
|
||||||
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
|
|
||||||
if (!tool)
|
|
||||||
tool = CMakeToolManager::defaultCMakeTool();
|
|
||||||
|
|
||||||
if (tool)
|
|
||||||
m_keywords = tool->keywords();
|
m_keywords = tool->keywords();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,6 +313,7 @@ class CMakeHoverHandler : public TextEditor::BaseHoverHandler
|
|||||||
{
|
{
|
||||||
mutable CMakeKeywords m_keywords;
|
mutable CMakeKeywords m_keywords;
|
||||||
QString m_helpToolTip;
|
QString m_helpToolTip;
|
||||||
|
QString m_contextHelp;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const CMakeKeywords &keywords() const;
|
const CMakeKeywords &keywords() const;
|
||||||
@@ -333,22 +326,13 @@ public:
|
|||||||
|
|
||||||
const CMakeKeywords &CMakeHoverHandler::keywords() const
|
const CMakeKeywords &CMakeHoverHandler::keywords() const
|
||||||
{
|
{
|
||||||
if (m_keywords.functions.isEmpty()) {
|
if (m_keywords.functions.isEmpty())
|
||||||
CMakeTool *tool = nullptr;
|
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||||
if (auto bs = ProjectTree::currentBuildSystem())
|
|
||||||
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
|
|
||||||
if (!tool)
|
|
||||||
tool = CMakeToolManager::defaultCMakeTool();
|
|
||||||
|
|
||||||
if (tool)
|
|
||||||
m_keywords = tool->keywords();
|
m_keywords = tool->keywords();
|
||||||
}
|
|
||||||
|
|
||||||
return m_keywords;
|
return m_keywords;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString readFirstParagraphs(const QString &element, const FilePath &helpFile);
|
|
||||||
|
|
||||||
void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
||||||
int pos,
|
int pos,
|
||||||
ReportPriority report)
|
ReportPriority report)
|
||||||
@@ -360,24 +344,39 @@ void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget
|
|||||||
const QString word = Utils::Text::wordUnderCursor(cursor);
|
const QString word = Utils::Text::wordUnderCursor(cursor);
|
||||||
|
|
||||||
FilePath helpFile;
|
FilePath helpFile;
|
||||||
for (const auto &map : {keywords().functions,
|
QString helpCategory;
|
||||||
keywords().variables,
|
struct
|
||||||
keywords().directoryProperties,
|
{
|
||||||
keywords().sourceProperties,
|
const QMap<QString, Utils::FilePath> ↦
|
||||||
keywords().targetProperties,
|
QString helpCategory;
|
||||||
keywords().testProperties,
|
} keywordsListMaps[] = {{keywords().functions, "command"},
|
||||||
keywords().properties,
|
{keywords().variables, "variable"},
|
||||||
keywords().includeStandardModules,
|
{keywords().directoryProperties, "prop_dir"},
|
||||||
keywords().findModules,
|
{keywords().sourceProperties, "prop_sf"},
|
||||||
keywords().policies}) {
|
{keywords().targetProperties, "prop_tgt"},
|
||||||
if (map.contains(word)) {
|
{keywords().testProperties, "prop_test"},
|
||||||
helpFile = map.value(word);
|
{keywords().properties, "prop_gbl"},
|
||||||
|
{keywords().includeStandardModules, "module"},
|
||||||
|
{keywords().findModules, "module"},
|
||||||
|
{keywords().policies, "policy"}};
|
||||||
|
|
||||||
|
for (const auto &pair : keywordsListMaps) {
|
||||||
|
if (pair.map.contains(word)) {
|
||||||
|
helpFile = pair.map.value(word);
|
||||||
|
helpCategory = pair.helpCategory;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_helpToolTip.clear();
|
m_helpToolTip.clear();
|
||||||
if (!helpFile.isEmpty())
|
if (!helpFile.isEmpty())
|
||||||
m_helpToolTip = readFirstParagraphs(word, helpFile);
|
m_helpToolTip = CMakeToolManager::toolTipForRstHelpFile(helpFile);
|
||||||
|
|
||||||
|
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||||
|
m_contextHelp = QString("%1/%2/%3")
|
||||||
|
.arg(tool->documentationUrl(tool->version(),
|
||||||
|
tool->qchFilePath().isEmpty()),
|
||||||
|
helpCategory,
|
||||||
|
word);
|
||||||
|
|
||||||
setPriority(m_helpToolTip.isEmpty() ? Priority_Tooltip : Priority_None);
|
setPriority(m_helpToolTip.isEmpty() ? Priority_Tooltip : Priority_None);
|
||||||
}
|
}
|
||||||
@@ -385,7 +384,7 @@ void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget
|
|||||||
void CMakeHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
|
void CMakeHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point)
|
||||||
{
|
{
|
||||||
if (!m_helpToolTip.isEmpty())
|
if (!m_helpToolTip.isEmpty())
|
||||||
Utils::ToolTip::show(point, m_helpToolTip, Qt::MarkdownText, editorWidget);
|
Utils::ToolTip::show(point, m_helpToolTip, Qt::MarkdownText, editorWidget, m_contextHelp);
|
||||||
else
|
else
|
||||||
Utils::ToolTip::hide();
|
Utils::ToolTip::hide();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
#include "cmakebuildsystem.h"
|
#include "cmakebuildsystem.h"
|
||||||
#include "cmakebuildtarget.h"
|
#include "cmakebuildtarget.h"
|
||||||
#include "cmakekitaspect.h"
|
|
||||||
#include "cmakeproject.h"
|
|
||||||
#include "cmakeprojectconstants.h"
|
#include "cmakeprojectconstants.h"
|
||||||
#include "cmaketool.h"
|
#include "cmaketool.h"
|
||||||
#include "cmaketoolmanager.h"
|
#include "cmaketoolmanager.h"
|
||||||
@@ -143,7 +141,7 @@ static int findPathStart(const AssistInterface *interface)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &icon)
|
static QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &icon)
|
||||||
{
|
{
|
||||||
return transform<QList>(words, [&icon](const QString &word) -> AssistProposalItemInterface * {
|
return transform<QList>(words, [&icon](const QString &word) -> AssistProposalItemInterface * {
|
||||||
AssistProposalItem *item = new AssistProposalItem();
|
AssistProposalItem *item = new AssistProposalItem();
|
||||||
@@ -153,26 +151,12 @@ QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &i
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QString readFirstParagraphs(const QString &element, const FilePath &helpFile)
|
static QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words,
|
||||||
{
|
|
||||||
static QMap<FilePath, QString> map;
|
|
||||||
if (map.contains(helpFile))
|
|
||||||
return map.value(helpFile);
|
|
||||||
|
|
||||||
auto content = helpFile.fileContents(1024).value_or(QByteArray());
|
|
||||||
const QString firstParagraphs
|
|
||||||
= QString("```\n%1\n```").arg(QString::fromUtf8(content.left(content.lastIndexOf("\n"))));
|
|
||||||
|
|
||||||
map[helpFile] = firstParagraphs;
|
|
||||||
return firstParagraphs;
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words,
|
|
||||||
const QIcon &icon)
|
const QIcon &icon)
|
||||||
{
|
{
|
||||||
struct MarkDownAssitProposalItem : public AssistProposalItem
|
struct MarkDownAssitProposalItem : public AssistProposalItem
|
||||||
{
|
{
|
||||||
Qt::TextFormat detailFormat() const { return Qt::MarkdownText; }
|
Qt::TextFormat detailFormat() const override { return Qt::MarkdownText; }
|
||||||
};
|
};
|
||||||
|
|
||||||
QList<AssistProposalItemInterface *> list;
|
QList<AssistProposalItemInterface *> list;
|
||||||
@@ -180,10 +164,10 @@ QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath>
|
|||||||
MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem();
|
MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem();
|
||||||
item->setText(it.key());
|
item->setText(it.key());
|
||||||
if (!it.value().isEmpty())
|
if (!it.value().isEmpty())
|
||||||
item->setDetail(readFirstParagraphs(it.key(), it.value()));
|
item->setDetail(CMakeToolManager::toolTipForRstHelpFile(it.value()));
|
||||||
item->setIcon(icon);
|
item->setIcon(icon);
|
||||||
list << item;
|
list << item;
|
||||||
};
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +187,7 @@ static int addFilePathItems(const AssistInterface *interface,
|
|||||||
|
|
||||||
const QString word = interface->textAt(startPos, interface->position() - startPos);
|
const QString word = interface->textAt(startPos, interface->position() - startPos);
|
||||||
FilePath baseDir = interface->filePath().absoluteFilePath().parentDir();
|
FilePath baseDir = interface->filePath().absoluteFilePath().parentDir();
|
||||||
const int lastSlashPos = word.lastIndexOf(QLatin1Char('/'));
|
const qsizetype lastSlashPos = word.lastIndexOf(QLatin1Char('/'));
|
||||||
|
|
||||||
QString prefix = word;
|
QString prefix = word;
|
||||||
if (lastSlashPos != -1) {
|
if (lastSlashPos != -1) {
|
||||||
@@ -227,7 +211,7 @@ static int addFilePathItems(const AssistInterface *interface,
|
|||||||
return startPos;
|
return startPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByteArray &content)
|
static QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const QByteArray &content)
|
||||||
{
|
{
|
||||||
cmListFile cmakeListFile;
|
cmListFile cmakeListFile;
|
||||||
std::string errorString;
|
std::string errorString;
|
||||||
@@ -258,16 +242,8 @@ IAssistProposal *CMakeFileCompletionAssist::performAsync()
|
|||||||
Project *project = nullptr;
|
Project *project = nullptr;
|
||||||
const FilePath &filePath = interface()->filePath();
|
const FilePath &filePath = interface()->filePath();
|
||||||
if (!filePath.isEmpty() && filePath.isFile()) {
|
if (!filePath.isEmpty() && filePath.isFile()) {
|
||||||
CMakeTool *cmake = nullptr;
|
if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool())
|
||||||
project = static_cast<CMakeProject *>(ProjectManager::projectForFile(filePath));
|
keywords = tool->keywords();
|
||||||
if (project && project->activeTarget())
|
|
||||||
cmake = CMakeKitAspect::cmakeTool(project->activeTarget()->kit());
|
|
||||||
|
|
||||||
if (!cmake)
|
|
||||||
cmake = CMakeToolManager::defaultCMakeTool();
|
|
||||||
|
|
||||||
if (cmake && cmake->isValid())
|
|
||||||
keywords = cmake->keywords();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList buildTargets;
|
QStringList buildTargets;
|
||||||
|
|||||||
@@ -93,13 +93,15 @@ QtcPlugin {
|
|||||||
name: "3rdparty"
|
name: "3rdparty"
|
||||||
cpp.includePaths: base.concat("3rdparty/cmake")
|
cpp.includePaths: base.concat("3rdparty/cmake")
|
||||||
|
|
||||||
prefix: "3rdparty/cmake/"
|
prefix: "3rdparty/"
|
||||||
files: [
|
files: [
|
||||||
"cmListFileCache.cxx",
|
"cmake/cmListFileCache.cxx",
|
||||||
"cmListFileCache.h",
|
"cmake/cmListFileCache.h",
|
||||||
"cmListFileLexer.cxx",
|
"cmake/cmListFileLexer.cxx",
|
||||||
"cmListFileLexer.h",
|
"cmake/cmListFileLexer.h",
|
||||||
"cmStandardLexer.h",
|
"cmake/cmStandardLexer.h",
|
||||||
|
"rstparser/rstparser.cc",
|
||||||
|
"rstparser/rstparser.h"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,17 @@
|
|||||||
#include "cmakespecificsettings.h"
|
#include "cmakespecificsettings.h"
|
||||||
#include "cmaketoolsettingsaccessor.h"
|
#include "cmaketoolsettingsaccessor.h"
|
||||||
|
|
||||||
|
#include "3rdparty/rstparser/rstparser.h"
|
||||||
|
|
||||||
#include <extensionsystem/pluginmanager.h>
|
#include <extensionsystem/pluginmanager.h>
|
||||||
|
|
||||||
#include <coreplugin/helpmanager.h>
|
#include <coreplugin/helpmanager.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/buildsystem.h>
|
||||||
|
#include <projectexplorer/projecttree.h>
|
||||||
|
#include <projectexplorer/target.h>
|
||||||
|
#include <stack>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/pointeralgorithm.h>
|
#include <utils/pointeralgorithm.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
@@ -32,6 +38,137 @@ public:
|
|||||||
Internal::CMakeToolSettingsAccessor m_accessor;
|
Internal::CMakeToolSettingsAccessor m_accessor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class HtmlHandler : public rst::ContentHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::stack<QString> m_tags;
|
||||||
|
|
||||||
|
QStringList m_p;
|
||||||
|
QStringList m_h3;
|
||||||
|
QStringList m_cmake_code;
|
||||||
|
|
||||||
|
QString m_last_directive_type;
|
||||||
|
QString m_last_directive_class;
|
||||||
|
|
||||||
|
void StartBlock(rst::BlockType type) final
|
||||||
|
{
|
||||||
|
QString tag;
|
||||||
|
switch (type) {
|
||||||
|
case rst::REFERENCE_LINK:
|
||||||
|
// not used, HandleReferenceLink is used instead
|
||||||
|
break;
|
||||||
|
case rst::H1:
|
||||||
|
tag = "h1";
|
||||||
|
break;
|
||||||
|
case rst::H2:
|
||||||
|
tag = "h2";
|
||||||
|
break;
|
||||||
|
case rst::H3:
|
||||||
|
tag = "h3";
|
||||||
|
break;
|
||||||
|
case rst::H4:
|
||||||
|
tag = "h4";
|
||||||
|
break;
|
||||||
|
case rst::H5:
|
||||||
|
tag = "h5";
|
||||||
|
break;
|
||||||
|
case rst::CODE:
|
||||||
|
tag = "code";
|
||||||
|
break;
|
||||||
|
case rst::PARAGRAPH:
|
||||||
|
tag = "p";
|
||||||
|
break;
|
||||||
|
case rst::LINE_BLOCK:
|
||||||
|
tag = "pre";
|
||||||
|
break;
|
||||||
|
case rst::BLOCK_QUOTE:
|
||||||
|
if (m_last_directive_type == "code-block" && m_last_directive_class == "cmake")
|
||||||
|
tag = "cmake-code";
|
||||||
|
else
|
||||||
|
tag = "blockquote";
|
||||||
|
break;
|
||||||
|
case rst::BULLET_LIST:
|
||||||
|
tag = "ul";
|
||||||
|
break;
|
||||||
|
case rst::LIST_ITEM:
|
||||||
|
tag = "li";
|
||||||
|
break;
|
||||||
|
case rst::LITERAL_BLOCK:
|
||||||
|
tag = "pre";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == "p")
|
||||||
|
m_p.push_back(QString());
|
||||||
|
if (tag == "h3")
|
||||||
|
m_h3.push_back(QString());
|
||||||
|
if (tag == "cmake-code")
|
||||||
|
m_cmake_code.push_back(QString());
|
||||||
|
|
||||||
|
if (tag == "code" && m_tags.top() == "p")
|
||||||
|
m_p.last().append("`");
|
||||||
|
|
||||||
|
m_tags.push(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndBlock() final
|
||||||
|
{
|
||||||
|
// Add a new "p" collector for any `code` markup that comes afterwads
|
||||||
|
// since we are insterested only in the first paragraph.
|
||||||
|
if (m_tags.top() == "p")
|
||||||
|
m_p.push_back(QString());
|
||||||
|
|
||||||
|
if (m_tags.top() == "code" && !m_p.isEmpty()) {
|
||||||
|
m_tags.pop();
|
||||||
|
|
||||||
|
if (m_tags.size() > 0 && m_tags.top() == "p")
|
||||||
|
m_p.last().append("`");
|
||||||
|
} else {
|
||||||
|
m_tags.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleText(const char *text, std::size_t size) final
|
||||||
|
{
|
||||||
|
if (m_last_directive_type.endsWith("replace"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString str = QString::fromUtf8(text, size);
|
||||||
|
|
||||||
|
if (m_tags.top() == "h3")
|
||||||
|
m_h3.last().append(str);
|
||||||
|
if (m_tags.top() == "p")
|
||||||
|
m_p.last().append(str);
|
||||||
|
if (m_tags.top() == "cmake-code")
|
||||||
|
m_cmake_code.last().append(str);
|
||||||
|
if (m_tags.top() == "code" && !m_p.isEmpty())
|
||||||
|
m_p.last().append(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleDirective(const std::string &type, const std::string &name) final
|
||||||
|
{
|
||||||
|
m_last_directive_type = QString::fromStdString(type);
|
||||||
|
m_last_directive_class = QString::fromStdString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleReferenceLink(const std::string &type, const std::string &text) final
|
||||||
|
{
|
||||||
|
Q_UNUSED(type)
|
||||||
|
if (!m_p.isEmpty())
|
||||||
|
m_p.last().append(QString::fromStdString(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString content() const
|
||||||
|
{
|
||||||
|
const QString title = m_h3.isEmpty() ? QString() : m_h3.first();
|
||||||
|
const QString description = m_p.isEmpty() ? QString() : m_p.first();
|
||||||
|
const QString cmakeCode = m_cmake_code.isEmpty() ? QString() : m_cmake_code.first();
|
||||||
|
|
||||||
|
return QString("### %1\n\n%2\n\n````\n%3\n````").arg(title, description, cmakeCode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static CMakeToolManagerPrivate *d = nullptr;
|
static CMakeToolManagerPrivate *d = nullptr;
|
||||||
CMakeToolManager *CMakeToolManager::m_instance = nullptr;
|
CMakeToolManager *CMakeToolManager::m_instance = nullptr;
|
||||||
|
|
||||||
@@ -108,6 +245,37 @@ void CMakeToolManager::deregisterCMakeTool(const Id &id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CMakeTool *CMakeToolManager::defaultProjectOrDefaultCMakeTool()
|
||||||
|
{
|
||||||
|
static CMakeTool *tool = nullptr;
|
||||||
|
|
||||||
|
auto updateTool = [&] {
|
||||||
|
tool = nullptr;
|
||||||
|
if (auto bs = ProjectExplorer::ProjectTree::currentBuildSystem())
|
||||||
|
tool = CMakeKitAspect::cmakeTool(bs->target()->kit());
|
||||||
|
if (!tool)
|
||||||
|
tool = CMakeToolManager::defaultCMakeTool();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!tool)
|
||||||
|
updateTool();
|
||||||
|
|
||||||
|
QObject::connect(CMakeToolManager::instance(),
|
||||||
|
&CMakeToolManager::cmakeUpdated,
|
||||||
|
CMakeToolManager::instance(),
|
||||||
|
[&]() { updateTool(); });
|
||||||
|
QObject::connect(CMakeToolManager::instance(),
|
||||||
|
&CMakeToolManager::cmakeRemoved,
|
||||||
|
CMakeToolManager::instance(),
|
||||||
|
[&]() { updateTool(); });
|
||||||
|
QObject::connect(CMakeToolManager::instance(),
|
||||||
|
&CMakeToolManager::defaultCMakeChanged,
|
||||||
|
CMakeToolManager::instance(),
|
||||||
|
[&]() { updateTool(); });
|
||||||
|
|
||||||
|
return tool;
|
||||||
|
}
|
||||||
|
|
||||||
CMakeTool *CMakeToolManager::defaultCMakeTool()
|
CMakeTool *CMakeToolManager::defaultCMakeTool()
|
||||||
{
|
{
|
||||||
return findById(d->m_defaultCMake);
|
return findById(d->m_defaultCMake);
|
||||||
@@ -167,6 +335,25 @@ void CMakeToolManager::updateDocumentation()
|
|||||||
Core::HelpManager::registerDocumentation(docs);
|
Core::HelpManager::registerDocumentation(docs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile)
|
||||||
|
{
|
||||||
|
static QHash<FilePath, QString> map;
|
||||||
|
if (map.contains(helpFile))
|
||||||
|
return map.value(helpFile);
|
||||||
|
|
||||||
|
auto content = helpFile.fileContents(1024).value_or(QByteArray());
|
||||||
|
content.replace("\r\n", "\n");
|
||||||
|
|
||||||
|
HtmlHandler handler;
|
||||||
|
rst::Parser parser(&handler);
|
||||||
|
parser.Parse(content.left(content.lastIndexOf('\n')));
|
||||||
|
|
||||||
|
const QString tooltip = handler.content();
|
||||||
|
|
||||||
|
map[helpFile] = tooltip;
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
|
||||||
QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths,
|
QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths,
|
||||||
const QString &detectionSource,
|
const QString &detectionSource,
|
||||||
QString *logMessage)
|
QString *logMessage)
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public:
|
|||||||
static bool registerCMakeTool(std::unique_ptr<CMakeTool> &&tool);
|
static bool registerCMakeTool(std::unique_ptr<CMakeTool> &&tool);
|
||||||
static void deregisterCMakeTool(const Utils::Id &id);
|
static void deregisterCMakeTool(const Utils::Id &id);
|
||||||
|
|
||||||
|
static CMakeTool *defaultProjectOrDefaultCMakeTool();
|
||||||
|
|
||||||
static CMakeTool *defaultCMakeTool();
|
static CMakeTool *defaultCMakeTool();
|
||||||
static void setDefaultCMakeTool(const Utils::Id &id);
|
static void setDefaultCMakeTool(const Utils::Id &id);
|
||||||
static CMakeTool *findByCommand(const Utils::FilePath &command);
|
static CMakeTool *findByCommand(const Utils::FilePath &command);
|
||||||
@@ -40,6 +42,8 @@ public:
|
|||||||
|
|
||||||
static void updateDocumentation();
|
static void updateDocumentation();
|
||||||
|
|
||||||
|
static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
|
QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths,
|
||||||
const QString &detectionSource,
|
const QString &detectionSource,
|
||||||
|
|||||||
@@ -8,17 +8,11 @@
|
|||||||
#include "ctfvisualizertr.h"
|
#include "ctfvisualizertr.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <tracing/timelinemodelaggregator.h>
|
#include <tracing/timelinemodelaggregator.h>
|
||||||
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QList>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
|
|
||||||
namespace CtfVisualizer {
|
namespace CtfVisualizer {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -26,48 +20,6 @@ using json = nlohmann::json;
|
|||||||
|
|
||||||
using namespace Constants;
|
using namespace Constants;
|
||||||
|
|
||||||
|
|
||||||
class CtfJsonParserCallback
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
explicit CtfJsonParserCallback(CtfTraceManager *traceManager)
|
|
||||||
: m_traceManager(traceManager)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool callback(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed)
|
|
||||||
{
|
|
||||||
if ((event == json::parse_event_t::array_start && depth == 0)
|
|
||||||
|| (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) {
|
|
||||||
m_isInTraceArray = true;
|
|
||||||
m_traceArrayDepth = depth;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) {
|
|
||||||
m_isInTraceArray = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) {
|
|
||||||
m_traceManager->addEvent(parsed);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) {
|
|
||||||
// keep outer object and values in trace objects:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// discard any objects outside of trace array:
|
|
||||||
// TODO: parse other data, e.g. stack frames
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
CtfTraceManager *m_traceManager;
|
|
||||||
|
|
||||||
bool m_isInTraceArray = false;
|
|
||||||
int m_traceArrayDepth = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
CtfTraceManager::CtfTraceManager(QObject *parent,
|
CtfTraceManager::CtfTraceManager(QObject *parent,
|
||||||
Timeline::TimelineModelAggregator *modelAggregator,
|
Timeline::TimelineModelAggregator *modelAggregator,
|
||||||
CtfStatisticsModel *statisticsModel)
|
CtfStatisticsModel *statisticsModel)
|
||||||
@@ -75,7 +27,6 @@ CtfTraceManager::CtfTraceManager(QObject *parent,
|
|||||||
, m_modelAggregator(modelAggregator)
|
, m_modelAggregator(modelAggregator)
|
||||||
, m_statisticsModel(statisticsModel)
|
, m_statisticsModel(statisticsModel)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qint64 CtfTraceManager::traceDuration() const
|
qint64 CtfTraceManager::traceDuration() const
|
||||||
@@ -142,26 +93,6 @@ void CtfTraceManager::addEvent(const json &event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CtfTraceManager::load(const QString &filename)
|
|
||||||
{
|
|
||||||
clearAll();
|
|
||||||
|
|
||||||
std::ifstream file(filename.toStdString());
|
|
||||||
if (!file.is_open()) {
|
|
||||||
QMessageBox::warning(Core::ICore::dialogParent(),
|
|
||||||
Tr::tr("CTF Visualizer"),
|
|
||||||
Tr::tr("Cannot read the CTF file."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CtfJsonParserCallback ctfParser(this);
|
|
||||||
json::parser_callback_t callback = [&ctfParser](int depth, json::parse_event_t event, json &parsed) {
|
|
||||||
return ctfParser.callback(depth, event, parsed);
|
|
||||||
};
|
|
||||||
json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false);
|
|
||||||
file.close();
|
|
||||||
updateStatistics();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CtfTraceManager::finalize()
|
void CtfTraceManager::finalize()
|
||||||
{
|
{
|
||||||
bool userConsentToIgnoreDeepTraces = false;
|
bool userConsentToIgnoreDeepTraces = false;
|
||||||
|
|||||||
@@ -5,13 +5,11 @@
|
|||||||
#include "json/json.hpp"
|
#include "json/json.hpp"
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
namespace Timeline {
|
namespace Timeline { class TimelineModelAggregator; }
|
||||||
class TimelineModelAggregator;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace CtfVisualizer {
|
namespace CtfVisualizer {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -34,7 +32,6 @@ public:
|
|||||||
|
|
||||||
void addEvent(const nlohmann::json &event);
|
void addEvent(const nlohmann::json &event);
|
||||||
|
|
||||||
void load(const QString &filename);
|
|
||||||
void finalize();
|
void finalize();
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
@@ -46,6 +43,9 @@ public:
|
|||||||
void setThreadRestriction(const QString &tid, bool restrictToThisThread);
|
void setThreadRestriction(const QString &tid, bool restrictToThisThread);
|
||||||
bool isRestrictedTo(const QString &tid) const;
|
bool isRestrictedTo(const QString &tid) const;
|
||||||
|
|
||||||
|
void updateStatistics();
|
||||||
|
void clearAll();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void detailsRequested(const QString &title);
|
void detailsRequested(const QString &title);
|
||||||
|
|
||||||
@@ -53,10 +53,6 @@ protected:
|
|||||||
void addModelForThread(const QString &threadId, const QString &processId);
|
void addModelForThread(const QString &threadId, const QString &processId);
|
||||||
void addModelsToAggregator();
|
void addModelsToAggregator();
|
||||||
|
|
||||||
void updateStatistics();
|
|
||||||
|
|
||||||
void clearAll();
|
|
||||||
|
|
||||||
Timeline::TimelineModelAggregator *const m_modelAggregator;
|
Timeline::TimelineModelAggregator *const m_modelAggregator;
|
||||||
CtfStatisticsModel *const m_statisticsModel;
|
CtfStatisticsModel *const m_statisticsModel;
|
||||||
|
|
||||||
|
|||||||
@@ -13,30 +13,29 @@
|
|||||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/progressmanager/progressmanager.h>
|
#include <coreplugin/progressmanager/taskprogress.h>
|
||||||
|
|
||||||
#include <debugger/analyzer/analyzerconstants.h>
|
#include <debugger/analyzer/analyzerconstants.h>
|
||||||
|
|
||||||
|
#include <utils/async.h>
|
||||||
#include <utils/stylehelper.h>
|
#include <utils/stylehelper.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
#include <QAction>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QFutureInterface>
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QThread>
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace CtfVisualizer::Constants;
|
using namespace CtfVisualizer::Constants;
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
namespace CtfVisualizer {
|
namespace CtfVisualizer {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
CtfVisualizerTool::CtfVisualizerTool()
|
CtfVisualizerTool::CtfVisualizerTool()
|
||||||
: QObject (nullptr)
|
: QObject (nullptr)
|
||||||
, m_isLoading(false)
|
|
||||||
, m_loadJson(nullptr)
|
, m_loadJson(nullptr)
|
||||||
, m_traceView(nullptr)
|
, m_traceView(nullptr)
|
||||||
, m_modelAggregator(new Timeline::TimelineModelAggregator(this))
|
, m_modelAggregator(new Timeline::TimelineModelAggregator(this))
|
||||||
@@ -150,34 +149,84 @@ Timeline::TimelineZoomControl *CtfVisualizerTool::zoomControl() const
|
|||||||
return m_zoomControl.get();
|
return m_zoomControl.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CtfVisualizerTool::loadJson(const QString &filename)
|
class CtfJsonParserFunctor
|
||||||
{
|
{
|
||||||
if (m_isLoading)
|
public:
|
||||||
return;
|
CtfJsonParserFunctor(QPromise<nlohmann::json> &promise)
|
||||||
|
: m_promise(promise) {}
|
||||||
|
|
||||||
if (filename.isEmpty()) {
|
bool operator()(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed)
|
||||||
m_isLoading = false;
|
{
|
||||||
|
using json = nlohmann::json;
|
||||||
|
if ((event == json::parse_event_t::array_start && depth == 0)
|
||||||
|
|| (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) {
|
||||||
|
m_isInTraceArray = true;
|
||||||
|
m_traceArrayDepth = depth;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) {
|
||||||
|
m_isInTraceArray = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) {
|
||||||
|
m_promise.addResult(parsed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) {
|
||||||
|
// keep outer object and values in trace objects:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// discard any objects outside of trace array:
|
||||||
|
// TODO: parse other data, e.g. stack frames
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QPromise<nlohmann::json> &m_promise;
|
||||||
|
bool m_isInTraceArray = false;
|
||||||
|
int m_traceArrayDepth = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void load(QPromise<nlohmann::json> &promise, const QString &fileName)
|
||||||
|
{
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
std::ifstream file(fileName.toStdString());
|
||||||
|
if (!file.is_open()) {
|
||||||
|
promise.future().cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_isLoading = true;
|
CtfJsonParserFunctor functor(promise);
|
||||||
|
json::parser_callback_t callback = [&functor](int depth, json::parse_event_t event, json &parsed) {
|
||||||
|
return functor(depth, event, parsed);
|
||||||
|
};
|
||||||
|
|
||||||
auto *futureInterface = new QFutureInterface<void>();
|
|
||||||
auto *task = new QFuture<void>(futureInterface);
|
|
||||||
|
|
||||||
QThread *thread = QThread::create([this, filename, futureInterface]() {
|
|
||||||
try {
|
try {
|
||||||
m_traceManager->load(filename);
|
json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
// nlohmann::json can throw exceptions when requesting type that is wrong
|
// nlohmann::json can throw exceptions when requesting type that is wrong
|
||||||
}
|
}
|
||||||
m_modelAggregator->moveToThread(QApplication::instance()->thread());
|
|
||||||
m_modelAggregator->setParent(this);
|
|
||||||
futureInterface->reportFinished();
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(thread, &QThread::finished, this, [this, thread, task, futureInterface]() {
|
file.close();
|
||||||
// in main thread:
|
}
|
||||||
|
|
||||||
|
void CtfVisualizerTool::loadJson(const QString &fileName)
|
||||||
|
{
|
||||||
|
using namespace Tasking;
|
||||||
|
|
||||||
|
if (m_loader || fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto onSetup = [this, fileName](Async<nlohmann::json> &async) {
|
||||||
|
m_traceManager->clearAll();
|
||||||
|
async.setConcurrentCallData(load, fileName);
|
||||||
|
connect(&async, &AsyncBase::resultReadyAt, this, [this, asyncPtr = &async](int index) {
|
||||||
|
m_traceManager->addEvent(asyncPtr->resultAt(index));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const auto onDone = [this] {
|
||||||
|
m_traceManager->updateStatistics();
|
||||||
if (m_traceManager->isEmpty()) {
|
if (m_traceManager->isEmpty()) {
|
||||||
QMessageBox::warning(Core::ICore::dialogParent(),
|
QMessageBox::warning(Core::ICore::dialogParent(),
|
||||||
Tr::tr("CTF Visualizer"),
|
Tr::tr("CTF Visualizer"),
|
||||||
@@ -189,17 +238,22 @@ void CtfVisualizerTool::loadJson(const QString &filename)
|
|||||||
zoomControl()->setRange(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20);
|
zoomControl()->setRange(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20);
|
||||||
}
|
}
|
||||||
setAvailableThreads(m_traceManager->getSortedThreads());
|
setAvailableThreads(m_traceManager->getSortedThreads());
|
||||||
thread->deleteLater();
|
m_loader.release()->deleteLater();
|
||||||
delete task;
|
};
|
||||||
delete futureInterface;
|
const auto onError = [this] {
|
||||||
m_isLoading = false;
|
QMessageBox::warning(Core::ICore::dialogParent(),
|
||||||
}, Qt::QueuedConnection);
|
Tr::tr("CTF Visualizer"),
|
||||||
|
Tr::tr("Cannot read the CTF file."));
|
||||||
|
m_loader.release()->deleteLater();
|
||||||
|
};
|
||||||
|
|
||||||
m_modelAggregator->setParent(nullptr);
|
const Group recipe { AsyncTask<nlohmann::json>(onSetup) };
|
||||||
m_modelAggregator->moveToThread(thread);
|
m_loader.reset(new TaskTree(recipe));
|
||||||
|
connect(m_loader.get(), &TaskTree::done, this, onDone);
|
||||||
thread->start();
|
connect(m_loader.get(), &TaskTree::errorOccurred, this, onError);
|
||||||
Core::ProgressManager::addTask(*task, Tr::tr("Loading CTF File"), CtfVisualizerTaskLoadJson);
|
auto progress = new TaskProgress(m_loader.get());
|
||||||
|
progress->setDisplayName(Tr::tr("Loading CTF File"));
|
||||||
|
m_loader->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -6,12 +6,15 @@
|
|||||||
#include "ctfvisualizerconstants.h"
|
#include "ctfvisualizerconstants.h"
|
||||||
|
|
||||||
#include <debugger/debuggermainwindow.h>
|
#include <debugger/debuggermainwindow.h>
|
||||||
|
|
||||||
#include <tracing/timelinemodelaggregator.h>
|
#include <tracing/timelinemodelaggregator.h>
|
||||||
#include <tracing/timelinezoomcontrol.h>
|
#include <tracing/timelinezoomcontrol.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
namespace Tasking { class TaskTree; }
|
||||||
|
|
||||||
namespace CtfVisualizer {
|
namespace CtfVisualizer {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -21,7 +24,6 @@ class CtfStatisticsView;
|
|||||||
class CtfTimelineModel;
|
class CtfTimelineModel;
|
||||||
class CtfVisualizerTraceView;
|
class CtfVisualizerTraceView;
|
||||||
|
|
||||||
|
|
||||||
class CtfVisualizerTool : public QObject
|
class CtfVisualizerTool : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -34,7 +36,7 @@ public:
|
|||||||
CtfTraceManager *traceManager() const;
|
CtfTraceManager *traceManager() const;
|
||||||
Timeline::TimelineZoomControl *zoomControl() const;
|
Timeline::TimelineZoomControl *zoomControl() const;
|
||||||
|
|
||||||
void loadJson(const QString &filename);
|
void loadJson(const QString &fileName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createViews();
|
void createViews();
|
||||||
@@ -45,11 +47,11 @@ private:
|
|||||||
void setAvailableThreads(const QList<CtfTimelineModel *> &threads);
|
void setAvailableThreads(const QList<CtfTimelineModel *> &threads);
|
||||||
void toggleThreadRestriction(QAction *action);
|
void toggleThreadRestriction(QAction *action);
|
||||||
|
|
||||||
Utils::Perspective m_perspective{Constants::CtfVisualizerPerspectiveId,
|
Utils::Perspective m_perspective{CtfVisualizer::Constants::CtfVisualizerPerspectiveId,
|
||||||
QCoreApplication::translate("QtC::CtfVisualizer",
|
QCoreApplication::translate("QtC::CtfVisualizer",
|
||||||
"Chrome Trace Format Visualizer")};
|
"Chrome Trace Format Visualizer")};
|
||||||
|
|
||||||
bool m_isLoading;
|
std::unique_ptr<Tasking::TaskTree> m_loader;
|
||||||
QScopedPointer<QAction> m_loadJson;
|
QScopedPointer<QAction> m_loadJson;
|
||||||
|
|
||||||
CtfVisualizerTraceView *m_traceView;
|
CtfVisualizerTraceView *m_traceView;
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ void LldbEngine::setupEngine()
|
|||||||
|
|
||||||
showMessage("STARTING LLDB: " + lldbCmd.toUserOutput());
|
showMessage("STARTING LLDB: " + lldbCmd.toUserOutput());
|
||||||
Environment environment = runParameters().debugger.environment;
|
Environment environment = runParameters().debugger.environment;
|
||||||
|
environment.appendOrSet("QT_CREATOR_LLDB_PROCESS", "1");
|
||||||
environment.appendOrSet("PYTHONUNBUFFERED", "1"); // avoid flushing problem on macOS
|
environment.appendOrSet("PYTHONUNBUFFERED", "1"); // avoid flushing problem on macOS
|
||||||
DebuggerItem::addAndroidLldbPythonEnv(lldbCmd, environment);
|
DebuggerItem::addAndroidLldbPythonEnv(lldbCmd, environment);
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ add_qtc_plugin(MesonProjectManager
|
|||||||
mesoninfoparser.h
|
mesoninfoparser.h
|
||||||
mesonoutputparser.cpp
|
mesonoutputparser.cpp
|
||||||
mesonoutputparser.h
|
mesonoutputparser.h
|
||||||
mesonprocess.cpp
|
|
||||||
mesonprocess.h
|
|
||||||
mesonproject.cpp
|
mesonproject.cpp
|
||||||
mesonproject.h
|
mesonproject.h
|
||||||
mesonprojectimporter.cpp
|
mesonprojectimporter.cpp
|
||||||
|
|||||||
@@ -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",
|
"mesonbuildconfiguration.h",
|
||||||
"mesonbuildsystem.cpp",
|
"mesonbuildsystem.cpp",
|
||||||
"mesonbuildsystem.h",
|
"mesonbuildsystem.h",
|
||||||
"mesonprocess.cpp",
|
|
||||||
"mesonprocess.h",
|
|
||||||
"mesonproject.cpp",
|
"mesonproject.cpp",
|
||||||
"mesonproject.h",
|
"mesonproject.h",
|
||||||
"mesonprojectimporter.cpp",
|
"mesonprojectimporter.cpp",
|
||||||
|
|||||||
@@ -4,33 +4,44 @@
|
|||||||
#include "mesonprojectparser.h"
|
#include "mesonprojectparser.h"
|
||||||
|
|
||||||
#include "mesoninfoparser.h"
|
#include "mesoninfoparser.h"
|
||||||
|
#include "mesonprojectmanagertr.h"
|
||||||
#include "mesonprojectnodes.h"
|
#include "mesonprojectnodes.h"
|
||||||
#include "mesontools.h"
|
#include "mesontools.h"
|
||||||
#include "projecttree.h"
|
#include "projecttree.h"
|
||||||
|
|
||||||
|
#include <coreplugin/messagemanager.h>
|
||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
|
|
||||||
#include <projectexplorer/projectexplorer.h>
|
#include <projectexplorer/projectexplorer.h>
|
||||||
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
#include <projectexplorer/taskhub.h>
|
||||||
|
|
||||||
#include <utils/async.h>
|
#include <utils/async.h>
|
||||||
|
#include <utils/environment.h>
|
||||||
#include <utils/fileinprojectfinder.h>
|
#include <utils/fileinprojectfinder.h>
|
||||||
|
#include <utils/stringutils.h>
|
||||||
#include <QStringList>
|
|
||||||
#include <QTextStream>
|
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include <coreplugin/progressmanager/processprogress.h>
|
||||||
|
|
||||||
|
using namespace Core;
|
||||||
|
using namespace ProjectExplorer;
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
namespace MesonProjectManager {
|
namespace MesonProjectManager {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(mesonProcessLog, "qtc.meson.buildsystem", QtWarningMsg);
|
||||||
|
|
||||||
struct CompilerArgs
|
struct CompilerArgs
|
||||||
{
|
{
|
||||||
QStringList args;
|
QStringList args;
|
||||||
QStringList includePaths;
|
QStringList includePaths;
|
||||||
ProjectExplorer::Macros macros;
|
Macros macros;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::optional<QString> extractValueIfMatches(const QString &arg,
|
static std::optional<QString> extractValueIfMatches(const QString &arg,
|
||||||
const QStringList &candidates)
|
const QStringList &candidates)
|
||||||
{
|
{
|
||||||
for (const auto &flag : candidates) {
|
for (const auto &flag : candidates) {
|
||||||
@@ -40,22 +51,23 @@ inline std::optional<QString> extractValueIfMatches(const QString &arg,
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::optional<QString> extractInclude(const QString &arg)
|
static std::optional<QString> extractInclude(const QString &arg)
|
||||||
{
|
{
|
||||||
return extractValueIfMatches(arg, {"-I", "/I", "-isystem", "-imsvc", "/imsvc"});
|
return extractValueIfMatches(arg, {"-I", "/I", "-isystem", "-imsvc", "/imsvc"});
|
||||||
}
|
}
|
||||||
inline std::optional<ProjectExplorer::Macro> extractMacro(const QString &arg)
|
|
||||||
|
static std::optional<Macro> extractMacro(const QString &arg)
|
||||||
{
|
{
|
||||||
auto define = extractValueIfMatches(arg, {"-D", "/D"});
|
auto define = extractValueIfMatches(arg, {"-D", "/D"});
|
||||||
if (define)
|
if (define)
|
||||||
return ProjectExplorer::Macro::fromKeyValue(define->toLatin1());
|
return Macro::fromKeyValue(define->toLatin1());
|
||||||
auto undef = extractValueIfMatches(arg, {"-U", "/U"});
|
auto undef = extractValueIfMatches(arg, {"-U", "/U"});
|
||||||
if (undef)
|
if (undef)
|
||||||
return ProjectExplorer::Macro(undef->toLatin1(), ProjectExplorer::MacroType::Undefine);
|
return Macro(undef->toLatin1(), MacroType::Undefine);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompilerArgs splitArgs(const QStringList &args)
|
static CompilerArgs splitArgs(const QStringList &args)
|
||||||
{
|
{
|
||||||
CompilerArgs splited;
|
CompilerArgs splited;
|
||||||
for (const QString &arg : args) {
|
for (const QString &arg : args) {
|
||||||
@@ -74,7 +86,7 @@ CompilerArgs splitArgs(const QStringList &args)
|
|||||||
return splited;
|
return splited;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList)
|
static QStringList toAbsolutePath(const FilePath &refPath, QStringList &pathList)
|
||||||
{
|
{
|
||||||
QStringList allAbs;
|
QStringList allAbs;
|
||||||
std::transform(std::cbegin(pathList),
|
std::transform(std::cbegin(pathList),
|
||||||
@@ -86,35 +98,22 @@ QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList
|
|||||||
return allAbs;
|
return allAbs;
|
||||||
}
|
}
|
||||||
|
|
||||||
MesonProjectParser::MesonProjectParser(const Utils::Id &meson,
|
MesonProjectParser::MesonProjectParser(const Id &meson, const Environment &env, Project *project)
|
||||||
Utils::Environment env,
|
|
||||||
ProjectExplorer::Project *project)
|
|
||||||
: m_env{env}
|
: m_env{env}
|
||||||
, m_meson{meson}
|
, m_meson{meson}
|
||||||
, m_projectName{project->displayName()}
|
, m_projectName{project->displayName()}
|
||||||
{
|
{
|
||||||
connect(&m_process, &MesonProcess::finished, this, &MesonProjectParser::processFinished);
|
|
||||||
connect(&m_process,
|
|
||||||
&MesonProcess::readyReadStandardOutput,
|
|
||||||
&m_outputParser,
|
|
||||||
&MesonOutputParser::readStdo);
|
|
||||||
|
|
||||||
// TODO re-think the way all BuildSystem/ProjectParser are tied
|
// TODO re-think the way all BuildSystem/ProjectParser are tied
|
||||||
// I take project info here, I also take build and src dir later from
|
// I take project info here, I also take build and src dir later from
|
||||||
// functions args.
|
// functions args.
|
||||||
auto fileFinder = new Utils::FileInProjectFinder;
|
auto fileFinder = new FileInProjectFinder;
|
||||||
fileFinder->setProjectDirectory(project->projectDirectory());
|
fileFinder->setProjectDirectory(project->projectDirectory());
|
||||||
fileFinder->setProjectFiles(project->files(ProjectExplorer::Project::AllFiles));
|
fileFinder->setProjectFiles(project->files(Project::AllFiles));
|
||||||
m_outputParser.setFileFinder(fileFinder);
|
m_outputParser.setFileFinder(fileFinder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MesonProjectParser::setMesonTool(const Utils::Id &meson)
|
bool MesonProjectParser::configure(const FilePath &sourcePath,
|
||||||
{
|
const FilePath &buildPath,
|
||||||
m_meson = meson;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MesonProjectParser::configure(const Utils::FilePath &sourcePath,
|
|
||||||
const Utils::FilePath &buildPath,
|
|
||||||
const QStringList &args)
|
const QStringList &args)
|
||||||
{
|
{
|
||||||
m_introType = IntroDataType::file;
|
m_introType = IntroDataType::file;
|
||||||
@@ -126,18 +125,18 @@ bool MesonProjectParser::configure(const Utils::FilePath &sourcePath,
|
|||||||
m_pendingCommands.enqueue(
|
m_pendingCommands.enqueue(
|
||||||
std::make_tuple(MesonTools::mesonWrapper(m_meson)->regenerate(sourcePath, buildPath),
|
std::make_tuple(MesonTools::mesonWrapper(m_meson)->regenerate(sourcePath, buildPath),
|
||||||
false));
|
false));
|
||||||
return m_process.run(cmd, m_env, m_projectName);
|
return run(cmd, m_env, m_projectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MesonProjectParser::wipe(const Utils::FilePath &sourcePath,
|
bool MesonProjectParser::wipe(const FilePath &sourcePath,
|
||||||
const Utils::FilePath &buildPath,
|
const FilePath &buildPath,
|
||||||
const QStringList &args)
|
const QStringList &args)
|
||||||
{
|
{
|
||||||
return setup(sourcePath, buildPath, args, true);
|
return setup(sourcePath, buildPath, args, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MesonProjectParser::setup(const Utils::FilePath &sourcePath,
|
bool MesonProjectParser::setup(const FilePath &sourcePath,
|
||||||
const Utils::FilePath &buildPath,
|
const FilePath &buildPath,
|
||||||
const QStringList &args,
|
const QStringList &args,
|
||||||
bool forceWipe)
|
bool forceWipe)
|
||||||
{
|
{
|
||||||
@@ -149,10 +148,10 @@ bool MesonProjectParser::setup(const Utils::FilePath &sourcePath,
|
|||||||
if (forceWipe || isSetup(buildPath))
|
if (forceWipe || isSetup(buildPath))
|
||||||
cmdArgs << "--wipe";
|
cmdArgs << "--wipe";
|
||||||
auto cmd = MesonTools::mesonWrapper(m_meson)->setup(sourcePath, buildPath, cmdArgs);
|
auto cmd = MesonTools::mesonWrapper(m_meson)->setup(sourcePath, buildPath, cmdArgs);
|
||||||
return m_process.run(cmd, m_env, m_projectName);
|
return run(cmd, m_env, m_projectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MesonProjectParser::parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath)
|
bool MesonProjectParser::parse(const FilePath &sourcePath, const FilePath &buildPath)
|
||||||
{
|
{
|
||||||
m_srcDir = sourcePath;
|
m_srcDir = sourcePath;
|
||||||
m_buildDir = buildPath;
|
m_buildDir = buildPath;
|
||||||
@@ -165,29 +164,29 @@ bool MesonProjectParser::parse(const Utils::FilePath &sourcePath, const Utils::F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MesonProjectParser::parse(const Utils::FilePath &sourcePath)
|
bool MesonProjectParser::parse(const FilePath &sourcePath)
|
||||||
{
|
{
|
||||||
m_srcDir = sourcePath;
|
m_srcDir = sourcePath;
|
||||||
m_introType = IntroDataType::stdo;
|
m_introType = IntroDataType::stdo;
|
||||||
m_outputParser.setSourceDirectory(sourcePath);
|
m_outputParser.setSourceDirectory(sourcePath);
|
||||||
return m_process.run(MesonTools::mesonWrapper(m_meson)->introspect(sourcePath),
|
return run(MesonTools::mesonWrapper(m_meson)->introspect(sourcePath),
|
||||||
m_env,
|
m_env,
|
||||||
m_projectName,
|
m_projectName,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const
|
QList<BuildTargetInfo> MesonProjectParser::appsTargets() const
|
||||||
{
|
{
|
||||||
QList<ProjectExplorer::BuildTargetInfo> apps;
|
QList<BuildTargetInfo> apps;
|
||||||
for (const Target &target : m_parserResult.targets) {
|
for (const Target &target : m_parserResult.targets) {
|
||||||
if (target.type == Target::Type::executable) {
|
if (target.type == Target::Type::executable) {
|
||||||
ProjectExplorer::BuildTargetInfo bti;
|
BuildTargetInfo bti;
|
||||||
bti.displayName = target.name;
|
bti.displayName = target.name;
|
||||||
bti.buildKey = Target::fullName(m_buildDir, target);
|
bti.buildKey = Target::fullName(m_buildDir, target);
|
||||||
bti.displayNameUniquifier = bti.buildKey;
|
bti.displayNameUniquifier = bti.buildKey;
|
||||||
bti.targetFilePath = Utils::FilePath::fromString(target.fileName.first());
|
bti.targetFilePath = FilePath::fromString(target.fileName.first());
|
||||||
bti.workingDirectory = Utils::FilePath::fromString(target.fileName.first()).absolutePath();
|
bti.workingDirectory = FilePath::fromString(target.fileName.first()).absolutePath();
|
||||||
bti.projectFilePath = Utils::FilePath::fromString(target.definedIn);
|
bti.projectFilePath = FilePath::fromString(target.definedIn);
|
||||||
bti.usesTerminal = true;
|
bti.usesTerminal = true;
|
||||||
apps.append(bti);
|
apps.append(bti);
|
||||||
}
|
}
|
||||||
@@ -198,8 +197,8 @@ QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const
|
|||||||
bool MesonProjectParser::startParser()
|
bool MesonProjectParser::startParser()
|
||||||
{
|
{
|
||||||
m_parserFutureResult = Utils::asyncRun(
|
m_parserFutureResult = Utils::asyncRun(
|
||||||
ProjectExplorer::ProjectExplorerPlugin::sharedThreadPool(),
|
ProjectExplorerPlugin::sharedThreadPool(),
|
||||||
[processOutput = m_process.stdOut(), introType = m_introType,
|
[processOutput = m_stdo, introType = m_introType,
|
||||||
buildDir = m_buildDir, srcDir = m_srcDir] {
|
buildDir = m_buildDir, srcDir = m_srcDir] {
|
||||||
if (introType == IntroDataType::file)
|
if (introType == IntroDataType::file)
|
||||||
return extractParserResults(srcDir, MesonInfoParser::parse(buildDir));
|
return extractParserResults(srcDir, MesonInfoParser::parse(buildDir));
|
||||||
@@ -212,7 +211,7 @@ bool MesonProjectParser::startParser()
|
|||||||
}
|
}
|
||||||
|
|
||||||
MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
|
MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
|
||||||
const Utils::FilePath &srcDir, MesonInfoParser::Result &&parserResult)
|
const FilePath &srcDir, MesonInfoParser::Result &&parserResult)
|
||||||
{
|
{
|
||||||
auto rootNode = ProjectTree::buildTree(srcDir,
|
auto rootNode = ProjectTree::buildTree(srcDir,
|
||||||
parserResult.targets,
|
parserResult.targets,
|
||||||
@@ -220,14 +219,21 @@ MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
|
|||||||
return new ParserData{std::move(parserResult), std::move(rootNode)};
|
return new ParserData{std::move(parserResult), std::move(rootNode)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MesonProjectParser::addMissingTargets(QStringList &targetList)
|
static void addMissingTargets(QStringList &targetList)
|
||||||
{
|
{
|
||||||
// Not all targets are listed in introspection data
|
// Not all targets are listed in introspection data
|
||||||
for (const auto &target : additionalTargets()) {
|
static const QString additionalTargets[] {
|
||||||
if (!targetList.contains(target)) {
|
Constants::Targets::all,
|
||||||
|
Constants::Targets::clean,
|
||||||
|
Constants::Targets::install,
|
||||||
|
Constants::Targets::benchmark,
|
||||||
|
Constants::Targets::scan_build
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const QString &target : additionalTargets) {
|
||||||
|
if (!targetList.contains(target))
|
||||||
targetList.append(target);
|
targetList.append(target);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *> &data)
|
void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *> &data)
|
||||||
@@ -245,17 +251,17 @@ void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *>
|
|||||||
emit parsingCompleted(true);
|
emit parsingCompleted(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart(
|
RawProjectPart MesonProjectParser::buildRawPart(
|
||||||
const Target &target,
|
const Target &target,
|
||||||
const Target::SourceGroup &sources,
|
const Target::SourceGroup &sources,
|
||||||
const ProjectExplorer::ToolChain *cxxToolChain,
|
const ToolChain *cxxToolChain,
|
||||||
const ProjectExplorer::ToolChain *cToolChain)
|
const ToolChain *cToolChain)
|
||||||
{
|
{
|
||||||
ProjectExplorer::RawProjectPart part;
|
RawProjectPart part;
|
||||||
part.setDisplayName(target.name);
|
part.setDisplayName(target.name);
|
||||||
part.setBuildSystemTarget(Target::fullName(m_buildDir, target));
|
part.setBuildSystemTarget(Target::fullName(m_buildDir, target));
|
||||||
part.setFiles(sources.sources + sources.generatedSources);
|
part.setFiles(sources.sources + sources.generatedSources);
|
||||||
auto flags = splitArgs(sources.parameters);
|
CompilerArgs flags = splitArgs(sources.parameters);
|
||||||
part.setMacros(flags.macros);
|
part.setMacros(flags.macros);
|
||||||
part.setIncludePaths(toAbsolutePath(m_buildDir, flags.includePaths));
|
part.setIncludePaths(toAbsolutePath(m_buildDir, flags.includePaths));
|
||||||
part.setProjectFileLocation(target.definedIn);
|
part.setProjectFileLocation(target.definedIn);
|
||||||
@@ -267,30 +273,10 @@ ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart(
|
|||||||
return part;
|
return part;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MesonProjectParser::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
RawProjectParts MesonProjectParser::buildProjectParts(
|
||||||
|
const ToolChain *cxxToolChain, const ToolChain *cToolChain)
|
||||||
{
|
{
|
||||||
if (exitCode == 0 && exitStatus == QProcess::NormalExit) {
|
RawProjectParts parts;
|
||||||
if (m_pendingCommands.isEmpty())
|
|
||||||
startParser();
|
|
||||||
else {
|
|
||||||
// see comment near m_pendingCommands declaration
|
|
||||||
std::tuple<Command, bool> args = m_pendingCommands.dequeue();
|
|
||||||
m_process.run(std::get<0>(args), m_env, m_projectName, std::get<1>(args));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (m_introType == IntroDataType::stdo) {
|
|
||||||
auto data = m_process.stdErr();
|
|
||||||
Core::MessageManager::writeSilently(QString::fromLocal8Bit(data));
|
|
||||||
m_outputParser.readStdo(data);
|
|
||||||
}
|
|
||||||
emit parsingCompleted(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProjectExplorer::RawProjectParts MesonProjectParser::buildProjectParts(
|
|
||||||
const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cToolChain)
|
|
||||||
{
|
|
||||||
ProjectExplorer::RawProjectParts parts;
|
|
||||||
for_each_source_group(m_parserResult.targets,
|
for_each_source_group(m_parserResult.targets,
|
||||||
[&parts,
|
[&parts,
|
||||||
&cxxToolChain,
|
&cxxToolChain,
|
||||||
@@ -321,11 +307,111 @@ bool MesonProjectParser::matchesKit(const KitData &kit)
|
|||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MesonProjectParser::usesSameMesonVersion(const Utils::FilePath &buildPath)
|
bool MesonProjectParser::usesSameMesonVersion(const FilePath &buildPath)
|
||||||
{
|
{
|
||||||
auto info = MesonInfoParser::mesonInfo(buildPath);
|
auto info = MesonInfoParser::mesonInfo(buildPath);
|
||||||
auto meson = MesonTools::mesonWrapper(m_meson);
|
auto meson = MesonTools::mesonWrapper(m_meson);
|
||||||
return info && meson && info->mesonVersion == meson->version();
|
return info && meson && info->mesonVersion == meson->version();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MesonProjectParser::run(const Command &command,
|
||||||
|
const Environment &env,
|
||||||
|
const QString &projectName,
|
||||||
|
bool captureStdo)
|
||||||
|
{
|
||||||
|
if (!sanityCheck(command))
|
||||||
|
return false;
|
||||||
|
m_stdo.clear();
|
||||||
|
TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM);
|
||||||
|
setupProcess(command, env, projectName, captureStdo);
|
||||||
|
m_elapsed.start();
|
||||||
|
m_process->start();
|
||||||
|
qCDebug(mesonProcessLog()) << "Starting:" << command.toUserOutput();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MesonProjectParser::handleProcessDone()
|
||||||
|
{
|
||||||
|
if (m_process->result() != ProcessResult::FinishedWithSuccess)
|
||||||
|
TaskHub::addTask(BuildSystemTask{Task::TaskType::Error, m_process->exitMessage()});
|
||||||
|
|
||||||
|
m_stdo = m_process->readAllRawStandardOutput();
|
||||||
|
m_stderr = m_process->readAllRawStandardError();
|
||||||
|
const QString elapsedTime = formatElapsedTime(m_elapsed.elapsed());
|
||||||
|
MessageManager::writeSilently(elapsedTime);
|
||||||
|
|
||||||
|
if (m_process->exitCode() == 0 && m_process->exitStatus() == QProcess::NormalExit) {
|
||||||
|
if (m_pendingCommands.isEmpty())
|
||||||
|
startParser();
|
||||||
|
else {
|
||||||
|
// see comment near m_pendingCommands declaration
|
||||||
|
std::tuple<Command, bool> args = m_pendingCommands.dequeue();
|
||||||
|
run(std::get<0>(args), m_env, m_projectName, std::get<1>(args));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_introType == IntroDataType::stdo) {
|
||||||
|
MessageManager::writeSilently(QString::fromLocal8Bit(m_stderr));
|
||||||
|
m_outputParser.readStdo(m_stderr);
|
||||||
|
}
|
||||||
|
emit parsingCompleted(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MesonProjectParser::setupProcess(const Command &command, const Environment &env,
|
||||||
|
const QString &projectName, bool captureStdo)
|
||||||
|
{
|
||||||
|
if (m_process)
|
||||||
|
m_process.release()->deleteLater();
|
||||||
|
m_process.reset(new Process);
|
||||||
|
connect(m_process.get(), &Process::done, this, &MesonProjectParser::handleProcessDone);
|
||||||
|
if (!captureStdo) {
|
||||||
|
connect(m_process.get(), &Process::readyReadStandardOutput,
|
||||||
|
this, &MesonProjectParser::processStandardOutput);
|
||||||
|
connect(m_process.get(), &Process::readyReadStandardError,
|
||||||
|
this, &MesonProjectParser::processStandardError);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_process->setWorkingDirectory(command.workDir());
|
||||||
|
m_process->setEnvironment(env);
|
||||||
|
MessageManager::writeFlashing(Tr::tr("Running %1 in %2.")
|
||||||
|
.arg(command.toUserOutput(), command.workDir().toUserOutput()));
|
||||||
|
m_process->setCommand(command.cmdLine());
|
||||||
|
m_process->setTimeoutS(10);
|
||||||
|
ProcessProgress *progress = new ProcessProgress(m_process.get());
|
||||||
|
progress->setDisplayName(Tr::tr("Configuring \"%1\".").arg(projectName));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MesonProjectParser::sanityCheck(const Command &command) const
|
||||||
|
{
|
||||||
|
const auto &exe = command.cmdLine().executable();
|
||||||
|
if (!exe.exists()) {
|
||||||
|
//Should only reach this point if Meson exe is removed while a Meson project is opened
|
||||||
|
TaskHub::addTask(
|
||||||
|
BuildSystemTask{Task::TaskType::Error,
|
||||||
|
Tr::tr("Executable does not exist: %1").arg(exe.toUserOutput())});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!exe.toFileInfo().isExecutable()) {
|
||||||
|
TaskHub::addTask(
|
||||||
|
BuildSystemTask{Task::TaskType::Error,
|
||||||
|
Tr::tr("Command is not executable: %1").arg(exe.toUserOutput())});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MesonProjectParser::processStandardOutput()
|
||||||
|
{
|
||||||
|
const auto data = m_process->readAllRawStandardOutput();
|
||||||
|
MessageManager::writeSilently(QString::fromLocal8Bit(data));
|
||||||
|
m_outputParser.readStdo(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MesonProjectParser::processStandardError()
|
||||||
|
{
|
||||||
|
MessageManager::writeSilently(QString::fromLocal8Bit(m_process->readAllRawStandardError()));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace MesonProjectManager
|
} // namespace MesonProjectManager
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "kitdata.h"
|
#include "kitdata.h"
|
||||||
#include "mesoninfoparser.h"
|
#include "mesoninfoparser.h"
|
||||||
#include "mesonoutputparser.h"
|
#include "mesonoutputparser.h"
|
||||||
#include "mesonprocess.h"
|
|
||||||
#include "mesonprojectnodes.h"
|
#include "mesonprojectnodes.h"
|
||||||
#include "mesonwrapper.h"
|
#include "mesonwrapper.h"
|
||||||
|
|
||||||
@@ -14,9 +13,6 @@
|
|||||||
#include <projectexplorer/kit.h>
|
#include <projectexplorer/kit.h>
|
||||||
#include <projectexplorer/rawprojectpart.h>
|
#include <projectexplorer/rawprojectpart.h>
|
||||||
|
|
||||||
#include <utils/environment.h>
|
|
||||||
#include <utils/fileutils.h>
|
|
||||||
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
|
|
||||||
@@ -26,6 +22,7 @@ namespace Internal {
|
|||||||
class MesonProjectParser : public QObject
|
class MesonProjectParser : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
enum class IntroDataType { file, stdo };
|
enum class IntroDataType { file, stdo };
|
||||||
struct ParserData
|
struct ParserData
|
||||||
{
|
{
|
||||||
@@ -34,8 +31,10 @@ class MesonProjectParser : public QObject
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MesonProjectParser(const Utils::Id &meson, Utils::Environment env, ProjectExplorer::Project* project);
|
MesonProjectParser(const Utils::Id &meson,
|
||||||
void setMesonTool(const Utils::Id &meson);
|
const Utils::Environment &env,
|
||||||
|
ProjectExplorer::Project *project);
|
||||||
|
|
||||||
bool configure(const Utils::FilePath &sourcePath,
|
bool configure(const Utils::FilePath &sourcePath,
|
||||||
const Utils::FilePath &buildPath,
|
const Utils::FilePath &buildPath,
|
||||||
const QStringList &args);
|
const QStringList &args);
|
||||||
@@ -49,22 +48,11 @@ public:
|
|||||||
bool parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath);
|
bool parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath);
|
||||||
bool parse(const Utils::FilePath &sourcePath);
|
bool parse(const Utils::FilePath &sourcePath);
|
||||||
|
|
||||||
Q_SIGNAL void parsingCompleted(bool success);
|
|
||||||
|
|
||||||
std::unique_ptr<MesonProjectNode> takeProjectNode() { return std::move(m_rootNode); }
|
std::unique_ptr<MesonProjectNode> takeProjectNode() { return std::move(m_rootNode); }
|
||||||
|
|
||||||
inline const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; };
|
const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; };
|
||||||
inline const TargetsList &targets() const { return m_parserResult.targets; }
|
const TargetsList &targets() const { return m_parserResult.targets; }
|
||||||
inline const QStringList &targetsNames() const { return m_targetsNames; }
|
const QStringList &targetsNames() const { return m_targetsNames; }
|
||||||
|
|
||||||
static inline QStringList additionalTargets()
|
|
||||||
{
|
|
||||||
return QStringList{Constants::Targets::all,
|
|
||||||
Constants::Targets::clean,
|
|
||||||
Constants::Targets::install,
|
|
||||||
Constants::Targets::benchmark,
|
|
||||||
Constants::Targets::scan_build};
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<ProjectExplorer::BuildTargetInfo> appsTargets() const;
|
QList<ProjectExplorer::BuildTargetInfo> appsTargets() const;
|
||||||
|
|
||||||
@@ -72,26 +60,27 @@ public:
|
|||||||
const ProjectExplorer::ToolChain *cxxToolChain,
|
const ProjectExplorer::ToolChain *cxxToolChain,
|
||||||
const ProjectExplorer::ToolChain *cToolChain);
|
const ProjectExplorer::ToolChain *cToolChain);
|
||||||
|
|
||||||
inline void setEnvironment(const Utils::Environment &environment) { m_env = environment; }
|
void setEnvironment(const Utils::Environment &environment) { m_env = environment; }
|
||||||
|
|
||||||
inline void setQtVersion(Utils::QtMajorVersion v) { m_qtVersion = v; }
|
void setQtVersion(Utils::QtMajorVersion v) { m_qtVersion = v; }
|
||||||
|
|
||||||
bool matchesKit(const KitData &kit);
|
bool matchesKit(const KitData &kit);
|
||||||
|
|
||||||
bool usesSameMesonVersion(const Utils::FilePath &buildPath);
|
bool usesSameMesonVersion(const Utils::FilePath &buildPath);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void parsingCompleted(bool success);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool startParser();
|
bool startParser();
|
||||||
static ParserData *extractParserResults(const Utils::FilePath &srcDir,
|
static ParserData *extractParserResults(const Utils::FilePath &srcDir,
|
||||||
MesonInfoParser::Result &&parserResult);
|
MesonInfoParser::Result &&parserResult);
|
||||||
static void addMissingTargets(QStringList &targetList);
|
|
||||||
void update(const QFuture<ParserData *> &data);
|
void update(const QFuture<ParserData *> &data);
|
||||||
ProjectExplorer::RawProjectPart buildRawPart(const Target &target,
|
ProjectExplorer::RawProjectPart buildRawPart(const Target &target,
|
||||||
const Target::SourceGroup &sources,
|
const Target::SourceGroup &sources,
|
||||||
const ProjectExplorer::ToolChain *cxxToolChain,
|
const ProjectExplorer::ToolChain *cxxToolChain,
|
||||||
const ProjectExplorer::ToolChain *cToolChain);
|
const ProjectExplorer::ToolChain *cToolChain);
|
||||||
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
|
||||||
MesonProcess m_process;
|
|
||||||
MesonOutputParser m_outputParser;
|
MesonOutputParser m_outputParser;
|
||||||
Utils::Environment m_env;
|
Utils::Environment m_env;
|
||||||
Utils::Id m_meson;
|
Utils::Id m_meson;
|
||||||
@@ -108,6 +97,22 @@ private:
|
|||||||
// maybe moving meson to build step could make this class simpler
|
// maybe moving meson to build step could make this class simpler
|
||||||
// also this should ease command dependencies
|
// also this should ease command dependencies
|
||||||
QQueue<std::tuple<Command, bool>> m_pendingCommands;
|
QQueue<std::tuple<Command, bool>> m_pendingCommands;
|
||||||
|
|
||||||
|
bool run(const Command &command, const Utils::Environment &env,
|
||||||
|
const QString &projectName, bool captureStdo = false);
|
||||||
|
|
||||||
|
void handleProcessDone();
|
||||||
|
void setupProcess(const Command &command, const Utils::Environment &env,
|
||||||
|
const QString &projectName, bool captureStdo);
|
||||||
|
bool sanityCheck(const Command &command) const;
|
||||||
|
|
||||||
|
void processStandardOutput();
|
||||||
|
void processStandardError();
|
||||||
|
|
||||||
|
std::unique_ptr<Utils::Process> m_process;
|
||||||
|
QElapsedTimer m_elapsed;
|
||||||
|
QByteArray m_stdo;
|
||||||
|
QByteArray m_stderr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -12,36 +12,20 @@
|
|||||||
#include "toolssettingsaccessor.h"
|
#include "toolssettingsaccessor.h"
|
||||||
#include "toolssettingspage.h"
|
#include "toolssettingspage.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
|
||||||
|
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
#include <projectexplorer/projectmanager.h>
|
#include <projectexplorer/projectmanager.h>
|
||||||
#include <projectexplorer/runcontrol.h>
|
#include <projectexplorer/runcontrol.h>
|
||||||
|
|
||||||
#include <utils/fsengine/fileiconprovider.h>
|
#include <utils/fsengine/fileiconprovider.h>
|
||||||
|
|
||||||
using namespace Core;
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace MesonProjectManager::Internal {
|
namespace MesonProjectManager::Internal {
|
||||||
|
|
||||||
class MesonProjectPluginPrivate : public QObject
|
class MesonProjectPluginPrivate
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
|
||||||
public:
|
public:
|
||||||
MesonProjectPluginPrivate()
|
|
||||||
{
|
|
||||||
MesonTools::setTools(m_toolsSettings.loadMesonTools(ICore::dialogParent()));
|
|
||||||
connect(ICore::instance(),
|
|
||||||
&ICore::saveSettingsRequested,
|
|
||||||
this,
|
|
||||||
&MesonProjectPluginPrivate::saveAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
~MesonProjectPluginPrivate() {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ToolsSettingsPage m_toolslSettingsPage;
|
ToolsSettingsPage m_toolslSettingsPage;
|
||||||
ToolsSettingsAccessor m_toolsSettings;
|
ToolsSettingsAccessor m_toolsSettings;
|
||||||
MesonBuildStepFactory m_buildStepFactory;
|
MesonBuildStepFactory m_buildStepFactory;
|
||||||
@@ -50,11 +34,6 @@ private:
|
|||||||
MesonActionsManager m_actions;
|
MesonActionsManager m_actions;
|
||||||
MachineFileManager m_machineFilesManager;
|
MachineFileManager m_machineFilesManager;
|
||||||
SimpleTargetRunnerFactory m_mesonRunWorkerFactory{{m_runConfigurationFactory.runConfigurationId()}};
|
SimpleTargetRunnerFactory m_mesonRunWorkerFactory{{m_runConfigurationFactory.runConfigurationId()}};
|
||||||
|
|
||||||
void saveAll()
|
|
||||||
{
|
|
||||||
m_toolsSettings.saveMesonTools(MesonTools::tools(), ICore::dialogParent());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
MesonProjectPlugin::~MesonProjectPlugin()
|
MesonProjectPlugin::~MesonProjectPlugin()
|
||||||
@@ -72,5 +51,3 @@ void MesonProjectPlugin::initialize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // MesonProjectManager::Internal
|
} // MesonProjectManager::Internal
|
||||||
|
|
||||||
#include "mesonprojectplugin.moc"
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "mesonpluginconstants.h"
|
#include "mesonpluginconstants.h"
|
||||||
#include "mesonprojectmanagertr.h"
|
#include "mesonprojectmanagertr.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace Core;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace MesonProjectManager {
|
namespace MesonProjectManager {
|
||||||
@@ -30,11 +32,16 @@ ToolsSettingsAccessor::ToolsSettingsAccessor()
|
|||||||
{
|
{
|
||||||
setDocType("QtCreatorMesonTools");
|
setDocType("QtCreatorMesonTools");
|
||||||
setApplicationDisplayName(QGuiApplication::applicationDisplayName());
|
setApplicationDisplayName(QGuiApplication::applicationDisplayName());
|
||||||
setBaseFilePath(Core::ICore::userResourcePath(Constants::ToolsSettings::FILENAME));
|
setBaseFilePath(ICore::userResourcePath(Constants::ToolsSettings::FILENAME));
|
||||||
|
|
||||||
|
MesonTools::setTools(loadMesonTools());
|
||||||
|
|
||||||
|
QObject::connect(ICore::instance(), &ICore::saveSettingsRequested, [this] {
|
||||||
|
saveMesonTools(MesonTools::tools());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t> &tools,
|
void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t> &tools)
|
||||||
QWidget *parent)
|
|
||||||
{
|
{
|
||||||
using namespace Constants;
|
using namespace Constants;
|
||||||
Store data;
|
Store data;
|
||||||
@@ -51,13 +58,13 @@ void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t>
|
|||||||
entry_count++;
|
entry_count++;
|
||||||
}
|
}
|
||||||
data.insert(ToolsSettings::ENTRY_COUNT, entry_count);
|
data.insert(ToolsSettings::ENTRY_COUNT, entry_count);
|
||||||
saveSettings(data, parent);
|
saveSettings(data, ICore::dialogParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<MesonTools::Tool_t> ToolsSettingsAccessor::loadMesonTools(QWidget *parent)
|
std::vector<MesonTools::Tool_t> ToolsSettingsAccessor::loadMesonTools()
|
||||||
{
|
{
|
||||||
using namespace Constants;
|
using namespace Constants;
|
||||||
auto data = restoreSettings(parent);
|
auto data = restoreSettings(ICore::dialogParent());
|
||||||
auto entry_count = data.value(ToolsSettings::ENTRY_COUNT, 0).toInt();
|
auto entry_count = data.value(ToolsSettings::ENTRY_COUNT, 0).toInt();
|
||||||
std::vector<MesonTools::Tool_t> result;
|
std::vector<MesonTools::Tool_t> result;
|
||||||
for (auto toolIndex = 0; toolIndex < entry_count; toolIndex++) {
|
for (auto toolIndex = 0; toolIndex < entry_count; toolIndex++) {
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ class ToolsSettingsAccessor final : public Utils::UpgradingSettingsAccessor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ToolsSettingsAccessor();
|
ToolsSettingsAccessor();
|
||||||
void saveMesonTools(const std::vector<MesonTools::Tool_t> &tools, QWidget *parent);
|
|
||||||
std::vector<MesonTools::Tool_t> loadMesonTools(QWidget *parent);
|
void saveMesonTools(const std::vector<MesonTools::Tool_t> &tools);
|
||||||
|
std::vector<MesonTools::Tool_t> loadMesonTools();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ bool SshParameters::setupSshEnvironment(Process *process)
|
|||||||
const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0"));
|
const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0"));
|
||||||
if (SshSettings::askpassFilePath().exists()) {
|
if (SshSettings::askpassFilePath().exists()) {
|
||||||
env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput());
|
env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput());
|
||||||
|
env.set("SSH_ASKPASS_REQUIRE", "force");
|
||||||
|
|
||||||
// OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform.
|
// OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform.
|
||||||
if (!env.hasKey("DISPLAY"))
|
if (!env.hasKey("DISPLAY"))
|
||||||
|
|||||||
@@ -86,20 +86,36 @@ void PySideInstaller::installPyside(const FilePath &python,
|
|||||||
{
|
{
|
||||||
QMap<QVersionNumber, Utils::FilePath> availablePySides;
|
QMap<QVersionNumber, Utils::FilePath> availablePySides;
|
||||||
|
|
||||||
const QString hostQtTail = HostOsInfo::isMacHost() ? QString("Tools/sdktool")
|
const Utils::QtcSettings *settings = Core::ICore::settings(QSettings::SystemScope);
|
||||||
|
|
||||||
|
const FilePaths requirementsList
|
||||||
|
= Utils::transform(settings->value("Python/PySideWheelsRequirements").toList(),
|
||||||
|
&FilePath::fromSettings);
|
||||||
|
for (const FilePath &requirements : requirementsList) {
|
||||||
|
if (requirements.exists()) {
|
||||||
|
auto version = QVersionNumber::fromString(requirements.parentDir().fileName());
|
||||||
|
availablePySides[version] = requirements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requirementsList.isEmpty()) { // fallback remove in Qt Creator 13
|
||||||
|
const QString hostQtTail = HostOsInfo::isMacHost()
|
||||||
|
? QString("Tools/sdktool")
|
||||||
: QString("Tools/sdktool/share/qtcreator");
|
: QString("Tools/sdktool/share/qtcreator");
|
||||||
|
|
||||||
const std::optional<FilePath> qtInstallDir
|
const std::optional<FilePath> qtInstallDir
|
||||||
= QtSupport::LinkWithQtSupport::linkedQt().tailRemoved(hostQtTail);
|
= QtSupport::LinkWithQtSupport::linkedQt().tailRemoved(hostQtTail);
|
||||||
if (qtInstallDir) {
|
if (qtInstallDir) {
|
||||||
const FilePath qtForPythonDir = qtInstallDir->pathAppended("QtForPython");
|
const FilePath qtForPythonDir = qtInstallDir->pathAppended("QtForPython");
|
||||||
for (const FilePath &versionDir : qtForPythonDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
for (const FilePath &versionDir :
|
||||||
|
qtForPythonDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||||
FilePath requirements = versionDir.pathAppended("requirements.txt");
|
FilePath requirements = versionDir.pathAppended("requirements.txt");
|
||||||
if (requirements.exists())
|
if (!requirementsList.contains(requirements) && requirements.exists())
|
||||||
availablePySides[QVersionNumber::fromString(versionDir.fileName())] = requirements;
|
availablePySides[QVersionNumber::fromString(versionDir.fileName())]
|
||||||
|
= requirements;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto install = new PipInstallTask(python);
|
auto install = new PipInstallTask(python);
|
||||||
connect(install, &PipInstallTask::finished, install, &QObject::deleteLater);
|
connect(install, &PipInstallTask::finished, install, &QObject::deleteLater);
|
||||||
|
|||||||
@@ -21,13 +21,14 @@
|
|||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/async.h>
|
||||||
#include <utils/detailswidget.h>
|
#include <utils/detailswidget.h>
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/listmodel.h>
|
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
|
#include <utils/listmodel.h>
|
||||||
#include <utils/pathchooser.h>
|
#include <utils/pathchooser.h>
|
||||||
#include <utils/process.h>
|
#include <utils/process.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/treemodel.h>
|
#include <utils/treemodel.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
@@ -366,14 +367,6 @@ private:
|
|||||||
InterpreterOptionsWidget *m_widget = nullptr;
|
InterpreterOptionsWidget *m_widget = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable)
|
|
||||||
{
|
|
||||||
return Utils::anyOf(pythons, [pythonExecutable](const Interpreter &interpreter) {
|
|
||||||
return interpreter.command.toFileInfo().canonicalFilePath()
|
|
||||||
== pythonExecutable.toFileInfo().canonicalFilePath();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static InterpreterOptionsPage &interpreterOptionsPage()
|
static InterpreterOptionsPage &interpreterOptionsPage()
|
||||||
{
|
{
|
||||||
static InterpreterOptionsPage page;
|
static InterpreterOptionsPage page;
|
||||||
@@ -626,8 +619,9 @@ static void disableOutdatedPyls()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addPythonsFromRegistry(QList<Interpreter> &pythons)
|
static QList<Interpreter> pythonsFromRegistry()
|
||||||
{
|
{
|
||||||
|
QList<Interpreter> pythons;
|
||||||
QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore",
|
QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore",
|
||||||
QSettings::NativeFormat);
|
QSettings::NativeFormat);
|
||||||
for (const QString &versionGroup : pythonRegistry.childGroups()) {
|
for (const QString &versionGroup : pythonRegistry.childGroups()) {
|
||||||
@@ -636,7 +630,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
|
|||||||
QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath");
|
QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath");
|
||||||
if (regVal.isValid()) {
|
if (regVal.isValid()) {
|
||||||
const FilePath &executable = FilePath::fromUserInput(regVal.toString());
|
const FilePath &executable = FilePath::fromUserInput(regVal.toString());
|
||||||
if (executable.exists() && !alreadyRegistered(pythons, executable)) {
|
if (executable.exists()) {
|
||||||
pythons << Interpreter{QUuid::createUuid().toString(),
|
pythons << Interpreter{QUuid::createUuid().toString(),
|
||||||
name,
|
name,
|
||||||
FilePath::fromUserInput(regVal.toString())};
|
FilePath::fromUserInput(regVal.toString())};
|
||||||
@@ -645,7 +639,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
|
|||||||
regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath");
|
regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath");
|
||||||
if (regVal.isValid()) {
|
if (regVal.isValid()) {
|
||||||
const FilePath &executable = FilePath::fromUserInput(regVal.toString());
|
const FilePath &executable = FilePath::fromUserInput(regVal.toString());
|
||||||
if (executable.exists() && !alreadyRegistered(pythons, executable)) {
|
if (executable.exists()) {
|
||||||
pythons << Interpreter{QUuid::createUuid().toString(),
|
pythons << Interpreter{QUuid::createUuid().toString(),
|
||||||
//: <python display name> (Windowed)
|
//: <python display name> (Windowed)
|
||||||
Tr::tr("%1 (Windowed)").arg(name),
|
Tr::tr("%1 (Windowed)").arg(name),
|
||||||
@@ -656,28 +650,30 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons)
|
|||||||
if (regVal.isValid()) {
|
if (regVal.isValid()) {
|
||||||
const FilePath &path = FilePath::fromUserInput(regVal.toString());
|
const FilePath &path = FilePath::fromUserInput(regVal.toString());
|
||||||
const FilePath python = path.pathAppended("python").withExecutableSuffix();
|
const FilePath python = path.pathAppended("python").withExecutableSuffix();
|
||||||
if (python.exists() && !alreadyRegistered(pythons, python))
|
if (python.exists())
|
||||||
pythons << createInterpreter(python, "Python " + versionGroup);
|
pythons << createInterpreter(python, "Python " + versionGroup);
|
||||||
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
|
const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix();
|
||||||
if (pythonw.exists() && !alreadyRegistered(pythons, pythonw))
|
if (pythonw.exists())
|
||||||
pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)");
|
pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)");
|
||||||
}
|
}
|
||||||
pythonRegistry.endGroup();
|
pythonRegistry.endGroup();
|
||||||
}
|
}
|
||||||
|
return pythons;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addPythonsFromPath(QList<Interpreter> &pythons)
|
static QList<Interpreter> pythonsFromPath()
|
||||||
{
|
{
|
||||||
|
QList<Interpreter> pythons;
|
||||||
if (HostOsInfo::isWindowsHost()) {
|
if (HostOsInfo::isWindowsHost()) {
|
||||||
for (const FilePath &executable : FilePath("python").searchAllInPath()) {
|
for (const FilePath &executable : FilePath("python").searchAllInPath()) {
|
||||||
// Windows creates empty redirector files that may interfere
|
// Windows creates empty redirector files that may interfere
|
||||||
if (executable.toFileInfo().size() == 0)
|
if (executable.toFileInfo().size() == 0)
|
||||||
continue;
|
continue;
|
||||||
if (executable.exists() && !alreadyRegistered(pythons, executable))
|
if (executable.exists())
|
||||||
pythons << createInterpreter(executable, "Python from Path");
|
pythons << createInterpreter(executable, "Python from Path");
|
||||||
}
|
}
|
||||||
for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) {
|
for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) {
|
||||||
if (executable.exists() && !alreadyRegistered(pythons, executable))
|
if (executable.exists())
|
||||||
pythons << createInterpreter(executable, "Python from Path", "(Windowed)");
|
pythons << createInterpreter(executable, "Python from Path", "(Windowed)");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -690,11 +686,12 @@ static void addPythonsFromPath(QList<Interpreter> &pythons)
|
|||||||
const QDir dir(path.toString());
|
const QDir dir(path.toString());
|
||||||
for (const QFileInfo &fi : dir.entryInfoList(filters)) {
|
for (const QFileInfo &fi : dir.entryInfoList(filters)) {
|
||||||
const FilePath executable = Utils::FilePath::fromFileInfo(fi);
|
const FilePath executable = Utils::FilePath::fromFileInfo(fi);
|
||||||
if (executable.exists() && !alreadyRegistered(pythons, executable))
|
if (executable.exists())
|
||||||
pythons << createInterpreter(executable, "Python from Path");
|
pythons << createInterpreter(executable, "Python from Path");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return pythons;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString idForPythonFromPath(const QList<Interpreter> &pythons)
|
static QString idForPythonFromPath(const QList<Interpreter> &pythons)
|
||||||
@@ -713,6 +710,51 @@ static QString idForPythonFromPath(const QList<Interpreter> &pythons)
|
|||||||
|
|
||||||
static PythonSettings *settingsInstance = nullptr;
|
static PythonSettings *settingsInstance = nullptr;
|
||||||
|
|
||||||
|
static bool alreadyRegistered(const Interpreter &candidate)
|
||||||
|
{
|
||||||
|
return Utils::anyOf(settingsInstance->interpreters(),
|
||||||
|
[candidate = candidate.command](const Interpreter &interpreter) {
|
||||||
|
return interpreter.command.isSameDevice(candidate)
|
||||||
|
&& interpreter.command.resolveSymlinks()
|
||||||
|
== candidate.resolveSymlinks();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scanPath()
|
||||||
|
{
|
||||||
|
auto watcher = new QFutureWatcher<QList<Interpreter>>();
|
||||||
|
QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() {
|
||||||
|
for (const Interpreter &interpreter : watcher->result()) {
|
||||||
|
if (!alreadyRegistered(interpreter))
|
||||||
|
settingsInstance->addInterpreter(interpreter);
|
||||||
|
}
|
||||||
|
watcher->deleteLater();
|
||||||
|
});
|
||||||
|
watcher->setFuture(Utils::asyncRun(pythonsFromPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scanRegistry()
|
||||||
|
{
|
||||||
|
auto watcher = new QFutureWatcher<QList<Interpreter>>();
|
||||||
|
QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() {
|
||||||
|
for (const Interpreter &interpreter : watcher->result()) {
|
||||||
|
if (!alreadyRegistered(interpreter))
|
||||||
|
settingsInstance->addInterpreter(interpreter);
|
||||||
|
}
|
||||||
|
watcher->deleteLater();
|
||||||
|
scanPath();
|
||||||
|
});
|
||||||
|
watcher->setFuture(Utils::asyncRun(pythonsFromRegistry));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scanSystemForInterpreters()
|
||||||
|
{
|
||||||
|
if (Utils::HostOsInfo::isWindowsHost())
|
||||||
|
scanRegistry();
|
||||||
|
else
|
||||||
|
scanPath();
|
||||||
|
}
|
||||||
|
|
||||||
PythonSettings::PythonSettings()
|
PythonSettings::PythonSettings()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!settingsInstance, return);
|
QTC_ASSERT(!settingsInstance, return);
|
||||||
@@ -723,9 +765,7 @@ PythonSettings::PythonSettings()
|
|||||||
|
|
||||||
initFromSettings(Core::ICore::settings());
|
initFromSettings(Core::ICore::settings());
|
||||||
|
|
||||||
if (HostOsInfo::isWindowsHost())
|
scanSystemForInterpreters();
|
||||||
addPythonsFromRegistry(m_interpreters);
|
|
||||||
addPythonsFromPath(m_interpreters);
|
|
||||||
|
|
||||||
if (m_defaultInterpreterId.isEmpty())
|
if (m_defaultInterpreterId.isEmpty())
|
||||||
m_defaultInterpreterId = idForPythonFromPath(m_interpreters);
|
m_defaultInterpreterId = idForPythonFromPath(m_interpreters);
|
||||||
|
|||||||
@@ -751,8 +751,10 @@ FilePath source(IDocument *document)
|
|||||||
void setProcessEnvironment(Environment *e)
|
void setProcessEnvironment(Environment *e)
|
||||||
{
|
{
|
||||||
const QString prompt = Internal::commonSettings().sshPasswordPrompt().path();
|
const QString prompt = Internal::commonSettings().sshPasswordPrompt().path();
|
||||||
if (!prompt.isEmpty())
|
if (!prompt.isEmpty()) {
|
||||||
e->set("SSH_ASKPASS", prompt);
|
e->set("SSH_ASKPASS", prompt);
|
||||||
|
e->set("SSH_ASKPASS_REQUIRE", "force");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace VcsBase
|
} // namespace VcsBase
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ project('mesonsampleproject', 'cpp',default_options : ['cpp_std=c++11'])
|
|||||||
qt5 = import('qt5')
|
qt5 = import('qt5')
|
||||||
qt5dep = dependency('qt5', modules : ['Core', 'Widgets'])
|
qt5dep = dependency('qt5', modules : ['Core', 'Widgets'])
|
||||||
|
|
||||||
translations = qt5.compile_translations(ts_files : 'mesonsampleproject_fr_FR.ts', build_by_default : true)
|
#translations = qt5.compile_translations(ts_files : 'mesonsampleproject_fr_FR.ts', build_by_default : true)
|
||||||
|
|
||||||
generated_files = qt5.preprocess(
|
generated_files = qt5.preprocess(
|
||||||
moc_headers : 'mesonsampleproject.h',
|
moc_headers : 'mesonsampleproject.h',
|
||||||
|
|||||||
@@ -126,7 +126,7 @@
|
|||||||
:Qt Creator.DragDoc_QToolButton {toolTip='Drag to drag documents between splits' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
:Qt Creator.DragDoc_QToolButton {toolTip='Drag to drag documents between splits' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
||||||
:Qt Creator.Events_QDockWidget {name='QmlProfiler.Statistics.DockDockWidget' type='QDockWidget' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
:Qt Creator.Events_QDockWidget {name='QmlProfiler.Statistics.DockDockWidget' type='QDockWidget' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
||||||
:Qt Creator.Events_QTabBar {aboveWidget=':Qt Creator.Events_QDockWidget' type='QTabBar' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
:Qt Creator.Events_QTabBar {aboveWidget=':Qt Creator.Events_QDockWidget' type='QTabBar' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
||||||
:Qt Creator.Issues_QListView {type='Utils::TreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'}
|
:Qt Creator.Issues_QListView {type='QTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'}
|
||||||
:Qt Creator.Project.Menu.File_QMenu {name='Project.Menu.File' type='QMenu'}
|
:Qt Creator.Project.Menu.File_QMenu {name='Project.Menu.File' type='QMenu'}
|
||||||
:Qt Creator.Project.Menu.Folder_QMenu {name='Project.Menu.Folder' type='QMenu' visible='1'}
|
:Qt Creator.Project.Menu.Folder_QMenu {name='Project.Menu.Folder' type='QMenu' visible='1'}
|
||||||
:Qt Creator.QML debugging and profiling:_QComboBox {leftWidget=':Qt Creator.QML debugging and profiling:_QLabel' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
:Qt Creator.QML debugging and profiling:_QComboBox {leftWidget=':Qt Creator.QML debugging and profiling:_QLabel' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ def __createProjectOrFileSelectType__(category, template, fromWelcome = False, i
|
|||||||
return __getSupportedPlatforms__(str(text), template)[0]
|
return __getSupportedPlatforms__(str(text), template)[0]
|
||||||
|
|
||||||
def __createProjectSetNameAndPath__(path, projectName = None, checks = True):
|
def __createProjectSetNameAndPath__(path, projectName = None, checks = True):
|
||||||
directoryEdit = waitForObject("{type='Utils::FancyLineEdit' unnamed='1' visible='1' "
|
pathChooser = waitForObject("{type='Utils::PathChooser' name='baseFolder' visible='1'}")
|
||||||
"toolTip~='Full path: .*'}")
|
directoryEdit = getChildByClass(pathChooser, "Utils::FancyLineEdit")
|
||||||
replaceEditorContent(directoryEdit, path)
|
replaceEditorContent(directoryEdit, path)
|
||||||
projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' "
|
projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' "
|
||||||
"type='Utils::FancyLineEdit'}")
|
"type='Utils::FancyLineEdit'}")
|
||||||
|
|||||||
Reference in New Issue
Block a user