diff --git a/README.md b/README.md index 5666df9cb68..14493d1afd1 100644 --- a/README.md +++ b/README.md @@ -157,15 +157,21 @@ like Qt and LLVM, additionally run cmake --install . --prefix /path/to/qtcreator_install --component Dependencies -### Performance Analyzer +### Perf Profiler Support -If you have not installed Qt with the Maintenance Tool, you must -either set the path to the `perfparser` executable as a value of -the `PERFPROFILER_PARSER_FILEPATH` environment variable or copy -the executable to from the Qt Creator installation directory to -the build directory. That is, copy it from -`/path/to/qtcreator_install/Tools/QtCreator/libexec/qtcreator/` to -/path/to/qtcreator_buid/libexec/qtcreator/`. +Support for the [perf](https://perf.wiki.kernel.org/index.php/Main_Page) profiler +requires the `perfparser` tool that is part of the Qt Creator source package, and also +part of the Qt Creator Git repository in form of a submodule in `src/tools/perfparser`. + +Compilation of `perfparser` requires ELF and DWARF development packages. +You can either download and extract a prebuilt package from +https://download.qt.io/development_releases/prebuilt/elfutils/ and add the +directory to the `CMAKE_PREFIX_PATH` when configuring Qt Creator, +or install the `libdw-dev` package on Debian-style Linux systems. + +You can also point Qt Creator to a separate installation of `perfparser` by +setting the `PERFPROFILER_PARSER_FILEPATH` environment variable to the full +path to the executable. ## Getting LLVM/Clang for the Clang Code Model diff --git a/cmake/Findelfutils.cmake b/cmake/Findelfutils.cmake index 73e42130dfc..f5b3f0330f7 100644 --- a/cmake/Findelfutils.cmake +++ b/cmake/Findelfutils.cmake @@ -62,6 +62,8 @@ if(elfutils_FOUND) endif() endif() endforeach() +else() + message(STATUS " (set ELFUTILS_INSTALL_DIR, or install libdw-dev on a Debian-like system)") endif() mark_as_advanced(ELFUTILS_INCLUDE_DIR ELFUTILS_LIB_elf ELFUTILS_LIB_dw) diff --git a/scripts/build_plugin.py b/scripts/build_plugin.py index 4e142bcd1e0..df06908c9d3 100755 --- a/scripts/build_plugin.py +++ b/scripts/build_plugin.py @@ -39,6 +39,13 @@ def get_arguments(): action='store_true', default=False) parser.add_argument('--build-type', help='Build type to pass to CMake (defaults to RelWithDebInfo)', default='RelWithDebInfo') + # zipping + parser.add_argument('--zip-threads', help='Sets number of threads to use for 7z. Use "+" for turning threads on ' + 'without a specific number of threads. This is directly passed to the "-mmt" option of 7z.', + default='2') + # signing + parser.add_argument('--keychain-unlock-script', + help='Path to script for unlocking the keychain used for signing (macOS)') args = parser.parse_args() args.with_debug_info = args.build_type == 'RelWithDebInfo' return args @@ -144,6 +151,21 @@ def package(args, paths): common.check_print_call(['7z', 'a', '-mmt2', os.path.join(paths.result, args.name + '-debug.7z'), '*'], paths.debug_install) + if common.is_mac_platform() and common.codesign_call(): + if args.keychain_unlock_script: + common.check_print_call([args.keychain_unlock_script], paths.install) + if os.environ.get('SIGNING_IDENTITY'): + signed_install_path = paths.install + '-signed' + common.copytree(paths.install, signed_install_path, symlinks=True) + apps = [d for d in os.listdir(signed_install_path) if d.endswith('.app')] + if apps: + app = apps[0] + common.conditional_sign_recursive(os.path.join(signed_install_path, app), + lambda ff: ff.endswith('.dylib')) + common.check_print_call(['7z', 'a', '-mmt' + args.zip_threads, + os.path.join(paths.result, args.name + '-signed.7z'), + app], + signed_install_path) def get_paths(args): Paths = collections.namedtuple('Paths', diff --git a/scripts/deployqt.py b/scripts/deployqt.py index 50eb3eb1253..05113e43e77 100755 --- a/scripts/deployqt.py +++ b/scripts/deployqt.py @@ -110,9 +110,12 @@ def is_ignored_windows_file(use_debug, basepath, filename): return False def ignored_qt_lib_files(path, filenames): + # Qt ships some unneeded object files in the qml plugins + # On Windows we also do not want to ship the wrong debug/release .dlls or .lib files etc if not common.is_windows_platform(): - return [] - return [fn for fn in filenames if is_ignored_windows_file(debug_build, path, fn)] + return [fn for fn in filenames if fn.endswith('.cpp.o')] + return [fn for fn in filenames + if fn.endswith('.cpp.obj') or is_ignored_windows_file(debug_build, path, fn)] def copy_qt_libs(target_qt_prefix_path, qt_bin_dir, qt_libs_dir, qt_plugin_dir, qt_qml_dir, plugins): print("copying Qt libraries...") diff --git a/scripts/deployqtHelper_mac.sh b/scripts/deployqtHelper_mac.sh index 4109e6f31db..efcde54bdfe 100755 --- a/scripts/deployqtHelper_mac.sh +++ b/scripts/deployqtHelper_mac.sh @@ -143,6 +143,9 @@ if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then fi +# clean up unneeded object files that are part of Qt for some static libraries +find "$app_path" -ipath "*/objects-*" -delete + # clean up after macdeployqt # it deploys some plugins (and libs for these) that interfere with what we want echo "Cleaning up after macdeployqt..." diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.h b/src/libs/languageserverprotocol/jsonrpcmessages.h index ebf51b4c14c..c33e46422ca 100644 --- a/src/libs/languageserverprotocol/jsonrpcmessages.h +++ b/src/libs/languageserverprotocol/jsonrpcmessages.h @@ -290,7 +290,10 @@ public: return val.isUndefined() ? std::nullopt : std::make_optional(fromJsonValue(val)); } void setError(const Error &error) - { m_jsonObject.insert(errorKey, QJsonValue(error)); } + { + QTC_CHECK(error.isValid()); + m_jsonObject.insert(errorKey, QJsonValue(error)); + } void clearError() { m_jsonObject.remove(errorKey); } bool isValid(QString *errorMessage) const override diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index 8db56aa4d42..4afe16e3795 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -467,10 +467,9 @@ Utils::Text::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffe bool secondTry) const { QTC_ASSERT(replacementsToKeep != ReplacementsToKeep::All, return Utils::Text::Replacements()); + QTC_ASSERT(!m_fileName.isEmpty(), return {}); - clang::format::FormatStyle style = styleForFile(); QByteArray originalBuffer = buffer; - int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, startBlock.blockNumber() + 1); QTC_ASSERT(utf8Offset >= 0, return Utils::Text::Replacements();); int utf8Length = selectedLines(m_doc, startBlock, endBlock).toUtf8().size(); @@ -479,6 +478,7 @@ Utils::Text::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffe if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision()); + clang::format::FormatStyle style = styleForFile(); adjustFormatStyleForLineBreak(style, replacementsToKeep); if (replacementsToKeep == ReplacementsToKeep::OnlyIndent) { CharacterContext currentCharContext = CharacterContext::Unknown; @@ -527,6 +527,7 @@ Utils::Text::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffe Utils::Text::Replacements ClangFormatBaseIndenter::format( const TextEditor::RangesInLines &rangesInLines) { + QTC_ASSERT(!m_fileName.isEmpty(), return {}); if (rangesInLines.empty()) return Utils::Text::Replacements(); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index a46bb03542c..73c84655841 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -441,6 +441,28 @@ static QString extractVisualStudioPlatformFromConfig(const CMakeConfig &config) return platform; } +void updateCompilerPaths(CMakeConfig &config, const Environment &env) +{ + auto updateRelativePath = [&config, env](const QByteArray &key) { + FilePath pathValue = config.filePathValueOf(key); + + if (pathValue.isAbsolutePath() || pathValue.isEmpty()) + return; + + pathValue = env.searchInPath(pathValue.fileName()); + + auto it = std::find_if(config.begin(), config.end(), [&key](const CMakeConfigItem &item) { + return item.key == key; + }); + QTC_ASSERT(it != config.end(), return); + + it->value = pathValue.path().toUtf8(); + }; + + updateRelativePath("CMAKE_C_COMPILER"); + updateRelativePath("CMAKE_CXX_COMPILER"); +} + QList CMakeProjectImporter::examineDirectory(const FilePath &importPath, QString *warningMessage) const { @@ -518,6 +540,7 @@ QList CMakeProjectImporter::examineDirectory(const FilePath &importPath, } } else { config = cache; + updateCompilerPaths(config, env); config << CMakeConfigItem("CMAKE_COMMAND", CMakeConfigItem::PATH, configurePreset.cmakeExecutable.value().toUtf8()); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 91fe7a2d808..76a42a91266 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -1893,6 +1893,7 @@ void ClientPrivate::handleMethod(const QString &method, const MessageId &id, con Response response(id); ResponseError error; error.setCode(ResponseError::MethodNotFound); + error.setMessage(QString("The client cannot handle the method '%1'.").arg(method)); response.setError(error); sendResponse(response); } diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index 5bf4acbaa8e..6e48066322e 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -538,6 +538,11 @@ QWidget *QMakeStep::createConfigWidget() BuildManager::buildLists({bc->cleanSteps()}); }); + connect(widget, &QObject::destroyed, this, [this] { + abisLabel = nullptr; + abisListWidget = nullptr; + }); + VariableChooser::addSupportForChildWidgets(widget, macroExpander()); return widget; diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 53b0f1fe053..c192249862c 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -4481,16 +4481,18 @@ void TextEditorWidgetPrivate::paintIndentDepth(PaintEventData &data, const QTextLine textLine = blockData.layout->lineAt(0); const QRectF rect = textLine.naturalTextRect(); - qreal x = textLine.cursorToX(0) + data.offset.x() + qMax(0, q->cursorWidth() - 1) - + singleAdvance * m_visualIndentOffset; + qreal x = textLine.x() + data.offset.x() + qMax(0, q->cursorWidth() - 1) + + singleAdvance * m_visualIndentOffset; int paintColumn = 0; const QString text = data.block.text().mid(m_visualIndentOffset); while (paintColumn < depth) { if (x >= 0) { int paintPosition = data.tabSettings.positionAtColumn(text, paintColumn); - if (blockData.layout->lineForTextPosition(paintPosition).lineNumber() != 0) + if (q->lineWrapMode() == QPlainTextEdit::WidgetWidth + && blockData.layout->lineForTextPosition(paintPosition).lineNumber() != 0) { break; + } const QPointF top(x, blockData.boundingRect.top()); const QPointF bottom(x, blockData.boundingRect.top() + rect.height()); const QLineF line(top, bottom); diff --git a/tests/system/shared/build_utils.py b/tests/system/shared/build_utils.py index 5d422f8ef08..05afc9c493f 100644 --- a/tests/system/shared/build_utils.py +++ b/tests/system/shared/build_utils.py @@ -209,8 +209,7 @@ def runVerify(): availableConfigs = iterateBuildConfigs() if not availableConfigs: test.fatal("Haven't found build configurations, quitting") - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() for kit, config in availableConfigs: selectBuildConfig(kit, config) test.log("Using build config '%s'" % config) diff --git a/tests/system/shared/editor_utils.py b/tests/system/shared/editor_utils.py index 60ed802745d..57a60fd6a97 100644 --- a/tests/system/shared/editor_utils.py +++ b/tests/system/shared/editor_utils.py @@ -400,8 +400,7 @@ def openDocument(treeElement): def earlyExit(details="No additional information"): test.fail("Something went wrong running this test", details) - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() def openDocumentPlaceCursor(doc, line, additionalFunction=None): cppEditorStr = ":Qt Creator_CppEditor::Internal::CPPEditorWidget" diff --git a/tests/system/suite_QMLS/tst_QMLS02/test.py b/tests/system/suite_QMLS/tst_QMLS02/test.py index cc931ae3239..c4bfdfd9efe 100644 --- a/tests/system/suite_QMLS/tst_QMLS02/test.py +++ b/tests/system/suite_QMLS/tst_QMLS02/test.py @@ -42,7 +42,5 @@ def main(): # wait for issues test.verify(waitFor("issuesModel.rowCount() == 0", 3000), "Verifying if error was properly cleared after code fix") - #save and exit - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() diff --git a/tests/system/suite_QMLS/tst_QMLS04/test.py b/tests/system/suite_QMLS/tst_QMLS04/test.py index 63db543bcba..2c42c178cc0 100644 --- a/tests/system/suite_QMLS/tst_QMLS04/test.py +++ b/tests/system/suite_QMLS/tst_QMLS04/test.py @@ -46,16 +46,13 @@ def main(): waitForObjectItem(":Qt Creator_Utils::NavigationTreeView", addBranchWildcardToRoot(myCompTE), 1000) except: test.fail("Refactoring failed - file MyComponent.qml was not generated properly in project explorer") - #save and exit - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() return test.passes("Refactoring - file MyComponent.qml was generated properly in project explorer") # open MyComponent.qml file for verification if not openDocument(myCompTE): test.fatal("Could not open MyComponent.qml.") - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() return editorArea = waitForObject(":Qt Creator_QmlJSEditor::QmlJSTextEditorWidget") codeText = str(editorArea.plainText) diff --git a/tests/system/suite_QMLS/tst_QMLS08/test.py b/tests/system/suite_QMLS/tst_QMLS08/test.py index 21def06251d..7b7cca9979b 100644 --- a/tests/system/suite_QMLS/tst_QMLS08/test.py +++ b/tests/system/suite_QMLS/tst_QMLS08/test.py @@ -19,8 +19,7 @@ def verifyNextLineIndented(editorArea, expectedIndentation): def verifyIndentation(editorArea): #verify indentation if not placeCursorToLine(editorArea, "id: wdw"): - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() return False type(editorArea, "") expectedIndentations = [1,1,1,2,2,2,2,3,3,3,4,3,3,2,1] @@ -58,6 +57,4 @@ def main(): # verify invoked indentation if not verifyIndentation(editorArea): return - # save and exit - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() diff --git a/tests/system/suite_editors/tst_generic_highlighter/test.py b/tests/system/suite_editors/tst_generic_highlighter/test.py index f1f154376ec..490c6021681 100644 --- a/tests/system/suite_editors/tst_generic_highlighter/test.py +++ b/tests/system/suite_editors/tst_generic_highlighter/test.py @@ -196,9 +196,8 @@ def main(): if current.endswith(".lhs"): type(editor, ">") type(editor, "") + saveAndExit() - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") def init(): syntaxDirectory = __highlighterDefinitionsDirectory__() diff --git a/tests/system/suite_editors/tst_rename_macros/test.py b/tests/system/suite_editors/tst_rename_macros/test.py index cfa29e49863..6d329e44667 100644 --- a/tests/system/suite_editors/tst_rename_macros/test.py +++ b/tests/system/suite_editors/tst_rename_macros/test.py @@ -24,9 +24,7 @@ def main(): expectedHeaderName=headerName) if not testRenameMacroAfterSourceMoving(): return - # save and exit - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() def testRenameMacroAfterSourceModification(): def __deleteAnyClass__(): diff --git a/tests/system/suite_qtquick/tst_qml_outline/test.py b/tests/system/suite_qtquick/tst_qml_outline/test.py index ddf4d368af5..63de1001e1b 100644 --- a/tests/system/suite_qtquick/tst_qml_outline/test.py +++ b/tests/system/suite_qtquick/tst_qml_outline/test.py @@ -21,8 +21,7 @@ def main(): qmlFiles = [treebase + "focus\\.qml", treebase + "Core.ListMenu\\.qml"] checkOutlineFor(qmlFiles) testModify() - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() def checkOutlineFor(qmlFiles): for qmlFile in qmlFiles: diff --git a/tests/system/suite_tools/tst_designer_autocomplete/test.py b/tests/system/suite_tools/tst_designer_autocomplete/test.py index 229420a8b8c..903ca01156a 100644 --- a/tests/system/suite_tools/tst_designer_autocomplete/test.py +++ b/tests/system/suite_tools/tst_designer_autocomplete/test.py @@ -46,5 +46,4 @@ def main(): 'Comparing line "%s" to expected "%s"' % (lineUnderCursor(editor), "ui->%s" % buttonName)) type(editor, "") # Delete line selectFromLocator("mainwindow.ui") - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit() diff --git a/tests/system/suite_tools/tst_designer_goto_slot/test.py b/tests/system/suite_tools/tst_designer_goto_slot/test.py index cb3c9eb743f..c43846d7f8c 100644 --- a/tests/system/suite_tools/tst_designer_goto_slot/test.py +++ b/tests/system/suite_tools/tst_designer_goto_slot/test.py @@ -33,5 +33,4 @@ def main(): type(editor, "") test.verify(waitFor('str(lineUnderCursor(editor)).strip() == con[3]', 1000), 'Comparing line "%s" to expected "%s"' % (lineUnderCursor(editor), con[3])) - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") + saveAndExit()