diff options
547 files changed, 11226 insertions, 7588 deletions
diff --git a/cmake/QtCreatorAPI.cmake b/cmake/QtCreatorAPI.cmake index c4f8d1f0a6c..7a390b1ad97 100644 --- a/cmake/QtCreatorAPI.cmake +++ b/cmake/QtCreatorAPI.cmake @@ -244,6 +244,7 @@ function(add_qtc_library name) unset(NAMELINK_OPTION) if (library_type STREQUAL "SHARED") set(NAMELINK_OPTION NAMELINK_SKIP) + qtc_add_link_flags_no_undefined(${name}) endif() unset(COMPONENT_OPTION) diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index d7bef93491f..2278932a02b 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -4,6 +4,10 @@ if (CMAKE_VERSION VERSION_LESS 3.18) endif() endif() +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18) + include(CheckLinkerFlag) +endif() + include(FeatureSummary) # @@ -161,6 +165,22 @@ function(qtc_enable_sanitize _sanitize_flags) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}" PARENT_SCOPE) endfunction() +function(qtc_add_link_flags_no_undefined target) + # needs CheckLinkerFlags + if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18) + set(no_undefined_flag "-Wl,--no-undefined") + check_linker_flag(CXX ${no_undefined_flag} QTC_LINKER_SUPPORTS_NO_UNDEFINED) + if (NOT QTC_LINKER_SUPPORTS_NO_UNDEFINED) + set(no_undefined_flag "-Wl,-undefined,error") + check_linker_flag(CXX ${no_undefined_flag} QTC_LINKER_SUPPORTS_UNDEFINED_ERROR) + if (NOT QTC_LINKER_SUPPORTS_UNDEFINED_ERROR) + return() + endif() + endif() + target_link_options("${target}" PRIVATE "${no_undefined_flag}") + endif() +endfunction() + function(append_extra_translations target_name) if(NOT ARGN) return() diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake index ade8b73ac23..3ca492689c7 100644 --- a/cmake/QtCreatorIDEBranding.cmake +++ b/cmake/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "7.0.0") # The IDE version. -set(IDE_VERSION_COMPAT "7.0.0") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "7.0.0") # The IDE display version. +set(IDE_VERSION "7.82.0") # The IDE version. +set(IDE_VERSION_COMPAT "7.82.0") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "8.0.0-beta1") # The IDE display version. set(IDE_COPYRIGHT_YEAR "2022") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. diff --git a/doc/qtcreator/images/qtcreator-code-pasting-options.png b/doc/qtcreator/images/qtcreator-code-pasting-options.png Binary files differindex 38a1d4a3977..d9bb548098a 100644 --- a/doc/qtcreator/images/qtcreator-code-pasting-options.png +++ b/doc/qtcreator/images/qtcreator-code-pasting-options.png diff --git a/doc/qtcreator/images/qtcreator-external-tools.png b/doc/qtcreator/images/qtcreator-external-tools.png Binary files differindex 31b325b1809..949eff9ae25 100644 --- a/doc/qtcreator/images/qtcreator-external-tools.png +++ b/doc/qtcreator/images/qtcreator-external-tools.png diff --git a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc index c9d9fe89b4f..f95f7dda851 100644 --- a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc +++ b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc @@ -504,6 +504,6 @@ the performance counters system. \endlist - Output from the helper program that processes the data is displayed in the - \uicontrol {General Messages} output pane. + Output from the helper program that processes the data is displayed in + \l{Viewing Output}{General Messages}. */ diff --git a/doc/qtcreator/src/android/androiddev.qdoc b/doc/qtcreator/src/android/androiddev.qdoc index 7af57672040..9d815d0f8c9 100644 --- a/doc/qtcreator/src/android/androiddev.qdoc +++ b/doc/qtcreator/src/android/androiddev.qdoc @@ -295,8 +295,8 @@ the application for debugging. \note \QC cannot debug applications on Android devices if Android Studio is - running. If the following message is displayed in the \uicontrol Output - pane, close Android Studio and try again: + running. If the following message is displayed in \l {Application Output}, + close Android Studio and try again: \badcode Ignoring second debugger -accepting and dropping. diff --git a/doc/qtcreator/src/android/deploying-android.qdoc b/doc/qtcreator/src/android/deploying-android.qdoc index 97c12b936d9..bc52fb782de 100644 --- a/doc/qtcreator/src/android/deploying-android.qdoc +++ b/doc/qtcreator/src/android/deploying-android.qdoc @@ -138,7 +138,7 @@ \l{androiddeployqt}. You can view information about what the \c androiddeployqt tool is doing in - the \uicontrol {Compile Output} pane. To view additional information, select the + \l {Compile Output}. To view additional information, select the \uicontrol {Verbose output} check box. Select \uicontrol {Add debug server} to include the debug server binary diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc index 45e55f42987..d2c760b6e1d 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc @@ -237,6 +237,5 @@ \image qtcreator-cmake-clean-steps.png - The build errors and warnings are parsed and displayed in the - \l Issues output pane. + The build errors and warnings are parsed and displayed in \l Issues. */ diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc index 5cac40c6372..f9e83a01bb1 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc @@ -166,7 +166,7 @@ \endlist - Warnings and errors are displayed in the \l {Issues} output pane. + Warnings and errors are displayed in \l {Issues}. \section1 Adding External Libraries to CMake Projects diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdocinc b/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdocinc index f1d74a9d8e9..bd2551e620d 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdocinc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdocinc @@ -203,9 +203,9 @@ views, select the \uicontrol {Use Python dumper} check box. For more information, see \l{Debugging Helper Implementation}. - To add information about first-chance and second-chance exceptions - to the \uicontrol Issues output pane, select the check boxes - in the \uicontrol {Add Exceptions to the Issues View} group. + To display information about first-chance and second-chance exceptions + in \l Issues, select the check boxes + in the \uicontrol {Add Exceptions to Issues View} group. \section2 Setting CDB Paths on Windows diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc index 28265fb9319..1791b8b9488 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc @@ -1497,7 +1497,7 @@ You might have created a release build that does not contain debug information. A GNU Compiler Collection (GCC) debug build has the \c {-g} option on the compiler command line. Check that this option is present in - the \uicontrol {Compile Output} pane. If it is not, adjust your build + the \l {Compile Output}. If it is not, adjust your build settings in the \uicontrol Projects mode. \section1 Debugger Does Not Work diff --git a/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc b/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc index 51afd4ba68f..9375d05fb65 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugger-example.qdoc @@ -104,7 +104,7 @@ \image qtquick-example-setting-breakpoint3.png \li To execute JavaScript commands in the current context, open the - \uicontrol {QML Debugger Console} output pane. + \uicontrol {QML Debugger Console}. \image qml-script-console.png diff --git a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc index 02897e71a94..be900ea655c 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc @@ -253,8 +253,8 @@ When the application is interrupted by a breakpoint, you can use the \uicontrol {QML Debugger Console} to execute JavaScript expressions in the - current context. To open it, choose \uicontrol View > - \uicontrol {Output Panes} > \uicontrol {QML Debugger Console}. + current context. To open it, choose \uicontrol View > \uicontrol Output > + \uicontrol {QML Debugger Console}. \image qml-script-console.png "QML Debugger Console" diff --git a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc index 371aaeee16c..de6d476d531 100644 --- a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc +++ b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc @@ -89,8 +89,8 @@ \endif \endlist - The \uicontrol {Search Results} pane shows the location - and number of search hits in the current project. + \l{Search Results} shows the location and number of search hits in the + current project. \if defined(qtcreator) \image qtcreator-refactoring-find.png @@ -101,12 +101,11 @@ You can browse the search results in the following ways: \list - \li To go directly to an instance, double-click the instance in the - \uicontrol {Search Results} pane. + \li To go directly to an instance, double-click the instance in + \uicontrol {Search Results}. \li To move between instances, click the \inlineimage icons/next.png (\uicontrol {Next Item}) button and \inlineimage icons/prev.png - (\uicontrol {Previous Item}) button in the - \uicontrol {Search Results} pane. + (\uicontrol {Previous Item}) button in \uicontrol {Search Results}. \li To expand and collapse the list of all instances, click the \inlineimage icons/qtcreator-expand.png (\uicontrol {Expand All}) button. @@ -139,7 +138,7 @@ \uicontrol {QML/JS} > \uicontrol {Rename Symbol Under Cursor} or press \key {Ctrl+Shift+R}. - The \uicontrol {Search Results} pane shows the location + \uicontrol {Search Results} shows the location and number of instances of the symbol in the current project. \if defined(qtcreator) @@ -153,15 +152,15 @@ To omit an instance, deselect the check box next to the instance. \note This action replaces all selected instances of the symbol in - all files listed in the \uicontrol {Search Results} pane. You cannot + all files listed in \uicontrol {Search Results}. You cannot undo this action. \if defined(qtcreator) If the symbol is a class, select the \uicontrol {Rename files} check box to also change the filenames that match the class name. - \note Renaming local symbols does not open the \uicontrol {Search Results} - pane. The instances of the symbol are highlighted in code and you can edit + \note Renaming local symbols does not open \uicontrol {Search Results}. + The instances of the symbol are highlighted in code and you can edit the symbol. All instances of the local symbol are changed as you type. \endif diff --git a/doc/qtcreator/src/editors/creator-code-syntax.qdoc b/doc/qtcreator/src/editors/creator-code-syntax.qdoc index 8da7ba560af..339b0f6f1b0 100644 --- a/doc/qtcreator/src/editors/creator-code-syntax.qdoc +++ b/doc/qtcreator/src/editors/creator-code-syntax.qdoc @@ -108,7 +108,7 @@ find common problems. To run the checks, select \uicontrol Tools > \uicontrol {QML/JS} > \uicontrol {Run Checks} or press \key {Ctrl+Shift+C}. The results are shown in the \uicontrol QML and \uicontrol {QML Analysis} - filters of the \uicontrol Issues output pane. + filters in \l Issues. Many of the error messages are similar to the ones in Douglas Crockford's \l{http://www.jslint.com}{JSLint} tool. For more information about JSLint diff --git a/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc b/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc index 5fbc28c8323..3110d8800f7 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc @@ -56,9 +56,9 @@ \li Select the \uicontrol {Copy-paste URL to clipboard} check box to copy the URL of the post on the code pasting service to the clipboard when you paste a post. - \li Select the \uicontrol {Display Output pane after sending a post} - check box to display the URL in the \uicontrol {General Messages} - output pane when you paste a post. + \li Select the \uicontrol {Display General Messages after sending a post} + check box to display the URL in \l{Viewing Output}{General Messages} + when you paste a post. \endlist Select \uicontrol Fileshare to specify the path to a shared network drive. @@ -70,8 +70,7 @@ To paste a snippet of code onto the server, select \uicontrol Tools > \uicontrol {Code Pasting} > \uicontrol {Paste Snippet} or press \key {Alt+C,Alt+P}. By default, \QC copies the URL of the snippet to the - clipboard and displays the URL in the \uicontrol {General Messages} output - pane. + clipboard and displays the URL in \uicontrol {General Messages}. To paste any content that you copied to the clipboard, select \uicontrol Tools > \uicontrol {Code Pasting} > \uicontrol {Paste Snippet}. diff --git a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc index 001e85c1462..056d8d86402 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc @@ -116,9 +116,9 @@ patterns to extend the MIME types, separated by semicolons. \li In the \uicontrol {Startup behavior} field, select whether the language server is started when \QC starts or when a project or file - with a matching MIME type is opened. The - \uicontrol {General Messages} \l{Viewing Output}{output pane} - displays information about the connection to the language server. + with a matching MIME type is opened. \l{Viewing Output} + {General Messages} displays information about the connection to the + language server. \li In the \uicontrol Initialization field, you can add language server specific JSON attributes to pass to an \c initialize request. \li In the \uicontrol Executable field, enter the path to the language diff --git a/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc b/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc index 19edbf37843..fadaa0853f9 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc @@ -90,7 +90,7 @@ chart, select \inlineimage icons/statistics.png (\uicontrol {View Statistics}). - To search from the state chart, use the \uicontrol Search pane. The search + To search from the state chart, use \l {Search Results}. The search checks the whole SCXML tree for attributes that match the search criteria. To save the currently visible part of the state chart as an image, select diff --git a/doc/qtcreator/src/editors/creator-search.qdoc b/doc/qtcreator/src/editors/creator-search.qdoc index 7a1e7dd9de3..e68c1725ca0 100644 --- a/doc/qtcreator/src/editors/creator-search.qdoc +++ b/doc/qtcreator/src/editors/creator-search.qdoc @@ -194,8 +194,8 @@ \image qtcreator-searchresults.png - A list of files containing the searched text is displayed in the - \uicontrol {Search Results} pane. + A list of files containing the searched text is displayed in + \l {Search Results}. \list diff --git a/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc b/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc index 42983fe6325..a2a7f8b48f3 100644 --- a/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc +++ b/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc @@ -73,8 +73,8 @@ If the editor cannot find the highlight definition for a file that you open for editing, it prompts you to download additional highlight definition files. Select \uicontrol {Download Definitions} to download the files. - Information about the downloaded files is displayed in the - \uicontrol {General Messages} \l{Viewing Output}{output pane}. + Information about the downloaded files is displayed in \l{Viewing Output} + {General Messages}. To suppress the message for a particular file pattern, select \uicontrol Tools > \uicontrol Options > \uicontrol {Text Editor} diff --git a/doc/qtcreator/src/howto/creator-external-tools.qdoc b/doc/qtcreator/src/howto/creator-external-tools.qdoc index 35aae547c7e..a492b11d859 100644 --- a/doc/qtcreator/src/howto/creator-external-tools.qdoc +++ b/doc/qtcreator/src/howto/creator-external-tools.qdoc @@ -135,8 +135,8 @@ working directory. \li In the \uicontrol Output field, select how to handle output from the - tool. You can ignore the output, view it in the \uicontrol {General - Messages} output pane, or replace the selected text with the + tool. You can ignore the output, view it in \l{Viewing Output} + {General Messages}, or replace the selected text with the output in the code editor. \li In the \uicontrol {Error output} field, select how to handle error diff --git a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc index a323726b775..2ff220a8c23 100644 --- a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc +++ b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc @@ -227,22 +227,22 @@ \li Switch to \uicontrol Help mode \li Ctrl+6 \row - \li Toggle \uicontrol{Issues} pane + \li Toggle \uicontrol{Issues} \li Alt+1 (Cmd+1 on \macos) \row - \li Toggle \uicontrol{Search Results} pane + \li Toggle \uicontrol{Search Results} \li Alt+2 (Cmd+2 on \macos) \row - \li Toggle \uicontrol{Application Output} pane + \li Toggle \uicontrol{Application Output} \li Alt+3 (Cmd+3 on \macos) \row - \li Toggle \uicontrol{Compile Output} pane + \li Toggle \uicontrol{Compile Output} \li Alt+4 (Cmd+4 on \macos) \row - \li Toggle other output panes + \li Toggle other output views \li Alt+number (Cmd+number on \macos) - Where the number is the number of the output pane. + Where the number is the number of the view. \if defined(qtcreator) \row \li Activate \uicontrol Bookmarks view @@ -255,13 +255,13 @@ \li Activate \uicontrol{Open Documents} view \li Alt+O \row - \li Maximize output panes + \li Maximize output views \li Alt+9 \row - \li Move to next item in output panes + \li Move to next item in output \li F6 \row - \li Move to previous item in output panes + \li Move to previous item in output \li Shift+F6 \row \li Activate \uicontrol Projects view diff --git a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc index 89064fe4de2..74aee3aacbb 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc @@ -332,7 +332,7 @@ \li In the \uicontrol Tests view, select the tests to run. - \li In the \uicontrol {Test Results} output pane, select: + \li In the \uicontrol {Test Results}, select: \list \li \inlineimage icons/run_small.png @@ -437,8 +437,8 @@ are omitted by default. To view them, deselect the \uicontrol {Omit internal messages} and \uicontrol {Omit run configuration warnings} check boxes. - By default, test result output is limited to 100,000 characters. The output - pane is automatically scrolled down when new results are added. To display + By default, test result output is limited to 100,000 characters. The view + is automatically scrolled down when new results are added. To display full results, deselect the \uicontrol {Limit result output} check box. To disable automatic scrolling, deselect the \uicontrol {Automatically scroll results} check box. @@ -598,7 +598,7 @@ \section1 Viewing Test Output - The test results are displayed in the \uicontrol {Test Results} output pane + The test results are displayed in \l{Viewing Output}{Test Results} in XML format. XML can be parsed more easily and reliably than plain text. However, if a Qt test crashes, it might not produce complete XML code that @@ -608,11 +608,11 @@ \uicontrol Options > \uicontrol {Testing} > \uicontrol {Qt Test}, and then deselect the \uicontrol {Use XML output} check box. Then select the \inlineimage icons/text.png - (\uicontrol {Switch Between Visual and Text Display}) button in the - \uicontrol {Test Results} output pane to switch to the text display. + (\uicontrol {Switch Between Visual and Text Display}) button in + \uicontrol {Test Results} to switch to the text display. - The following table lists the messages that the \uicontrol {Test Results} - output pane displays: + The following table lists the messages that \uicontrol {Test Results} + displays: \table \header diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc index cbee052d365..3562e088641 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc @@ -43,7 +43,7 @@ \li \l {Move between open files} \li \l {Switch to Edit mode} \li \l {Find a specific setting} - \li \l {Open output panes} + \li \l {View output} \li \l {Find keyboard shortcuts} \li \l {Run \QC from the command line} \li \l {Show and hide sidebars} @@ -114,13 +114,16 @@ To find specific settings in \uicontrol Tools > \uicontrol Options, use the filter located at the top left of the \uicontrol Options dialog box. - \section1 Open output panes + \section1 View output - The \l{Viewing Output}{output panes} provide a list of errors and warnings - encountered during a build, detailed output from the compiler, status of a - program when it is executed, debug output, and search results. + The \l{Viewing Output}{taskbar} provides different views to output from + several sources, such as a list of errors and warnings encountered during + a build, detailed output from the compiler, status of a program when it is + executed, debug output, or search results. - To open output panes, use the following shortcuts: + \image qtcreator-output-panes-taskbar.png "Output on the taskbar" + + To view different types of output, use the following shortcuts: \list @@ -134,7 +137,7 @@ \endlist - For additional ways to open all output panes, see \l{Viewing Output}. + For additional ways to view other types of output, see \l{Viewing Output}. \section1 Find keyboard shortcuts diff --git a/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc b/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc index e337a155739..0b7b830de1c 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc @@ -34,11 +34,11 @@ \page creator-task-lists.html \nextpage creator-logging-viewer.html - \title Showing Task List Files in Issues Pane + \title Showing Task List Files in Issues You can use code scanning and analysis tools to examine source code. These tools report issues for you to fix. \QC enables you to load lists of - issues into the \uicontrol Issues pane for easier navigation. + issues into \l Issues for easier navigation. \QC expects tasks to be defined in a simple line-based file format that is easy to generate using scripts. The scripts can either convert reports from @@ -49,13 +49,13 @@ \section1 Managing Task List Entries - To open task list files in the \uicontrol Issues pane, choose \uicontrol File > + To open task list files in \uicontrol Issues, choose \uicontrol File > \uicontrol Open. Right-click a task list entry to open a context menu that contains commands for managing the entry. You can copy or remove task list entries or navigate to the corresponding source code. - \QC monitors the loaded files and displays the changes in the \uicontrol Issues - pane. To keep the current entries in a task list, but stop checking for + \QC monitors the loaded files and displays the changes in \uicontrol Issues. + To keep the current entries in a task list, but stop checking for changes, select \uicontrol {Stop Monitoring}. \section1 Task List File Format diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc index bcb5b6ee9e7..d922281b4d5 100644 --- a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc +++ b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc @@ -256,7 +256,7 @@ \b {On Unix (Linux and \macos):} \c qDebug() and related functions use the standard output and error output. When you run or debug the - application, you can view the output in the \uicontrol{Application Output} pane. + application, you can view the output in \l{Application Output}. For console applications that require input, select \uicontrol Projects > \uicontrol {Run Settings} > \uicontrol {Run in terminal}. To specify the @@ -287,9 +287,9 @@ \uicontrol {Run in terminal} for console applications. For GUI applications, \c qDebug() and related functions use the Windows API - function \c OutputDebugString(). The output is displayed in the - \uicontrol{Application Output} pane. However, only one output pane tab may be - open at a time or the output is not displayed correctly. You can use an + function \c OutputDebugString(). The output is displayed in + \uicontrol{Application Output}. However, \QC can show output from only one + source at the time for it to be displayed correctly. You can use an external debug output viewer, such as the \l{https://technet.microsoft.com/en-us/sysinternals/bb896647} {DebugView for Windows} to display output from GUI applications. diff --git a/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc b/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc index 84c754d23ab..68a4042d4e4 100644 --- a/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc +++ b/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc @@ -51,8 +51,7 @@ In the \uicontrol {Target and configuration} group, specify the command helper and arguments that will be used to construct the build command. - The build errors and warnings are parsed and displayed in the - \uicontrol Issues output pane. + The build errors and warnings are parsed and displayed in \l Issues. Select the \uicontrol {Keep original jobs number} check box to stop IncrediBuild from overriding the \c {-j} command line switch, which @@ -142,6 +141,5 @@ For more information about the settings, see \l{IncrediBuild Build Steps}. - The build errors and warnings are parsed and displayed in the - \uicontrol Issues output pane. + The build errors and warnings are parsed and displayed in \l Issues. */ diff --git a/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc b/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc index b592c38d5f6..b0f5ef2c7da 100644 --- a/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc +++ b/doc/qtcreator/src/meson/creator-projects-meson-building.qdoc @@ -68,7 +68,6 @@ \image qtcreator-meson-clean-steps.png "Meson clean steps" - The build errors and warnings are parsed and displayed in the - \uicontrol Issues output pane. + The build errors and warnings are parsed and displayed in \uicontrol Issues. */ diff --git a/doc/qtcreator/src/overview/creator-only/creator-advanced.qdoc b/doc/qtcreator/src/overview/creator-only/creator-advanced.qdoc index b64bc842677..b3949a0d920 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-advanced.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-advanced.qdoc @@ -88,12 +88,11 @@ \list - \li \l{Showing Task List Files in Issues Pane} + \li \l{Showing Task List Files in Issues} You can load report files created by code scanning and analysis - tools to the \uicontrol Issues output pane. You can navigate to the - corresponding source code by clicking the error message or by using - keyboard shortcuts. + tools to \l Issues. You can navigate to the corresponding source + code by clicking the error message or by using keyboard shortcuts. \li \l{Inspecting Internal Logs} diff --git a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc b/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc index bac658aef58..250551f442f 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc @@ -104,7 +104,7 @@ items with names consisting of plain characters, numbers, underscores, and hyphens. - \li If error messages displayed in the \uicontrol {Compile Output} pane contain + \li If error messages displayed in \l {Compile Output} contain paths where slashes are missing (for example, C:QtSDK), check your PATH variable. For more information, see \l{Troubleshooting MinGW Compilation Errors}. diff --git a/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc index 6113855b829..00cbee95dba 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc @@ -35,7 +35,6 @@ \list \li \l Android \li \l iOS - \li \l {Universal Windows Platform (UWP)} \endlist You must install the tool chain for building applications for the targeted @@ -83,17 +82,4 @@ \li \l{Qt for iOS} \endlist - \section1 Universal Windows Platform (UWP) - - Microsoft Windows 10 introduced the Universal Windows Platform (UWP), - which provides a common application platform on every device that runs - Windows 10, as a successor to Windows Runtime (WinRT) introduced by - Windows 8. The UWP core APIs are the same on all Windows devices, and - therefore applications that only use the core APIs will run on any - Windows 10 device, such as a desktop PC, or Xbox One. - - For more information about developing applications for UWP using Qt 5, see - \l{https://doc.qt.io/qt-5/winrt-support.html}{Qt for UWP}. - - \note Developing for UWP using Qt 6 is not supported. */ diff --git a/doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc b/doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc index 70ff6464d68..4b54044fcbe 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-mobile-targets.qdoc @@ -38,7 +38,7 @@ You can connect \l{glossary-device}{devices} to the development PC to run, debug, and analyze applications built for them from \QC. When you install Qt for a - target platform, such as Android, QNX, or Universal Windows Platform (UWP), + target platform, such as Android or QNX, the build and run settings for the development targets might be set up automatically in \QC. diff --git a/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc index f17cb98fbde..5c846fb8fbc 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc @@ -81,17 +81,15 @@ \li \image ok \li \inlineimage ok \row - \li \l{Universal Windows Platform (UWP)}{UWP} - \li - \li - \li \image ok - \row \li \l{Building Applications for the Web}{WebAssembly} \li \image ok \li \image ok \li \image ok \endtable + \note UWP support was removed from \QC 8.0. + To develop for UWP using Qt 5, use \QC 7.0, or earlier. + \QC automatically runs scheduled checks for updates based on the settings specified in \uicontrol Tools > \uicontrol Options \uicontrol Environment > \uicontrol Update. diff --git a/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc b/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc index fd49fe581b9..499a5ff1fd5 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc @@ -32,7 +32,7 @@ Custom output parsers scan command line output for error and warning patterns that you specify and create entries - for found patterns in the \uicontrol Issues output pane. + for found patterns in \l Issues. To view or add custom output parsers, select \uicontrol Tools > \uicontrol Options > @@ -68,9 +68,9 @@ \li In the \uicontrol {Error message capture pattern} field, specify a regular expression to define what is an error. The custom parser matches the compile output line by line against the - regular expression and displays errors in the \uicontrol Issues - output pane. Create regular expression groups that contain - the file name, line number and error message. + regular expression and displays errors in \l Issues. Create + regular expression groups that contain the file name, line number + and error message. \li In the \uicontrol {Capture Positions} field, map the regular expression groups to \uicontrol {File name}, \uicontrol {Line number}, and \uicontrol Message. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc index 7d44a428848..93949aa6584 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc @@ -85,8 +85,8 @@ \li Click \inlineimage icons/run_small.png (\uicontrol Run) to build and run the application. - \li To see the compilation progress, press \key{Alt+4} to open the - \uicontrol {Compile Output} pane. + \li To see the compilation progress, press \key{Alt+4} to open + \l {Compile Output}. If build errors occur, check that a Qt version and \l{Adding Compilers}{compiler} are installed and diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc index a16d5bc93de..762be997286 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc @@ -46,8 +46,7 @@ To check that the application code can be compiled and linked for a device, you can build the project. The build errors and warnings are displayed in - the \uicontrol {Issues} output pane. More detailed information is displayed - in the \uicontrol {Compile Output} pane. + the \l Issues. More detailed information is displayed in \l {Compile Output}. To build an application: diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc index 79280560dca..86b6f168719 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc @@ -245,7 +245,7 @@ \section1 Troubleshooting \MinGW Compilation Errors - If error messages displayed in the \uicontrol {Compile Output} pane contain + If error messages displayed in \l {Compile Output} contain paths where slashes are missing (for example, \c {C:QtSDK}), check your PATH variable. At the command line, enter the following commands: diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc index baa3e32c566..72258f56284 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc @@ -659,28 +659,22 @@ value of the widget, the user-provided value is stored and will become the new default value the next time the wizard is run. - \li \c data specifies settings for the widget: + \li \c visible is set to \c true if the widget is visible, otherwise + it is set to \c false. By default, it is set to \c true. - \list + \li \c enabled is set to \c true if the widget is enabled, otherwise + it is set to \c false. By default, it is set to \c true. - \li \c visible is set to \c true if the widget is visible, otherwise - it is set to \c false. By default, it is set to \c true. + \li \c mandatory is set to \c true if this widget must have a value + for the \uicontrol Next button to become enabled. By default, it + is set to \c true. - \li \c enabled is set to \c true if the widget is enabled, otherwise - it is set to \c false. By default, it is set to \c true. + \li \c span is set to hide the label and to span the form. By + default, it is set to \c false. For more information, see + \l{Using Variables in Wizards}. - \li \c mandatory is set to \c true if this widget must have a value - for the \uicontrol Next button to become enabled. By default, it - is set to \c true. - - \li \c span is set to hide the label and to span the form. By - default, it is set to \c false. For more information, see - \l{Using Variables in Wizards}. - - \endlist - - The additional settings available for a particular widget are described - in the following sections. + \li \c data specifies additional settings for the particular widget type, as described + in the following sections. \endlist diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdoc index 329b69094bf..02975bb3981 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdoc @@ -86,7 +86,7 @@ errors occur, if possible. \li Select \uicontrol {Show command lines} to print actual - command lines to the compile output pane instead of + command lines to \l{Compile Output} instead of high-level descriptions. \li Select \uicontrol {Force probes} to force re-execution of diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc index d5899ee8e16..1a1f0a19a29 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc @@ -58,9 +58,6 @@ \li \l{Connecting MCUs}{MCU Device} (commercial only) \li \l{Connecting QNX Devices}{QNX Device} \li \l{Building Applications for the Web}{WebAssembly Runtime} - \li \l{https://doc.qt.io/qt-5/winrt-support.html}{Windows Phone} (Qt 5) - \li Windows Phone Emulator (Qt 5) - \li Windows Runtime (local, Qt 5) \endlist \section1 Filtering Kit Settings diff --git a/doc/qtcreator/src/projects/creator-projects-running.qdoc b/doc/qtcreator/src/projects/creator-projects-running.qdoc index 4d4bbbe4f34..d96d8329516 100644 --- a/doc/qtcreator/src/projects/creator-projects-running.qdoc +++ b/doc/qtcreator/src/projects/creator-projects-running.qdoc @@ -77,9 +77,9 @@ Select \uicontrol Manage to manage device settings. For example, you can add AVDs or manually start disconnected AVDs. - The \uicontrol {Application Output} pane displays the status of the + \l {Application Output} displays the status of the application while it is running. You can select the \uicontrol Run button - in the pane to re-run applications without building them first. This is + to re-run applications without building them first. This is useful when developing Qt Quick applications, because the QML files are interpreted at runtime. Therefore, the application does not need to be built again if you edited only QML files. This saves time especially if diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc index 23bc02bec20..ffdc1f1f039 100644 --- a/doc/qtcreator/src/qtcreator-toc.qdoc +++ b/doc/qtcreator/src/qtcreator-toc.qdoc @@ -228,7 +228,7 @@ \li \l{Using Command Line Options} \li \l{Keyboard Shortcuts} \li \l{Using External Tools} - \li \l{Showing Task List Files in Issues Pane} + \li \l{Showing Task List Files in Issues} \li \l{Inspecting Internal Logs} \li \l{Managing Data Collection} \list diff --git a/doc/qtcreator/src/user-interface/creator-ui.qdoc b/doc/qtcreator/src/user-interface/creator-ui.qdoc index 8e8536d7763..8f6beb09927 100644 --- a/doc/qtcreator/src/user-interface/creator-ui.qdoc +++ b/doc/qtcreator/src/user-interface/creator-ui.qdoc @@ -77,8 +77,8 @@ You can use the kit selector (2) to select the \l{glossary-buildandrun-kit}{kit} for running (3), debugging (4), or - building (5) the application. Output from these actions is displayed in the - output panes (7). + building (5) the application. Output from these actions is displayed on + the task bar (7). You can use the \l{Searching with the Locator}{locator} (6) to browse through projects, files, classes, functions, documentation, and file @@ -93,7 +93,7 @@ \li \l{Selecting Modes}{Mode selector} \li \l{Working with Sidebars}{Sidebars} \li \l{Browsing Project Contents}{Views} - \li \l{Viewing Output}{Output panes} + \li \l{Viewing Output}{Output} \endlist \else @@ -377,9 +377,9 @@ \title Viewing Output - \image qtcreator-general-messages.png "General Messages output pane" + \image qtcreator-general-messages.png "General Messages" - The task pane in \QC can display one of the following panes: + The taskbar in \QC can display following types of output: \list @@ -405,30 +405,30 @@ \endlist - Output panes are available on the taskbar in all \l{Selecting Modes}{modes}. + Output is available on the taskbar in all \l{Selecting Modes}{modes}. - \image qtcreator-output-panes-taskbar.png "Output panes on the taskbar" + \image qtcreator-output-panes-taskbar.png "Output on the taskbar" - You can open output panes in the following ways: + You can view output in the following ways: \list - \li Select the output pane on the taskbar. - \li Select \key Alt (\key Cmd on \macos) and the number of the pane on + \li Select the output view on the taskbar. + \li Select \key Alt (\key Cmd on \macos) and the number of the view on the taskbar. \li Select \inlineimage icons/output-pane-menu.png - , and then select the pane to open. - \li Select \uicontrol View > \uicontrol {Output Panes}. + , and then select the view to open. + \li Select \uicontrol View > \uicontrol Output. The menu items also display the keyboard shortcuts that you can use. \endlist - To maximize an open output pane, select the \inlineimage icons/arrowup.png - (\uicontrol {Maximize Output Pane}) button or press \key {Alt+Shift+9}. + To maximize an open output view, select the \inlineimage icons/arrowup.png + (\uicontrol Maximize) button or press \key {Alt+Shift+9}. To increase or decrease the output text size, select \inlineimage icons/plus.png (\uicontrol {Zoom In}) or \inlineimage icons/minus.png (\uicontrol {Zoom Out}), or press \key Ctrl++ or \key Ctrl+-. Zooming is - not supported in all output panes. + not supported in all output views. To open the \uicontrol{General Messages} and \if defined(qtcreator) @@ -436,16 +436,15 @@ \else \l{Using Git}{Version Control} \endif - panes, select \uicontrol View > \uicontrol {Output Panes}. + views, select \uicontrol View > \uicontrol Output. \if defined(qtcreator) - To display the \uicontrol {To-Do Entries} pane, enable the \uicontrol Todo - plugin. + To view \uicontrol {To-Do Entries}, enable the \uicontrol Todo plugin. \endif For more information about the \uicontrol {QML Debugger Console} view, see \l{Executing JavaScript Expressions}. - If the text in the output panes is not displayed correctly, \QC might + If the text in the output is not displayed correctly, \QC might be using a different codec from the one used by the tools that generate the output. To specify the codec to use, select \uicontrol Tools > \uicontrol Options > \uicontrol Environment > \uicontrol Interface, and @@ -455,7 +454,7 @@ \section1 Finding and Filtering Output - To search from output, press \key {Ctrl+F} when the pane is active. Enter + To search from output, press \key {Ctrl+F} when the view is active. Enter search criteria in the \uicontrol Find field. For more information, see \l{Finding and Replacing}. @@ -466,11 +465,11 @@ case-sensitivity. Select \uicontrol {Show Non-matching Lines} to hide the lines that match the filter. - Finding and filtering are not supported in all output panes. + Finding and filtering are not supported in all output views. \section1 Issues - The \uicontrol{Issues} pane provides lists of following types of issues: + \uicontrol{Issues} provides lists of following types of issues: \list @@ -491,8 +490,8 @@ {Errors and warnings from the current editor}. \endif - \li \uicontrol Compile - Selected output from the compiler. Open the - \uicontrol {Compile Output} pane for more detailed information. + \li \uicontrol Compile - Selected output from the compiler. Open + \uicontrol {Compile Output} for more detailed information. \li \uicontrol{Debug Information} - Lists debug information packages that might be missing. @@ -510,7 +509,7 @@ \if defined(qtcreator) \li \uicontrol {My Tasks} - Entries from a task list file (.tasks) generated - by \l{Showing Task List Files in Issues Pane} + by \l{Showing Task List Files in Issues} {code scanning and analysis tools}. \li \uicontrol Python - Runtime errors and exceptions of Python scripts. @@ -522,12 +521,12 @@ \endlist - The pane filters out irrelevant output from the build tools and presents the + The view filters out irrelevant output from the build tools and presents the issues in an organized way. To further filter the output by type, select \inlineimage icons/filtericon.png (\uicontrol {Filter Tree}) and then select a filter. - \image qtcreator-issues.png "Issues output pane" + \image qtcreator-issues.png "Issues" Select one or several lines to apply context-menu actions to their contents. You can remove the selected lines or copy their contents to the clipboard. @@ -539,23 +538,23 @@ select \uicontrol {Show in Editor} in the context menu. The entry must contain the name of the file where the issue was found. - To view more information about an issue in the \uicontrol {Compile Output} pane, + To view more information about an issue in \l {Compile Output}, select \uicontrol {Show Output} in the context menu. To jump from one issue to the next or previous one, press \key F6 and \key Shift+F6. - By default, the \uicontrol Issues pane is cleared on a new build. To keep + By default, the \uicontrol Issues view is cleared on a new build. To keep the issues from the previous build rounds, deselect \uicontrol Tools > \uicontrol Options > \uicontrol {Build & Run} > \uicontrol General > \uicontrol {Clear issues list on new build}. \section1 Search Results - In the \uicontrol{Search Results} pane, you can search through projects, files on + In \uicontrol{Search Results}, you can search through projects, files on a file system or the currently open files: - \image qtcreator-search-results.png "Search Results output pane" + \image qtcreator-search-results.png "Search Results" The search results are stored in the search history (1) from which you can select earlier searches. @@ -570,7 +569,7 @@ \section1 Application Output - The \uicontrol{Application Output} pane displays the status of a program when + \uicontrol{Application Output} displays the status of a program when it is executed, and the debug output. \image qtcreator-application-output.png @@ -589,37 +588,36 @@ \uicontrol Tools > \uicontrol Options > \uicontrol {Build & Run} > \uicontrol {Application Output}, or click the \inlineimage icons/settings.png (\uicontrol {Open Settings Page}) button. You can select whether to open - the \uicontrol{Application Output} pane on output when running or debugging + \uicontrol{Application Output} on output when running or debugging applications, to clear old output on a new run, to word-wrap output, and to limit output to the specified number of lines. \section1 Compile Output - The \uicontrol{Compile Output} pane provides all output from the compiler. + \uicontrol{Compile Output} provides all output from the compiler. The \uicontrol{Compile Output} is a more detailed version of information - displayed in the \uicontrol{Issues} pane. + displayed in \l Issues. - \image qtcreator-compile-output.png "Compile Output pane" + \image qtcreator-compile-output.png "Compile Output" Double-click on a file name in an error message to open the file in the code editor. Select the \uicontrol {Cancel Build} button to cancel the build. - To specify whether to open the \uicontrol {Compile Output} pane on output + To specify whether to open the \uicontrol {Compile Output} view on output when building applications, select \uicontrol Tools > \uicontrol Options > \uicontrol {Build & Run} > \uicontrol {Compile Output}, and then select the - \uicontrol {Open pane when building} check box. + \uicontrol {Open Compile Output when building} check box. In the \uicontrol {Limit output to} field, you can specify the maximum - amount of build output lines to display in the pane. + amount of build output lines to display. You can also reach the options page by clicking \inlineimage icons/settings.png (\uicontrol {Open Settings Page}). - To copy the output from the pane to the clipboard, select - \uicontrol {Select All} in the context menu, and then select - \uicontrol Copy. Save the output as a file if you want to - examine it later without having to build the project again. + To copy the output to the clipboard, select \uicontrol {Select All} in the + context menu, and then select \uicontrol Copy. Save the output as a file if + you want to examine it later without having to build the project again. This is useful for large projects that take a long time to build. \section2 Parsing Existing Compile Output @@ -641,8 +639,8 @@ \li In the \uicontrol {Use parsers from kit} field, select the kit to use for parsing the output. Select \uicontrol Manage to view and modify kit settings. - \li The parser displays the parsed output in the \uicontrol Issues - pane. By default, the pane is cleared before adding the new output. + \li The parser displays the parsed output in \l Issues. By default, the + view is cleared before adding the new output. Deselect the \uicontrol {Clear existing tasks} check box to append the new output to the old output. \li Select \uicontrol OK to start parsing. @@ -651,7 +649,7 @@ \if defined(qtcreator) \section1 To-Do Entries - The \uicontrol {To-Do Entries} pane lists the BUG, FIXME, NOTE, TODO, and + \uicontrol {To-Do Entries} lists the BUG, FIXME, NOTE, TODO, and WARNING keywords from the current file, from all project files, or from a subproject. Click the icons on the toolbar to show only the selected keywords. @@ -688,8 +686,8 @@ and load the plugin. In addition, you can open task list files generated by code scanning and - analysis tools in the \uicontrol Issues pane. For more information, see - \l{Showing Task List Files in Issues Pane}. + analysis tools in \l Issues. For more information, see + \l{Showing Task List Files in Issues}. \endif */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc index c00cd06cb1b..3555b137d82 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc @@ -129,9 +129,9 @@ \li \l{Using Subversion} \endlist - The \uicontrol{Version Control} output pane displays the commands that are + \uicontrol{Version Control} displays the commands that are executed, a timestamp, and the relevant output. Select \uicontrol View > - \uicontrol {Output Panes} > \uicontrol {Version Control} to open the pane. + \uicontrol Output > \uicontrol {Version Control} to open the view. \image qtcreator-vcs-pane.png diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index 348c775ea70..2f6236113ba 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -198,9 +198,9 @@ \section2 Viewing Git Status - To view the status of the repository in the \uicontrol {Version Control} - output pane, select \uicontrol Status. The context menu contains additional - actions, such as selecting and clearing all entries in the pane, copying + To view the status of the repository in \uicontrol {Version Control}, + select \uicontrol Status. The context menu contains additional + actions, such as selecting and clearing all entries, copying text, and opening files. \section2 Committing Changes to Git diff --git a/doc/qtcreatordev/src/qtcreator-dev.qdoc b/doc/qtcreatordev/src/qtcreator-dev.qdoc index 7a1e1093be8..11942e052a8 100644 --- a/doc/qtcreatordev/src/qtcreator-dev.qdoc +++ b/doc/qtcreatordev/src/qtcreator-dev.qdoc @@ -224,11 +224,11 @@ One way to handle that would be to let the tool create an output file, which is then opened within \QC. You provide an editor (probably read-only) for handling this file. For lists of issues, consider creating task list files - which are shown in the \uicontrol Issues output pane. + which are shown in \l Issues. \list \li \l{https://doc.qt.io/qtcreator/creator-task-lists.html} - {Showing Task List Files in the Issues Pane} + {Showing Task List Files in Issues} \li \l{Creating Plugins} \li \l{Qt Creator Coding Rules} \omit diff --git a/doc/qtcreatordev/src/qtcreator-ui-text.qdoc b/doc/qtcreatordev/src/qtcreator-ui-text.qdoc index f7fbad18096..059ef89c030 100644 --- a/doc/qtcreatordev/src/qtcreator-ui-text.qdoc +++ b/doc/qtcreatordev/src/qtcreator-ui-text.qdoc @@ -404,7 +404,7 @@ \li Dialog that provides feedback to users, in the form of status information, a warning, or an error message. \image qtcreator-error-message.png "Message box" - Output from Qt Creator should be displayed in output panes, + Output from Qt Creator should be displayed in output views, instead. \li Use the event as the title and provide a solution in the message box. @@ -417,10 +417,10 @@ Use descriptive, but short mode names. They have to fit in the \uicontrol {Mode selector}. \row - \li Output pane - \li A pane displayed in the task pane that displays output from Qt Creator. - \image qtcreator-output-pane.png "Output pane" - \li Use descriptive names for output panes. + \li Output + \li Views to display output from Qt Creator. + \image qtcreator-output-pane.png "Output" + \li Use descriptive names for output views. \row \li Sidebar \li A view available in the \uicontrol Edit and \uicontrol Debug modes that diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index f801369421a..4d231e7063b 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -3,15 +3,15 @@ import qbs.Environment import qbs.FileInfo Module { - property string qtcreator_display_version: '7.0.0' + property string qtcreator_display_version: '8.0.0-beta1' property string ide_version_major: '7' - property string ide_version_minor: '0' + property string ide_version_minor: '82' property string ide_version_release: '0' property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.' + ide_version_release property string ide_compat_version_major: '7' - property string ide_compat_version_minor: '0' + property string ide_compat_version_minor: '82' property string ide_compat_version_release: '0' property string qtcreator_compat_version: ide_compat_version_major + '.' + ide_compat_version_minor + '.' + ide_compat_version_release diff --git a/qtcreator_ide_branding.pri b/qtcreator_ide_branding.pri index 7cbfec223da..bb1f7131f26 100644 --- a/qtcreator_ide_branding.pri +++ b/qtcreator_ide_branding.pri @@ -1,6 +1,6 @@ -QTCREATOR_VERSION = 7.0.0 -QTCREATOR_COMPAT_VERSION = 7.0.0 -QTCREATOR_DISPLAY_VERSION = 7.0.0 +QTCREATOR_VERSION = 7.82.0 +QTCREATOR_COMPAT_VERSION = 7.82.0 +QTCREATOR_DISPLAY_VERSION = 8.0.0-beta1 QTCREATOR_COPYRIGHT_YEAR = 2022 IDE_DISPLAY_NAME = Qt Creator diff --git a/share/qtcreator/android/sdk_definitions.json b/share/qtcreator/android/sdk_definitions.json index 56ac6b4f47d..76e67c9fced 100644 --- a/share/qtcreator/android/sdk_definitions.json +++ b/share/qtcreator/android/sdk_definitions.json @@ -1,12 +1,12 @@ { "common": { "sdk_tools_url": { - "linux": "https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip", - "linux_sha256": "89f308315e041c93a37a79e0627c47f21d5c5edbe5e80ea8dc0aac8a649e0e92", - "windows": "https://dl.google.com/android/repository/commandlinetools-win-6609375_latest.zip", - "windows_sha256": "40bba20275180194bebf89bb58c74d712bb93cc401f36bd2f8f32383acf9826c", - "mac": "https://dl.google.com/android/repository/commandlinetools-mac-6609375_latest.zip", - "mac_sha256": "2c3822db1c916655223e5ee8ce0fbf6b73d0b99012045c9dc8eaa6a5736c0c55" + "linux": "https://dl.google.com/android/repository/commandlinetools-linux-8092744_latest.zip", + "linux_sha256": "d71f75333d79c9c6ef5c39d3456c6c58c613de30e6a751ea0dbd433e8f8b9cbf", + "windows": "https://dl.google.com/android/repository/commandlinetools-win-8092744_latest.zip", + "windows_sha256": "5de99ed67cb2e30fe443baf8b282d1b0b6247d0c25c6d888a7e8657b3b35c281", + "mac": "https://dl.google.com/android/repository/commandlinetools-mac-8092744_latest.zip", + "mac_sha256": "1de25523d595198d29666f9976eed65d99bbc5e4a3e8e48e5d6c98bb7e9030cc" }, "sdk_essential_packages": { "default": ["platform-tools", "platforms;android-31", "cmdline-tools;latest"], diff --git a/src/app/main.cpp b/src/app/main.cpp index e17ef27e209..c2a64e6d9ce 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -289,12 +289,6 @@ static void setHighDpiEnvironmentVariable() && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#if QT_VERSION == QT_VERSION_CHECK(5, 14, 0) - // work around QTBUG-80934 - QGuiApplication::setHighDpiScaleFactorRoundingPolicy( - Qt::HighDpiScaleFactorRoundingPolicy::Round); -#endif - #endif } else { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) diff --git a/src/libs/advanceddockingsystem/dockoverlay.cpp b/src/libs/advanceddockingsystem/dockoverlay.cpp index 904425ef13d..f98c3fbd5ff 100644 --- a/src/libs/advanceddockingsystem/dockoverlay.cpp +++ b/src/libs/advanceddockingsystem/dockoverlay.cpp @@ -39,7 +39,6 @@ #include "dockareatitlebar.h" #include <utils/hostosinfo.h> -#include <utils/porting.h> #include <QCursor> #include <QGridLayout> @@ -759,9 +758,9 @@ namespace ADS { {"Arrow", DockOverlayCross::ArrowColor}, {"Shadow", DockOverlayCross::ShadowColor}}; - auto colorList = colors.split(' ', Utils::SkipEmptyParts); + auto colorList = colors.split(' ', Qt::SkipEmptyParts); for (const auto &colorListEntry : colorList) { - auto componentColor = colorListEntry.split('=', Utils::SkipEmptyParts); + auto componentColor = colorListEntry.split('=', Qt::SkipEmptyParts); int component = colorCompenentStringMap.value(componentColor[0], -1); if (component < 0) continue; diff --git a/src/libs/advanceddockingsystem/workspacemodel.cpp b/src/libs/advanceddockingsystem/workspacemodel.cpp index fc76ce31804..b23cf394f46 100644 --- a/src/libs/advanceddockingsystem/workspacemodel.cpp +++ b/src/libs/advanceddockingsystem/workspacemodel.cpp @@ -161,13 +161,9 @@ QHash<int, QByteArray> WorkspaceModel::roleNames() const {LastWorkspaceRole, "activeWorkspace"}, {ActiveWorkspaceRole, "lastWorkspace"}}; -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) auto defaultRoles = QAbstractTableModel::roleNames(); defaultRoles.insert(extraRoles); return defaultRoles; -#else - return QAbstractTableModel::roleNames().unite(extraRoles); -#endif } void WorkspaceModel::sort(int column, Qt::SortOrder order) diff --git a/src/libs/clangsupport/connectionclient.cpp b/src/libs/clangsupport/connectionclient.cpp index d9addc6ecee..9645c1f6a42 100644 --- a/src/libs/clangsupport/connectionclient.cpp +++ b/src/libs/clangsupport/connectionclient.cpp @@ -327,15 +327,8 @@ void ConnectionClient::connectStandardOutputAndError(QtcProcess *process) const void ConnectionClient::connectLocalSocketError() const { - void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSocketError) -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - = &QLocalSocket::error; -#else - = &QLocalSocket::errorOccurred; -#endif - connect(m_localSocket, - LocalSocketErrorFunction, + &QLocalSocket::errorOccurred, this, &ConnectionClient::printLocalSocketError); } diff --git a/src/libs/clangsupport/connectionserver.h b/src/libs/clangsupport/connectionserver.h index 941423b0d15..c9288fd9aff 100644 --- a/src/libs/clangsupport/connectionserver.h +++ b/src/libs/clangsupport/connectionserver.h @@ -81,15 +81,8 @@ public: private: void connectToLocalServer(const QString &connectionName) { - void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSocketError) -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - = &QLocalSocket::error; -#else - = &QLocalSocket::errorOccurred; -#endif - QObject::connect(&m_localSocket, - LocalSocketErrorFunction, + &QLocalSocket::errorOccurred, [&] (QLocalSocket::LocalSocketError) { qWarning() << "ConnectionServer error:" << m_localSocket.errorString() << connectionName; }); diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 46dff4d78a8..a4e66f6fc81 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -53,7 +53,7 @@ #include <utils/executeondestruction.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/qtcsettings.h> @@ -422,7 +422,7 @@ QString PluginManager::systemInformation() QtcProcess qtDiagProc; qtDiagProc.setCommand(qtDiag); qtDiagProc.runBlocking(); - if (qtDiagProc.result() == QtcProcess::FinishedWithSuccess) + if (qtDiagProc.result() == ProcessResult::FinishedWithSuccess) result += qtDiagProc.allOutput() + "\n"; result += "Plugin information:\n\n"; auto longestSpec = std::max_element(d->pluginSpecs.cbegin(), d->pluginSpecs.cend(), diff --git a/src/libs/languageserverprotocol/basemessage.cpp b/src/libs/languageserverprotocol/basemessage.cpp index 8c1d4c1a2ff..f4a34a61783 100644 --- a/src/libs/languageserverprotocol/basemessage.cpp +++ b/src/libs/languageserverprotocol/basemessage.cpp @@ -27,8 +27,6 @@ #include "jsonrpcmessages.h" -#include <utils/mimetypes/mimedatabase.h> - #include <QBuffer> #include <QTextCodec> diff --git a/src/libs/languageserverprotocol/basemessage.h b/src/libs/languageserverprotocol/basemessage.h index b2835aa7d74..db3014f0126 100644 --- a/src/libs/languageserverprotocol/basemessage.h +++ b/src/libs/languageserverprotocol/basemessage.h @@ -27,8 +27,6 @@ #include "languageserverprotocol_global.h" -#include <utils/mimetypes/mimetype.h> - #include <QByteArray> #include <QCoreApplication> #include <QLoggingCategory> diff --git a/src/libs/languageserverprotocol/clientcapabilities.cpp b/src/libs/languageserverprotocol/clientcapabilities.cpp index c3665462f81..33c29df9287 100644 --- a/src/libs/languageserverprotocol/clientcapabilities.cpp +++ b/src/libs/languageserverprotocol/clientcapabilities.cpp @@ -29,12 +29,12 @@ namespace LanguageServerProtocol { Utils::optional<QList<SymbolKind> > SymbolCapabilities::SymbolKindCapabilities::valueSet() const { - Utils::optional<QList<int>> array = optionalArray<int>(valueSetKey); - if (!array) - return Utils::nullopt; - return Utils::make_optional(Utils::transform(array.value(), [] (int value) { - return static_cast<SymbolKind>(value); - })); + if (Utils::optional<QList<int>> array = optionalArray<int>(valueSetKey)) { + return Utils::make_optional(Utils::transform(*array, [] (int value) { + return static_cast<SymbolKind>(value); + })); + } + return Utils::nullopt; } void SymbolCapabilities::SymbolKindCapabilities::setValueSet(const QList<SymbolKind> &valueSet) diff --git a/src/libs/languageserverprotocol/completion.cpp b/src/libs/languageserverprotocol/completion.cpp index f4e1295d75d..9f04338e87e 100644 --- a/src/libs/languageserverprotocol/completion.cpp +++ b/src/libs/languageserverprotocol/completion.cpp @@ -44,14 +44,12 @@ Utils::optional<MarkupOrString> CompletionItem::documentation() const Utils::optional<CompletionItem::InsertTextFormat> CompletionItem::insertTextFormat() const { - Utils::optional<int> value = optionalValue<int>(insertTextFormatKey); - return value.has_value() - ? Utils::make_optional(CompletionItem::InsertTextFormat(value.value())) - : Utils::nullopt; + if (Utils::optional<int> value = optionalValue<int>(insertTextFormatKey)) + return Utils::make_optional(CompletionItem::InsertTextFormat(*value)); + return Utils::nullopt; } - CompletionItemResolveRequest::CompletionItemResolveRequest(const CompletionItem ¶ms) : Request(methodName, params) { } diff --git a/src/libs/languageserverprotocol/icontent.h b/src/libs/languageserverprotocol/icontent.h index 35cc84382a1..9173bda66ec 100644 --- a/src/libs/languageserverprotocol/icontent.h +++ b/src/libs/languageserverprotocol/icontent.h @@ -28,7 +28,6 @@ #include "basemessage.h" #include "lsputils.h" -#include <utils/mimetypes/mimetype.h> #include <utils/qtcassert.h> #include <utils/variant.h> diff --git a/src/libs/languageserverprotocol/initializemessages.cpp b/src/libs/languageserverprotocol/initializemessages.cpp index ab09cf1120c..5fcbfa9787a 100644 --- a/src/libs/languageserverprotocol/initializemessages.cpp +++ b/src/libs/languageserverprotocol/initializemessages.cpp @@ -85,12 +85,12 @@ Utils::optional<QList<CompletionItemKind::Kind>> TextDocumentClientCapabilities::CompletionCapabilities::CompletionItemKindCapabilities:: valueSet() const { - Utils::optional<QList<int>> array = optionalArray<int>(valueSetKey); - if (!array) - return Utils::nullopt; - return Utils::make_optional(Utils::transform(array.value(), [] (int value) { - return static_cast<CompletionItemKind::Kind>(value); - })); + if (Utils::optional<QList<int>> array = optionalArray<int>(valueSetKey)) { + return Utils::make_optional(Utils::transform(*array, [] (int value) { + return static_cast<CompletionItemKind::Kind>(value); + })); + } + return Utils::nullopt; } void diff --git a/src/libs/languageserverprotocol/jsonobject.h b/src/libs/languageserverprotocol/jsonobject.h index 3b32ed81f58..9f27b478cd1 100644 --- a/src/libs/languageserverprotocol/jsonobject.h +++ b/src/libs/languageserverprotocol/jsonobject.h @@ -150,9 +150,8 @@ Utils::optional<LanguageClientValue<T>> JsonObject::optionalClientValue(const QS template<typename T> QList<T> JsonObject::array(const QString &key) const { - const Utils::optional<QList<T>> &array = optionalArray<T>(key); - if (array.has_value()) - return array.value(); + if (const Utils::optional<QList<T>> &array = optionalArray<T>(key)) + return *array; qCDebug(conversionLog) << QString("Expected array under %1 in:").arg(key) << *this; return {}; } diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.cpp b/src/libs/languageserverprotocol/jsonrpcmessages.cpp index 2b71d34a754..abd19f4b68f 100644 --- a/src/libs/languageserverprotocol/jsonrpcmessages.cpp +++ b/src/libs/languageserverprotocol/jsonrpcmessages.cpp @@ -28,7 +28,6 @@ #include "lsputils.h" #include "initializemessages.h" -#include <utils/mimetypes/mimedatabase.h> #include <utils/qtcassert.h> #include <QCoreApplication> diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.h b/src/libs/languageserverprotocol/jsonrpcmessages.h index 010bc770f8a..ad11624a22a 100644 --- a/src/libs/languageserverprotocol/jsonrpcmessages.h +++ b/src/libs/languageserverprotocol/jsonrpcmessages.h @@ -122,7 +122,7 @@ public: virtual bool parametersAreValid(QString *errorMessage) const { if (auto parameter = params()) - return parameter.value().isValid(); + return parameter->isValid(); if (errorMessage) *errorMessage = QCoreApplication::translate("LanguageServerProtocol::Notification", "No parameters in \"%1\".").arg(method()); diff --git a/src/libs/languageserverprotocol/languagefeatures.cpp b/src/libs/languageserverprotocol/languagefeatures.cpp index 08d09cc3b8c..ceea23e569d 100644 --- a/src/libs/languageserverprotocol/languagefeatures.cpp +++ b/src/libs/languageserverprotocol/languagefeatures.cpp @@ -204,10 +204,9 @@ RenameRequest::RenameRequest(const RenameParams ¶ms) Utils::optional<DocumentUri> DocumentLink::target() const { - Utils::optional<QString> optionalTarget = optionalValue<QString>(targetKey); - return optionalTarget.has_value() - ? Utils::make_optional(DocumentUri::fromProtocol(optionalTarget.value())) - : Utils::nullopt; + if (Utils::optional<QString> optionalTarget = optionalValue<QString>(targetKey)) + return Utils::make_optional(DocumentUri::fromProtocol(*optionalTarget)); + return Utils::nullopt; } Utils::optional<QJsonValue> DocumentLink::data() const diff --git a/src/libs/languageserverprotocol/lsptypes.cpp b/src/libs/languageserverprotocol/lsptypes.cpp index 65651366a3d..196f4f43753 100644 --- a/src/libs/languageserverprotocol/lsptypes.cpp +++ b/src/libs/languageserverprotocol/lsptypes.cpp @@ -26,7 +26,6 @@ #include "lsptypes.h" #include "lsputils.h" -#include <utils/mimetypes/mimedatabase.h> #include <utils/textutils.h> #include <QFile> @@ -43,7 +42,7 @@ namespace LanguageServerProtocol { Utils::optional<DiagnosticSeverity> Diagnostic::severity() const { if (auto val = optionalValue<int>(severityKey)) - return Utils::make_optional(static_cast<DiagnosticSeverity>(val.value())); + return Utils::make_optional(static_cast<DiagnosticSeverity>(*val)); return Utils::nullopt; } @@ -352,13 +351,13 @@ bool DocumentFilter::applies(const Utils::FilePath &fileName, const Utils::MimeT QRegularExpression::PatternOption option = QRegularExpression::NoPatternOption; if (fileName.caseSensitivity() == Qt::CaseInsensitive) option = QRegularExpression::CaseInsensitiveOption; - const QRegularExpression regexp(expressionForGlob(_pattern.value()), option); + const QRegularExpression regexp(expressionForGlob(*_pattern), option); if (regexp.isValid() && regexp.match(fileName.toString()).hasMatch()) return true; } if (Utils::optional<QString> _lang = language()) { auto match = [&_lang](const Utils::MimeType &mimeType){ - return _lang.value() == TextDocumentItem::mimeTypeToLanguageId(mimeType); + return *_lang == TextDocumentItem::mimeTypeToLanguageId(mimeType); }; if (mimeType.isValid() && match(mimeType)) return true; diff --git a/src/libs/languageserverprotocol/lsptypes.h b/src/libs/languageserverprotocol/lsptypes.h index 41e7459e011..5dd3f586049 100644 --- a/src/libs/languageserverprotocol/lsptypes.h +++ b/src/libs/languageserverprotocol/lsptypes.h @@ -31,6 +31,7 @@ #include <utils/fileutils.h> #include <utils/link.h> +#include <utils/mimeutils.h> #include <utils/optional.h> #include <utils/textutils.h> #include <utils/variant.h> diff --git a/src/libs/languageserverprotocol/lsputils.cpp b/src/libs/languageserverprotocol/lsputils.cpp index fdac50c3d13..2762e60eac9 100644 --- a/src/libs/languageserverprotocol/lsputils.cpp +++ b/src/libs/languageserverprotocol/lsputils.cpp @@ -25,8 +25,6 @@ #include "lsputils.h" -#include <utils/mimetypes/mimedatabase.h> - #include <QHash> #include <QLoggingCategory> #include <QVector> diff --git a/src/libs/languageserverprotocol/lsputils.h b/src/libs/languageserverprotocol/lsputils.h index 08d5e1e3ec5..47352ff6bb7 100644 --- a/src/libs/languageserverprotocol/lsputils.h +++ b/src/libs/languageserverprotocol/lsputils.h @@ -28,7 +28,6 @@ #include "languageserverprotocol_global.h" #include <utils/algorithm.h> -#include <utils/mimetypes/mimetype.h> #include <utils/optional.h> #include <utils/qtcassert.h> #include <utils/variant.h> diff --git a/src/libs/languageserverprotocol/servercapabilities.cpp b/src/libs/languageserverprotocol/servercapabilities.cpp index 125972567d0..dd2b7628eb1 100644 --- a/src/libs/languageserverprotocol/servercapabilities.cpp +++ b/src/libs/languageserverprotocol/servercapabilities.cpp @@ -43,13 +43,12 @@ void ServerCapabilities::setTextDocumentSync(const ServerCapabilities::TextDocum TextDocumentSyncKind ServerCapabilities::textDocumentSyncKindHelper() { - Utils::optional<TextDocumentSync> sync = textDocumentSync(); - if (sync.has_value()) { - if (auto kind = Utils::get_if<int>(&sync.value())) + if (Utils::optional<TextDocumentSync> sync = textDocumentSync()) { + if (auto kind = Utils::get_if<int>(&*sync)) return static_cast<TextDocumentSyncKind>(*kind); - if (auto options = Utils::get_if<TextDocumentSyncOptions>(&sync.value())) { + if (auto options = Utils::get_if<TextDocumentSyncOptions>(&*sync)) { if (const Utils::optional<int> &change = options->change()) - return static_cast<TextDocumentSyncKind>(change.value()); + return static_cast<TextDocumentSyncKind>(*change); } } return TextDocumentSyncKind::None; diff --git a/src/libs/languageserverprotocol/workspace.cpp b/src/libs/languageserverprotocol/workspace.cpp index 8b82a70dde4..3244a96bbfb 100644 --- a/src/libs/languageserverprotocol/workspace.cpp +++ b/src/libs/languageserverprotocol/workspace.cpp @@ -81,7 +81,7 @@ ExecuteCommandParams::ExecuteCommandParams(const Command &command) { setCommand(command.command()); if (command.arguments().has_value()) - setArguments(command.arguments().value()); + setArguments(*command.arguments()); } LanguageServerProtocol::WorkSpaceFolderResult::operator const QJsonValue() const diff --git a/src/libs/qmldebug/qmldebugconnection.cpp b/src/libs/qmldebug/qmldebugconnection.cpp index ad34bdddab1..cf41b4275ad 100644 --- a/src/libs/qmldebug/qmldebugconnection.cpp +++ b/src/libs/qmldebug/qmldebugconnection.cpp @@ -347,11 +347,7 @@ void QmlDebugConnection::connectToHost(const QString &hostName, quint16 port) emit logStateChange(socketStateToString(state)); }); -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - const auto errorOccurred = QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error); -#else const auto errorOccurred = &QAbstractSocket::errorOccurred; -#endif connect(socket, errorOccurred, this, [this](QAbstractSocket::SocketError error) { emit logError(socketErrorToString(error)); socketDisconnected(); @@ -392,14 +388,7 @@ void QmlDebugConnection::newConnection() connect(socket, &QLocalSocket::disconnected, this, &QmlDebugConnection::socketDisconnected, Qt::QueuedConnection); - void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSocketError) -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - = &QLocalSocket::error; -#else - = &QLocalSocket::errorOccurred; -#endif - - connect(socket, LocalSocketErrorFunction, this, [this](QLocalSocket::LocalSocketError error) { + connect(socket, &QLocalSocket::errorOccurred, this, [this](QLocalSocket::LocalSocketError error) { emit logError(socketErrorToString(static_cast<QAbstractSocket::SocketError>(error))); socketDisconnected(); }, Qt::QueuedConnection); diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 7fe9885bdd6..f5ef34bb83d 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1128,6 +1128,44 @@ bool Check::visit(UiPublicMember *ast) { if (ast->type == UiPublicMember::Property) { const QStringView typeName = ast->memberType->name; + + // Check alias properties don't reference root item + // Item { + // id: root + // property alias p1: root + // property alias p2: root.child + // + // Item { id: child } + // } + // - Show error for alias property p1 + // - Show warning for alias property p2 + + // Check if type and id stack only contain one item as we are only looking for alias + // properties in the root item. + if (typeName == QLatin1String("alias") && ast->type == AST::UiPublicMember::Property + && m_typeStack.count() == 1 && m_idStack.count() == 1 && m_idStack.top().count() == 1) { + + const QString rootId = m_idStack.top().values().first(); + if (!rootId.isEmpty()) { + if (ExpressionStatement *exp = cast<ExpressionStatement *>(ast->statement)) { + ExpressionNode *node = exp->expression; + + // Check for case property alias p1: root + if (IdentifierExpression *idExp = cast<IdentifierExpression *>(node)) { + if (!idExp->name.isEmpty() && idExp->name.toString() == rootId) + addMessage(ErrAliasReferRoot, idExp->identifierToken); + + // Check for case property alias p2: root.child + } else if (FieldMemberExpression *fmExp = cast<FieldMemberExpression *>(node)) { + if (IdentifierExpression *base = cast<IdentifierExpression *>(fmExp->base)) { + if (!base->name.isEmpty() && base->name.toString() == rootId) + addMessage(WarnAliasReferRootHierarchy, base->identifierToken); + } + } + } + } + } + // warn about dubious use of var/variant if (typeName == QLatin1String("variant") || typeName == QLatin1String("var")) { Evaluate evaluator(&_scopeChain); diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp index a51929566ba..e5e3a952c05 100644 --- a/src/libs/qmljs/qmljsplugindumper.cpp +++ b/src/libs/qmljs/qmljsplugindumper.cpp @@ -210,7 +210,7 @@ static QString qmldumpFailedMessage(const FilePath &libraryPath, const QString & "\n" "%1" "\n" - "Check 'General Messages' output pane for details." + "Check General Messages for details." ).arg(firstLines); } diff --git a/src/libs/qmljs/qmljsplugindumper.h b/src/libs/qmljs/qmljsplugindumper.h index 3503c046810..7b4e035013e 100644 --- a/src/libs/qmljs/qmljsplugindumper.h +++ b/src/libs/qmljs/qmljsplugindumper.h @@ -27,8 +27,6 @@ #include <qmljs/qmljsmodelmanagerinterface.h> -#include <utils/qtcprocess.h> - #include <QObject> #include <QHash> @@ -36,7 +34,10 @@ QT_BEGIN_NAMESPACE class QDir; QT_END_NAMESPACE -namespace Utils { class FileSystemWatcher; } +namespace Utils { +class FileSystemWatcher; +class QtcProcess; +} namespace QmlJS { diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp index 0c026ecb75f..fd9b8bac378 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp +++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp @@ -255,6 +255,10 @@ StaticAnalysisMessages::StaticAnalysisMessages() tr("Components are only allowed to have a single child element.")); newMsg(WarnComponentRequiresChildren, Warning, tr("Components require a child element.")); + newMsg(ErrAliasReferRoot, Error, + tr("Do not reference the root item as alias.")); + newMsg(WarnAliasReferRootHierarchy, Warning, + tr("Avoid referencing the root item in a hierarchy.")); } } // anonymous namespace diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.h b/src/libs/qmljs/qmljsstaticanalysismessage.h index 51a517d2352..58b1d7cfd5e 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.h +++ b/src/libs/qmljs/qmljsstaticanalysismessage.h @@ -134,7 +134,9 @@ enum Type { WarnLogicalValueDoesNotDependOnValues = 325, ErrToManyComponentChildren = 326, WarnComponentRequiresChildren = 327, - WarnDuplicateImport = 400 + WarnDuplicateImport = 400, + ErrAliasReferRoot = 401, + WarnAliasReferRootHierarchy = 402 }; class QMLJS_EXPORT PrototypeMessageData { diff --git a/src/libs/ssh/CMakeLists.txt b/src/libs/ssh/CMakeLists.txt index 6f5d1cb7642..290afe5acda 100644 --- a/src/libs/ssh/CMakeLists.txt +++ b/src/libs/ssh/CMakeLists.txt @@ -11,7 +11,6 @@ add_qtc_library(QtcSsh sshconnectionmanager.cpp sshconnectionmanager.h sshkeycreationdialog.cpp sshkeycreationdialog.h sshkeycreationdialog.ui sshlogging.cpp sshlogging_p.h - sshprocess.cpp sshprocess.h sshconnection.cpp sshconnection.h sshconnectionmanager.cpp sshconnectionmanager.h sshkeycreationdialog.cpp sshkeycreationdialog.h sshkeycreationdialog.ui diff --git a/src/libs/ssh/sftpsession.cpp b/src/libs/ssh/sftpsession.cpp index 8ecdfb374f7..2b5535e1296 100644 --- a/src/libs/ssh/sftpsession.cpp +++ b/src/libs/ssh/sftpsession.cpp @@ -26,7 +26,7 @@ #include "sftpsession.h" #include "sshlogging_p.h" -#include "sshprocess.h" +#include "sshremoteprocess.h" #include "sshsettings.h" #include <utils/fileutils.h> @@ -58,7 +58,7 @@ struct Command struct SftpSession::SftpSessionPrivate { - SshProcess sftpProc = {ProcessMode::Writer}; + QtcProcess sftpProc; QStringList connectionArgs; QByteArray output; QQueue<Command> pendingCommands; @@ -111,6 +111,8 @@ static QByteArray prompt() { return "sftp> "; } SftpSession::SftpSession(const QStringList &connectionArgs) : d(new SftpSessionPrivate) { + SshRemoteProcess::setupSshEnvironment(&d->sftpProc); + d->sftpProc.setProcessMode(ProcessMode::Writer); d->connectionArgs = connectionArgs; connect(&d->sftpProc, &QtcProcess::started, [this] { qCDebug(sshLog) << "sftp process started"; diff --git a/src/libs/ssh/sftptransfer.cpp b/src/libs/ssh/sftptransfer.cpp index 26d4306f5c7..3b2acf784c9 100644 --- a/src/libs/ssh/sftptransfer.cpp +++ b/src/libs/ssh/sftptransfer.cpp @@ -26,7 +26,7 @@ #include "sftptransfer.h" #include "sshlogging_p.h" -#include "sshprocess.h" +#include "sshremoteprocess.h" #include "sshsettings.h" #include <QDir> @@ -44,7 +44,7 @@ namespace QSsh { struct SftpTransfer::SftpTransferPrivate { - SshProcess sftpProc; + QtcProcess sftpProc; FilesToTransfer files; Internal::FileTransferType transferType; FileTransferErrorHandling errorHandlingMode; @@ -109,6 +109,7 @@ SftpTransfer::SftpTransfer(const FilesToTransfer &files, Internal::FileTransferT const QStringList &connectionArgs) : d(new SftpTransferPrivate) { + SshRemoteProcess::setupSshEnvironment(&d->sftpProc); d->files = files; d->transferType = type; d->errorHandlingMode = errorHandlingMode; diff --git a/src/libs/ssh/ssh.qbs b/src/libs/ssh/ssh.qbs index cd306d0c4a3..295117c0714 100644 --- a/src/libs/ssh/ssh.qbs +++ b/src/libs/ssh/ssh.qbs @@ -35,8 +35,6 @@ Project { "sshkeycreationdialog.ui", "sshlogging.cpp", "sshlogging_p.h", - "sshprocess.cpp", - "sshprocess.h", "sshremoteprocess.cpp", "sshremoteprocess.h", "sshremoteprocessrunner.cpp", diff --git a/src/libs/ssh/sshconnection.cpp b/src/libs/ssh/sshconnection.cpp index 839ee2d5431..2e07e12bbad 100644 --- a/src/libs/ssh/sshconnection.cpp +++ b/src/libs/ssh/sshconnection.cpp @@ -28,7 +28,6 @@ #include "sftpsession.h" #include "sftptransfer.h" #include "sshlogging_p.h" -#include "sshprocess.h" #include "sshremoteprocess.h" #include "sshsettings.h" @@ -129,7 +128,10 @@ bool operator!=(const SshConnectionParameters &p1, const SshConnectionParameters struct SshConnection::SshConnectionPrivate { SshConnectionPrivate(const SshConnectionParameters &sshParameters) - : connParams(sshParameters) {} + : connParams(sshParameters) + { + SshRemoteProcess::setupSshEnvironment(&masterProcess); + } QString fullProcessError() { @@ -166,7 +168,7 @@ struct SshConnection::SshConnectionPrivate const SshConnectionParameters connParams; SshConnectionInfo connInfo; - SshProcess masterProcess; + QtcProcess masterProcess; QString errorString; std::unique_ptr<QTemporaryDir> masterSocketDir; State state = Unconnected; @@ -307,17 +309,16 @@ SshConnection::~SshConnection() delete d; } -SshRemoteProcessPtr SshConnection::createRemoteProcess(const QString &command, ProcessMode processMode) +SshRemoteProcessPtr SshConnection::createRemoteProcess(const QString &command) { QTC_ASSERT(state() == Connected, return SshRemoteProcessPtr()); return SshRemoteProcessPtr(new SshRemoteProcess(command, - d->connectionArgs(SshSettings::sshFilePath()), - processMode)); + d->connectionArgs(SshSettings::sshFilePath()))); } -SshRemoteProcessPtr SshConnection::createRemoteShell(ProcessMode processMode) +SshRemoteProcessPtr SshConnection::createRemoteShell() { - return createRemoteProcess({}, processMode); + return createRemoteProcess({}); } SftpTransferPtr SshConnection::createUpload(const FilesToTransfer &files, diff --git a/src/libs/ssh/sshconnection.h b/src/libs/ssh/sshconnection.h index a559fc13163..d63b5b321f1 100644 --- a/src/libs/ssh/sshconnection.h +++ b/src/libs/ssh/sshconnection.h @@ -29,7 +29,6 @@ #include "ssh_global.h" #include <utils/filepath.h> -#include <utils/processutils.h> #include <QFlags> #include <QHostAddress> @@ -115,10 +114,8 @@ public: bool sharingEnabled() const; ~SshConnection(); - SshRemoteProcessPtr createRemoteProcess(const QString &command, Utils::ProcessMode processMode - = Utils::ProcessMode::Reader); - SshRemoteProcessPtr createRemoteShell(Utils::ProcessMode processMode - = Utils::ProcessMode::Reader); + SshRemoteProcessPtr createRemoteProcess(const QString &command); + SshRemoteProcessPtr createRemoteShell(); SftpTransferPtr createUpload(const FilesToTransfer &files, FileTransferErrorHandling errorHandlingMode); SftpTransferPtr createDownload(const FilesToTransfer &files, diff --git a/src/libs/ssh/sshprocess.cpp b/src/libs/ssh/sshprocess.cpp deleted file mode 100644 index 7064deed4b2..00000000000 --- a/src/libs/ssh/sshprocess.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "sshprocess.h" - -#include "sshsettings.h" - -#include <utils/environment.h> - -namespace QSsh { - -SshProcess::SshProcess(Utils::ProcessMode processMode) - : Utils::QtcProcess(processMode) -{ - Utils::Environment env = Utils::Environment::systemEnvironment(); - if (SshSettings::askpassFilePath().exists()) { - env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput()); - - // OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform. - if (!env.hasKey("DISPLAY")) - env.set("DISPLAY", ":0"); - } - setEnvironment(env); - - // Otherwise, ssh will ignore SSH_ASKPASS and read from /dev/tty directly. - setDisableUnixTerminal(); -} - -} // namespace QSsh diff --git a/src/libs/ssh/sshremoteprocess.cpp b/src/libs/ssh/sshremoteprocess.cpp index 0cb916acf13..e58cf5750d2 100644 --- a/src/libs/ssh/sshremoteprocess.cpp +++ b/src/libs/ssh/sshremoteprocess.cpp @@ -46,34 +46,38 @@ */ using namespace QSsh::Internal; +using namespace Utils; namespace QSsh { -SshRemoteProcess::SshRemoteProcess(const QString &command, const QStringList &connectionArgs, - Utils::ProcessMode processMode) - : SshProcess(processMode) +SshRemoteProcess::SshRemoteProcess(const QString &command, const QStringList &connectionArgs) + : QtcProcess() { + setupSshEnvironment(this); m_remoteCommand = command; m_connectionArgs = connectionArgs; +} - connect(this, &QtcProcess::finished, this, [this] { - QString error; - if (exitStatus() == QProcess::CrashExit) - error = tr("The ssh process crashed: %1").arg(errorString()); - emit done(error); - }); - connect(this, &QtcProcess::errorOccurred, [this](QProcess::ProcessError error) { - if (error == QProcess::FailedToStart) - emit done(errorString()); - }); +void SshRemoteProcess::emitFinished() +{ + if (exitStatus() == QProcess::CrashExit) + setErrorString(tr("The ssh process crashed: %1").arg(errorString())); + emit finished(); +} + +void SshRemoteProcess::emitErrorOccurred(QProcess::ProcessError error) +{ + if (error == QProcess::FailedToStart) + emit finished(); + emit errorOccurred(error); } void SshRemoteProcess::start() { QTC_ASSERT(!isRunning(), return); - const Utils::CommandLine cmd = fullLocalCommandLine(); + const CommandLine cmd = fullLocalCommandLine(); if (!m_displayName.isEmpty()) { - Utils::Environment env = environment(); + Environment env = environment(); env.set("DISPLAY", m_displayName); setEnvironment(env); } @@ -87,9 +91,9 @@ void SshRemoteProcess::requestX11Forwarding(const QString &displayName) m_displayName = displayName; } -Utils::CommandLine SshRemoteProcess::fullLocalCommandLine(bool inTerminal) const +CommandLine SshRemoteProcess::fullLocalCommandLine(bool inTerminal) const { - Utils::CommandLine cmd{SshSettings::sshFilePath()}; + CommandLine cmd {SshSettings::sshFilePath()}; if (!m_displayName.isEmpty()) cmd.addArg("-X"); @@ -105,4 +109,23 @@ Utils::CommandLine SshRemoteProcess::fullLocalCommandLine(bool inTerminal) const return cmd; } +bool SshRemoteProcess::setupSshEnvironment(QtcProcess *process) +{ + Environment env = process->hasEnvironment() ? process->environment() + : Environment::systemEnvironment(); + const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0")); + if (SshSettings::askpassFilePath().exists()) { + env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput()); + + // OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform. + if (!env.hasKey("DISPLAY")) + env.set("DISPLAY", ":0"); + } + process->setEnvironment(env); + + // Otherwise, ssh will ignore SSH_ASKPASS and read from /dev/tty directly. + process->setDisableUnixTerminal(); + return hasDisplay; +} + } // namespace QSsh diff --git a/src/libs/ssh/sshremoteprocess.h b/src/libs/ssh/sshremoteprocess.h index b5195454b50..f89a7313c6a 100644 --- a/src/libs/ssh/sshremoteprocess.h +++ b/src/libs/ssh/sshremoteprocess.h @@ -26,27 +26,30 @@ #pragma once #include "ssh_global.h" -#include "sshprocess.h" + +#include <utils/qtcprocess.h> namespace Utils { class CommandLine; } namespace QSsh { -class QSSH_EXPORT SshRemoteProcess : public SshProcess +class QSSH_EXPORT SshRemoteProcess : public Utils::QtcProcess { Q_OBJECT public: - SshRemoteProcess(const QString &command, const QStringList &connectionArgs, - Utils::ProcessMode processMode = Utils::ProcessMode::Reader); + SshRemoteProcess(const QString &command, const QStringList &connectionArgs); void requestX11Forwarding(const QString &displayName); - void start(); + void start() override; Utils::CommandLine fullLocalCommandLine(bool inTerminal = false) const; -signals: - void done(const QString &error); + static bool setupSshEnvironment(Utils::QtcProcess *process); + +protected: + void emitFinished() override; + void emitErrorOccurred(QProcess::ProcessError error) override; private: QString m_remoteCommand; diff --git a/src/libs/ssh/sshremoteprocessrunner.cpp b/src/libs/ssh/sshremoteprocessrunner.cpp index edbc7219b17..d0e23b6a4b3 100644 --- a/src/libs/ssh/sshremoteprocessrunner.cpp +++ b/src/libs/ssh/sshremoteprocessrunner.cpp @@ -52,10 +52,8 @@ public: QString m_command; QString m_lastConnectionErrorString; QProcess::ExitStatus m_exitStatus; - QByteArray m_stdout; - QByteArray m_stderr; int m_exitCode; - QString m_processErrorString; + QString m_errorString; State m_state; }; @@ -88,7 +86,7 @@ void SshRemoteProcessRunner::runInternal(const QString &command, setState(Connecting); d->m_lastConnectionErrorString.clear(); - d->m_processErrorString.clear(); + d->m_errorString.clear(); d->m_exitCode = -1; d->m_command = command; d->m_connection = SshConnectionManager::acquireConnection(sshParams); @@ -113,12 +111,12 @@ void SshRemoteProcessRunner::handleConnected() d->m_process = d->m_connection->createRemoteProcess(d->m_command); connect(d->m_process.get(), &SshRemoteProcess::started, this, &SshRemoteProcessRunner::handleProcessStarted); - connect(d->m_process.get(), &SshRemoteProcess::done, + connect(d->m_process.get(), &SshRemoteProcess::finished, this, &SshRemoteProcessRunner::handleProcessFinished); connect(d->m_process.get(), &SshRemoteProcess::readyReadStandardOutput, - this, &SshRemoteProcessRunner::handleStdout); + this, &SshRemoteProcessRunner::readyReadStandardOutput); connect(d->m_process.get(), &SshRemoteProcess::readyReadStandardError, - this, &SshRemoteProcessRunner::handleStderr); + this, &SshRemoteProcessRunner::readyReadStandardError); d->m_process->start(); } @@ -141,28 +139,16 @@ void SshRemoteProcessRunner::handleProcessStarted() QTC_ASSERT(d->m_state == Connected, return); setState(ProcessRunning); - emit processStarted(); + emit started(); } -void SshRemoteProcessRunner::handleProcessFinished(const QString &error) +void SshRemoteProcessRunner::handleProcessFinished() { d->m_exitStatus = d->m_process->exitStatus(); d->m_exitCode = d->m_process->exitCode(); - d->m_processErrorString = error; + d->m_errorString = d->m_process->errorString(); setState(Inactive); - emit processClosed(d->m_processErrorString); -} - -void SshRemoteProcessRunner::handleStdout() -{ - d->m_stdout += d->m_process->readAllStandardOutput(); - emit readyReadStandardOutput(); -} - -void SshRemoteProcessRunner::handleStderr() -{ - d->m_stderr += d->m_process->readAllStandardError(); - emit readyReadStandardError(); + emit finished(); } void SshRemoteProcessRunner::setState(int newState) @@ -189,40 +175,36 @@ QString SshRemoteProcessRunner::lastConnectionErrorString() const { return d->m_lastConnectionErrorString; } -bool SshRemoteProcessRunner::isProcessRunning() const +bool SshRemoteProcessRunner::isRunning() const { return d->m_process && d->m_process->isRunning(); } -QProcess::ExitStatus SshRemoteProcessRunner::processExitStatus() const +QProcess::ExitStatus SshRemoteProcessRunner::exitStatus() const { - QTC_CHECK(!isProcessRunning()); + QTC_CHECK(!isRunning()); return d->m_exitStatus; } -int SshRemoteProcessRunner::processExitCode() const +int SshRemoteProcessRunner::exitCode() const { - QTC_CHECK(processExitStatus() == QProcess::NormalExit); + QTC_CHECK(exitStatus() == QProcess::NormalExit); return d->m_exitCode; } -QString SshRemoteProcessRunner::processErrorString() const +QString SshRemoteProcessRunner::errorString() const { - return d->m_processErrorString; + return d->m_errorString; } QByteArray SshRemoteProcessRunner::readAllStandardOutput() { - const QByteArray data = d->m_stdout; - d->m_stdout.clear(); - return data; + return d->m_process.get() ? d->m_process->readAllStandardOutput() : QByteArray(); } QByteArray SshRemoteProcessRunner::readAllStandardError() { - const QByteArray data = d->m_stderr; - d->m_stderr.clear(); - return data; + return d->m_process.get() ? d->m_process->readAllStandardError() : QByteArray(); } void SshRemoteProcessRunner::cancel() diff --git a/src/libs/ssh/sshremoteprocessrunner.h b/src/libs/ssh/sshremoteprocessrunner.h index 5cb188b2e68..f543424de03 100644 --- a/src/libs/ssh/sshremoteprocessrunner.h +++ b/src/libs/ssh/sshremoteprocessrunner.h @@ -43,29 +43,27 @@ public: QString lastConnectionErrorString() const; - bool isProcessRunning() const; + bool isRunning() const; void cancel(); - QProcess::ExitStatus processExitStatus() const; - int processExitCode() const; - QString processErrorString() const; + QProcess::ExitStatus exitStatus() const; + int exitCode() const; + QString errorString() const; QByteArray readAllStandardOutput(); QByteArray readAllStandardError(); signals: void connectionError(); - void processStarted(); + void started(); + void finished(); void readyReadStandardOutput(); void readyReadStandardError(); - void processClosed(const QString &error); private: void handleConnected(); void handleConnectionError(); void handleDisconnected(); void handleProcessStarted(); - void handleProcessFinished(const QString &error); - void handleStdout(); - void handleStderr(); + void handleProcessFinished(); void runInternal(const QString &command, const QSsh::SshConnectionParameters &sshParams); void setState(int newState); diff --git a/src/libs/ssh/sshsettings.cpp b/src/libs/ssh/sshsettings.cpp index 98446b752f1..571ca5c1afa 100644 --- a/src/libs/ssh/sshsettings.cpp +++ b/src/libs/ssh/sshsettings.cpp @@ -28,6 +28,7 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> +#include <QReadWriteLock> #include <QSettings> using namespace Utils; @@ -44,6 +45,7 @@ struct SshSettings FilePath askpassFilePath; FilePath keygenFilePath; QSsh::SshSettings::SearchPathRetriever searchPathRetriever = [] { return FilePaths(); }; + QReadWriteLock lock; }; } // namespace Internal @@ -72,6 +74,7 @@ static QString keygenFilePathKey() { return QString("KeygenFilePath"); } void SshSettings::loadSettings(QSettings *settings) { + QWriteLocker locker(&sshSettings->lock); AccessSettingsGroup g(settings); QVariant value = settings->value(connectionSharingKey()); if (value.isValid() && !HostOsInfo::isWindowsHost()) @@ -89,6 +92,7 @@ void SshSettings::loadSettings(QSettings *settings) void SshSettings::storeSettings(QSettings *settings) { + QReadLocker locker(&sshSettings->lock); AccessSettingsGroup g(settings); settings->setValue(connectionSharingKey(), sshSettings->useConnectionSharing); settings->setValue(connectionSharingTimeoutKey(), @@ -101,19 +105,27 @@ void SshSettings::storeSettings(QSettings *settings) void SshSettings::setConnectionSharingEnabled(bool share) { + QWriteLocker locker(&sshSettings->lock); sshSettings->useConnectionSharing = share; } -bool SshSettings::connectionSharingEnabled() { return sshSettings->useConnectionSharing; } +bool SshSettings::connectionSharingEnabled() +{ + QReadLocker locker(&sshSettings->lock); + return sshSettings->useConnectionSharing; +} void SshSettings::setConnectionSharingTimeout(int timeInMinutes) { + QWriteLocker locker(&sshSettings->lock); sshSettings->connectionSharingTimeOutInMinutes = timeInMinutes; } int SshSettings::connectionSharingTimeout() { + QReadLocker locker(&sshSettings->lock); return sshSettings->connectionSharingTimeOutInMinutes; } +// Keep read locker locked while calling this method static FilePath filePathValue(const FilePath &value, const QStringList &candidateFileNames) { if (!value.isEmpty()) @@ -128,24 +140,45 @@ static FilePath filePathValue(const FilePath &value, const QStringList &candidat return FilePath(); } +// Keep read locker locked while calling this method static FilePath filePathValue(const FilePath &value, const QString &candidateFileName) { return filePathValue(value, QStringList(candidateFileName)); } -void SshSettings::setSshFilePath(const FilePath &ssh) { sshSettings->sshFilePath = ssh; } -FilePath SshSettings::sshFilePath() { return filePathValue(sshSettings->sshFilePath, "ssh"); } +void SshSettings::setSshFilePath(const FilePath &ssh) +{ + QWriteLocker locker(&sshSettings->lock); + sshSettings->sshFilePath = ssh; +} + +FilePath SshSettings::sshFilePath() +{ + QReadLocker locker(&sshSettings->lock); + return filePathValue(sshSettings->sshFilePath, "ssh"); +} + +void SshSettings::setSftpFilePath(const FilePath &sftp) +{ + QWriteLocker locker(&sshSettings->lock); + sshSettings->sftpFilePath = sftp; +} -void SshSettings::setSftpFilePath(const FilePath &sftp) { sshSettings->sftpFilePath = sftp; } -FilePath SshSettings::sftpFilePath() { return filePathValue(sshSettings->sftpFilePath, "sftp"); } +FilePath SshSettings::sftpFilePath() +{ + QReadLocker locker(&sshSettings->lock); + return filePathValue(sshSettings->sftpFilePath, "sftp"); +} void SshSettings::setAskpassFilePath(const FilePath &askPass) { + QWriteLocker locker(&sshSettings->lock); sshSettings->askpassFilePath = askPass; } FilePath SshSettings::askpassFilePath() { + QReadLocker locker(&sshSettings->lock); FilePath candidate; candidate = sshSettings->askpassFilePath; if (candidate.isEmpty()) @@ -155,16 +188,19 @@ FilePath SshSettings::askpassFilePath() void SshSettings::setKeygenFilePath(const FilePath &keygen) { + QWriteLocker locker(&sshSettings->lock); sshSettings->keygenFilePath = keygen; } FilePath SshSettings::keygenFilePath() { + QReadLocker locker(&sshSettings->lock); return filePathValue(sshSettings->keygenFilePath, "ssh-keygen"); } void SshSettings::setExtraSearchPathRetriever(const SearchPathRetriever &pathRetriever) { + QWriteLocker locker(&sshSettings->lock); sshSettings->searchPathRetriever = pathRetriever; } diff --git a/src/libs/tracing/qml/MainView.qml b/src/libs/tracing/qml/MainView.qml index 5de435ef12d..c00112dee64 100644 --- a/src/libs/tracing/qml/MainView.qml +++ b/src/libs/tracing/qml/MainView.qml @@ -259,19 +259,21 @@ Rectangle { hoverEnabled: enabled z: 2 - onReleased: { - if (selectionRange.creationState === selectionRange.creationSecondLimit) { - content.interactive = true; - selectionRange.creationState = selectionRange.creationFinished; - } - } - onPressed: { + function handlePress() { if (selectionRange.creationState === selectionRange.creationFirstLimit) { content.interactive = false; selectionRange.setPos(selectionRangeControl.mouseX + content.contentX); selectionRange.creationState = selectionRange.creationSecondLimit; } } + + onReleased: { + if (selectionRange.creationState === selectionRange.creationSecondLimit) { + content.interactive = true; + selectionRange.creationState = selectionRange.creationFinished; + } + } + onPressed: handlePress() onPositionChanged: { if (selectionRange.creationState === selectionRange.creationInactive) selectionRange.creationState = selectionRange.creationFirstLimit; @@ -280,7 +282,7 @@ Rectangle { selectionRange.creationState !== selectionRange.creationFinished) selectionRange.setPos(selectionRangeControl.mouseX + content.contentX); } - onCanceled: pressed() + onCanceled: handlePress() } Flickable { diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 780f3e2017a..543f3ddb488 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -86,13 +86,7 @@ add_qtc_library(Utils listutils.h macroexpander.cpp macroexpander.h mapreduce.h - mimetypes/mimedatabase.cpp mimetypes/mimedatabase.h mimetypes/mimedatabase_p.h - mimetypes/mimeglobpattern.cpp mimetypes/mimeglobpattern_p.h - mimetypes/mimemagicrule.cpp mimetypes/mimemagicrule_p.h - mimetypes/mimemagicrulematcher.cpp mimetypes/mimemagicrulematcher_p.h - mimetypes/mimeprovider.cpp mimetypes/mimeprovider_p.h - mimetypes/mimetype.cpp mimetypes/mimetype.h mimetypes/mimetype_p.h - mimetypes/mimetypeparser.cpp mimetypes/mimetypeparser_p.h + mimeutils.h multitextcursor.cpp multitextcursor.h namevaluedictionary.cpp namevaluedictionary.h namevaluedictionary.cpp namevaluedictionary.h @@ -121,7 +115,10 @@ add_qtc_library(Utils porting.h portlist.cpp portlist.h predicates.h + processenums.h processhandle.cpp processhandle.h + processinfo.cpp processinfo.h + processinterface.h processreaper.cpp processreaper.h processutils.cpp processutils.h progressindicator.cpp progressindicator.h @@ -190,6 +187,37 @@ add_qtc_library(Utils wizardpage.cpp wizardpage.h ) +option(QTC_USE_NEW_MIMEDATABASE "Use updated MIME database implementation" YES) + +if(QTC_USE_NEW_MIMEDATABASE) + set(mime_prefix "mimetypes2") +else() + set(mime_prefix "mimetypes") +endif() + +extend_qtc_library(Utils + SOURCES_PREFIX ${mime_prefix} + PUBLIC_INCLUDES ${mime_prefix} + SOURCES + mimedatabase.cpp + mimedatabase.h + mimedatabase_p.h + mimeglobpattern.cpp + mimeglobpattern_p.h + mimemagicrule.cpp + mimemagicrule_p.h + mimemagicrulematcher.cpp + mimemagicrulematcher_p.h + mimeprovider.cpp + mimeprovider_p.h + mimetype.cpp + mimetype.h + mimetype_p.h + mimetypeparser.cpp + mimetypeparser_p.h + mimeutils.cpp +) + extend_qtc_library(Utils CONDITION WIN32 SOURCES touchbar/touchbar.cpp diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h index 62bcace8ef9..ae4a718c836 100644 --- a/src/libs/utils/algorithm.h +++ b/src/libs/utils/algorithm.h @@ -1303,11 +1303,7 @@ QList<T> toList(const QSet<T> &set) template <class Key, class T> void addToHash(QHash<Key, T> *result, const QHash<Key, T> &additionalContents) { -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - result->unite(additionalContents); -#else result->insert(additionalContents); -#endif } } // namespace Utils diff --git a/src/libs/utils/archive.cpp b/src/libs/utils/archive.cpp index d05b49eb6f6..3883fa3f192 100644 --- a/src/libs/utils/archive.cpp +++ b/src/libs/utils/archive.cpp @@ -28,7 +28,7 @@ #include "algorithm.h" #include "checkablemessagebox.h" #include "environment.h" -#include "mimetypes/mimedatabase.h" +#include "mimeutils.h" #include "qtcassert.h" #include "qtcprocess.h" @@ -219,7 +219,7 @@ Archive *Archive::unarchive(const FilePath &src, const FilePath &dest) [archive] { if (!archive->m_process) return; - emit archive->finished(archive->m_process->result() == QtcProcess::FinishedWithSuccess); + emit archive->finished(archive->m_process->result() == ProcessResult::FinishedWithSuccess); archive->m_process->deleteLater(); archive->m_process = nullptr; archive->deleteLater(); diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp index f5f5f5d39a4..efad8587b0c 100644 --- a/src/libs/utils/buildablehelperlibrary.cpp +++ b/src/libs/utils/buildablehelperlibrary.cpp @@ -48,7 +48,7 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) proc.setTimeoutS(1); proc.setCommand({qtChooser, {"-print-env"}}); proc.runBlocking(); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return {}; const QString output = proc.stdOut(); int pos = output.indexOf(toolDir); @@ -132,7 +132,7 @@ QString BuildableHelperLibrary::qtVersionForQMake(const FilePath &qmakePath) qmake.setTimeoutS(5); qmake.setCommand({qmakePath, {"--version"}}); qmake.runBlocking(); - if (qmake.result() != QtcProcess::FinishedWithSuccess) { + if (qmake.result() != ProcessResult::FinishedWithSuccess) { qWarning() << qmake.exitMessage(); return QString(); } diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index df3d12b463b..cae41a187fe 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -670,13 +670,7 @@ bool ProcessArgs::prepareCommand(const CommandLine &cmdLine, QString *outCmd, Pr } else { if (err != ProcessArgs::FoundMeta) return false; -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) *outCmd = qEnvironmentVariable("SHELL", "/bin/sh"); -#else - // for sdktool - *outCmd = qEnvironmentVariableIsSet("SHELL") ? QString::fromLocal8Bit(qgetenv("SHELL")) - : QString("/bin/sh"); -#endif *outArgs = ProcessArgs::createUnixArgs({"-c", quoteArg(executable.toString()) + ' ' + arguments}); } } @@ -1478,6 +1472,12 @@ void CommandLine::addCommandLineAsArgs(const CommandLine &cmd) addArgs(cmd.splitArguments()); } +void CommandLine::addCommandLineAsArgs(const CommandLine &cmd, RawType) +{ + addArg(cmd.executable().path()); + addArgs(cmd.arguments(), Raw); +} + void CommandLine::addArgs(const QString &inArgs, RawType) { ProcessArgs::addArgs(&m_arguments, inArgs); diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h index c5408c45b12..068e724dfd4 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -145,6 +145,7 @@ public: void addArgs(const QStringList &inArgs); void addCommandLineAsArgs(const CommandLine &cmd); + void addCommandLineAsArgs(const CommandLine &cmd, RawType); void addArgs(const QString &inArgs, RawType); diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index 7b0a48c6a8b..48e525d5229 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -310,7 +310,7 @@ FilePaths Environment::path() const FilePaths Environment::pathListValue(const QString &varName) const { const QStringList pathComponents = expandedValueForKey(varName).split( - OsSpecificAspects::pathListSeparator(m_osType), SkipEmptyParts); + OsSpecificAspects::pathListSeparator(m_osType), Qt::SkipEmptyParts); return transform(pathComponents, &FilePath::fromUserInput); } diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 60a2d6e62fa..e0e207a9b22 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -42,6 +42,7 @@ #ifdef QT_GUI_LIB #include <QMessageBox> +#include <QRegularExpression> #endif #ifdef Q_OS_WIN @@ -493,6 +494,43 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent, options); return transform(result, &FilePath::fromString); } + +// Used on 'ls' output on unix-like systems. +void FileUtils::iterateLsOutput(const FilePath &base, + const QStringList &entries, + const FileFilter &filter, + const std::function<bool (const FilePath &)> &callBack) +{ + QTC_CHECK(filter.iteratorFlags != QDirIterator::NoIteratorFlags); // FIXME: Not supported yet below. + + const QList<QRegularExpression> nameRegexps = + transform(filter.nameFilters, [](const QString &filter) { + QRegularExpression re; + re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); + QTC_CHECK(re.isValid()); + return re; + }); + + const auto nameMatches = [&nameRegexps](const QString &fileName) { + for (const QRegularExpression &re : nameRegexps) { + const QRegularExpressionMatch match = re.match(fileName); + if (match.hasMatch()) + return true; + } + return nameRegexps.isEmpty(); + }; + + // FIXME: Handle filters. For now bark on unsupported options. + QTC_CHECK(filter.fileFilters == QDir::NoFilter); + + for (const QString &entry : entries) { + if (!nameMatches(entry)) + continue; + if (!callBack(base.pathAppended(entry))) + break; + } +} + #endif // QT_WIDGETS_LIB } // namespace Utils diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 163e762e511..f76b621c484 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -144,6 +144,11 @@ public: static void setDeviceFileHooks(const DeviceFileHooks &hooks); + static void iterateLsOutput(const FilePath &base, + const QStringList &entries, + const FileFilter &filter, + const std::function<bool(const FilePath &)> &callBack); + #ifdef QT_WIDGETS_LIB static void setDialogParentGetter(const std::function<QWidget *()> &getter); @@ -173,6 +178,7 @@ public: QString *selectedFilter = nullptr, QFileDialog::Options options = {}); #endif + }; template<typename T> diff --git a/src/libs/utils/launcherinterface.cpp b/src/libs/utils/launcherinterface.cpp index a77650745a7..0d64c20674c 100644 --- a/src/libs/utils/launcherinterface.cpp +++ b/src/libs/utils/launcherinterface.cpp @@ -138,9 +138,7 @@ void LauncherInterfacePrivate::doStart() void LauncherInterfacePrivate::doStop() { m_server->close(); - if (!m_process) - return; - m_process->disconnect(); + QTC_ASSERT(m_process, return); m_socket->shutdown(); m_process->waitForFinished(3000); ProcessReaper::reap(m_process); @@ -240,11 +238,10 @@ void LauncherInterface::sendData(const QByteArray &data) instance()->m_private->socket()->sendData(data); } -Utils::Internal::CallerHandle *LauncherInterface::registerHandle(QObject *parent, quintptr token, - ProcessMode mode) +Utils::Internal::CallerHandle *LauncherInterface::registerHandle(QObject *parent, quintptr token) { QMutexLocker locker(&s_instanceMutex); - return instance()->m_private->socket()->registerHandle(parent, token, mode); + return instance()->m_private->socket()->registerHandle(parent, token); } void LauncherInterface::unregisterHandle(quintptr token) diff --git a/src/libs/utils/launcherinterface.h b/src/libs/utils/launcherinterface.h index 541501efeec..b7c04dcf8d7 100644 --- a/src/libs/utils/launcherinterface.h +++ b/src/libs/utils/launcherinterface.h @@ -28,7 +28,6 @@ #include "utils_global.h" #include "processreaper.h" -#include "processutils.h" #include "singleton.h" #include <QThread> @@ -55,8 +54,7 @@ private: static bool isStarted(); static bool isReady(); static void sendData(const QByteArray &data); - static Utils::Internal::CallerHandle *registerHandle(QObject *parent, quintptr token, - ProcessMode mode); + static Utils::Internal::CallerHandle *registerHandle(QObject *parent, quintptr token); static void unregisterHandle(quintptr token); LauncherInterface(); diff --git a/src/libs/utils/launcherpackets.cpp b/src/libs/utils/launcherpackets.cpp index a810fbd7b0e..8b201ecf4c4 100644 --- a/src/libs/utils/launcherpackets.cpp +++ b/src/libs/utils/launcherpackets.cpp @@ -58,9 +58,9 @@ StartProcessPacket::StartProcessPacket(quintptr token) void StartProcessPacket::doSerialize(QDataStream &stream) const { - stream << command << arguments << workingDir << env << int(processMode) << writeData << int(channelMode) - << standardInputFile << belowNormalPriority << nativeArguments << lowPriority - << unixTerminalDisabled; + stream << command << arguments << workingDir << env << int(processMode) << writeData + << int(processChannelMode) << standardInputFile << belowNormalPriority + << nativeArguments << lowPriority << unixTerminalDisabled; } void StartProcessPacket::doDeserialize(QDataStream &stream) @@ -69,7 +69,7 @@ void StartProcessPacket::doDeserialize(QDataStream &stream) stream >> command >> arguments >> workingDir >> env >> pm >> writeData >> cm >> standardInputFile >> belowNormalPriority >> nativeArguments >> lowPriority >> unixTerminalDisabled; - channelMode = QProcess::ProcessChannelMode(cm); + processChannelMode = QProcess::ProcessChannelMode(cm); processMode = Utils::ProcessMode(pm); } diff --git a/src/libs/utils/launcherpackets.h b/src/libs/utils/launcherpackets.h index 372290f1ca5..9d05782a4e7 100644 --- a/src/libs/utils/launcherpackets.h +++ b/src/libs/utils/launcherpackets.h @@ -25,7 +25,7 @@ #pragma once -#include "processutils.h" +#include "processenums.h" #include <QDataStream> #include <QProcess> @@ -113,7 +113,7 @@ public: QStringList env; ProcessMode processMode = ProcessMode::Reader; QByteArray writeData; - QProcess::ProcessChannelMode channelMode = QProcess::SeparateChannels; + QProcess::ProcessChannelMode processChannelMode = QProcess::SeparateChannels; QString standardInputFile; bool belowNormalPriority = false; QString nativeArguments; @@ -217,8 +217,8 @@ public: QString errorString; QByteArray stdOut; QByteArray stdErr; - QProcess::ExitStatus exitStatus = QProcess::ExitStatus::NormalExit; - QProcess::ProcessError error = QProcess::ProcessError::UnknownError; + QProcess::ExitStatus exitStatus = QProcess::NormalExit; + QProcess::ProcessError error = QProcess::UnknownError; int exitCode = 0; private: diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp index c232579e20d..4747813ce5c 100644 --- a/src/libs/utils/launchersocket.cpp +++ b/src/libs/utils/launchersocket.cpp @@ -215,7 +215,7 @@ void CallerHandle::handleError(const ErrorSignal *launcherSignal) QTC_ASSERT(isCalledFromCallersThread(), return); m_processState = QProcess::NotRunning; m_error = launcherSignal->error(); - m_errorString = launcherSignal->errorString(); + m_setup->m_errorString = launcherSignal->errorString(); if (m_error == QProcess::FailedToStart) m_exitCode = 255; // This code is being returned by QProcess when FailedToStart error occurred emit errorOccurred(m_error); @@ -232,17 +232,17 @@ void CallerHandle::handleStarted(const StartedSignal *launcherSignal) void CallerHandle::handleReadyRead(const ReadyReadSignal *launcherSignal) { QTC_ASSERT(isCalledFromCallersThread(), return); - if (m_channelMode == QProcess::ForwardedOutputChannel - || m_channelMode == QProcess::ForwardedChannels) { - std::cout << launcherSignal->stdOut().constData(); + if (m_setup->m_processChannelMode == QProcess::ForwardedOutputChannel + || m_setup->m_processChannelMode == QProcess::ForwardedChannels) { + std::cout << launcherSignal->stdOut().constData() << std::flush; } else { m_stdout += launcherSignal->stdOut(); if (!m_stdout.isEmpty()) emit readyReadStandardOutput(); } - if (m_channelMode == QProcess::ForwardedErrorChannel - || m_channelMode == QProcess::ForwardedChannels) { - std::cerr << launcherSignal->stdErr().constData(); + if (m_setup->m_processChannelMode == QProcess::ForwardedErrorChannel + || m_setup->m_processChannelMode == QProcess::ForwardedChannels) { + std::cerr << launcherSignal->stdErr().constData() << std::flush; } else { m_stderr += launcherSignal->stdErr(); if (!m_stderr.isEmpty()) @@ -256,7 +256,7 @@ void CallerHandle::handleFinished(const FinishedSignal *launcherSignal) m_processState = QProcess::NotRunning; m_exitStatus = launcherSignal->exitStatus(); m_exitCode = launcherSignal->exitCode(); - emit finished(m_exitCode, m_exitStatus); + emit finished(); } // Called from launcher's thread exclusively. @@ -293,8 +293,8 @@ void CallerHandle::cancel() case QProcess::NotRunning: break; case QProcess::Starting: - m_errorString = QCoreApplication::translate("Utils::LauncherHandle", - "Process was canceled before it was started."); + m_setup->m_errorString = QCoreApplication::translate("Utils::LauncherHandle", + "Process was canceled before it was started."); m_error = QProcess::FailedToStart; if (LauncherInterface::isReady()) // TODO: race condition with m_processState??? sendPacket(StopProcessPacket(m_token)); @@ -305,9 +305,6 @@ void CallerHandle::cancel() sendPacket(StopProcessPacket(m_token)); break; } - - if (m_launcherHandle) - m_launcherHandle->setCanceled(); } QByteArray CallerHandle::readAllStandardOutput() @@ -337,16 +334,16 @@ int CallerHandle::exitCode() const QString CallerHandle::errorString() const { QTC_ASSERT(isCalledFromCallersThread(), return {}); - return m_errorString; + return m_setup->m_errorString; } void CallerHandle::setErrorString(const QString &str) { QTC_ASSERT(isCalledFromCallersThread(), return); - m_errorString = str; + m_setup->m_errorString = str; } -void CallerHandle::start(const QString &program, const QStringList &arguments, const QByteArray &writeData) +void CallerHandle::start(const QString &program, const QStringList &arguments) { QTC_ASSERT(isCalledFromCallersThread(), return); if (!m_launcherHandle || m_launcherHandle->isSocketError()) { @@ -370,21 +367,20 @@ void CallerHandle::start(const QString &program, const QStringList &arguments, c QMutexLocker locker(&m_mutex); m_command = program; m_arguments = arguments; - m_writeData = writeData; m_processState = QProcess::Starting; StartProcessPacket *p = new StartProcessPacket(m_token); p->command = m_command; p->arguments = m_arguments; - p->env = m_environment.toStringList(); - p->workingDir = m_workingDirectory; - p->processMode = m_processMode; - p->writeData = m_writeData; - p->channelMode = m_channelMode; - p->standardInputFile = m_standardInputFile; - p->belowNormalPriority = m_belowNormalPriority; - p->nativeArguments = m_nativeArguments; - p->lowPriority = m_lowPriority; - p->unixTerminalDisabled = m_unixTerminalDisabled; + p->env = m_setup->m_environment.toStringList(); + p->workingDir = m_setup->m_workingDirectory.path(); + p->processMode = m_setup->m_processMode; + p->writeData = m_setup->m_writeData; + p->processChannelMode = m_setup->m_processChannelMode; + p->standardInputFile = m_setup->m_standardInputFile; + p->belowNormalPriority = m_setup->m_belowNormalPriority; + p->nativeArguments = m_setup->m_nativeArguments; + p->lowPriority = m_setup->m_lowPriority; + p->unixTerminalDisabled = m_setup->m_unixTerminalDisabled; m_startPacket.reset(p); if (LauncherInterface::isReady()) doStart(); @@ -444,28 +440,10 @@ QStringList CallerHandle::arguments() const return m_arguments; } -void CallerHandle::setStandardInputFile(const QString &fileName) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_standardInputFile = fileName; -} - -void CallerHandle::setProcessChannelMode(QProcess::ProcessChannelMode mode) +void CallerHandle::setProcessSetupData(const ProcessSetupData::Ptr &setup) { QTC_ASSERT(isCalledFromCallersThread(), return); - m_channelMode = mode; -} - -void CallerHandle::setProcessEnvironment(const QProcessEnvironment &environment) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_environment = environment; -} - -void CallerHandle::setWorkingDirectory(const QString &dir) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_workingDirectory = dir; + m_setup = setup; } QProcess::ExitStatus CallerHandle::exitStatus() const @@ -474,30 +452,6 @@ QProcess::ExitStatus CallerHandle::exitStatus() const return m_exitStatus; } -void CallerHandle::setBelowNormalPriority() -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_belowNormalPriority = true; -} - -void CallerHandle::setNativeArguments(const QString &arguments) -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_nativeArguments = arguments; -} - -void CallerHandle::setLowPriority() -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_lowPriority = true; -} - -void CallerHandle::setUnixTerminalDisabled() -{ - QTC_ASSERT(isCalledFromCallersThread(), return); - m_unixTerminalDisabled = true; -} - bool CallerHandle::waitForSignal(int msecs, CallerHandle::SignalType newSignal) { QTC_ASSERT(isCalledFromCallersThread(), return false); @@ -547,21 +501,16 @@ bool LauncherHandle::waitForSignal(int msecs, CallerHandle::SignalType newSignal break; if (!doWaitForSignal(deadline, newSignal)) break; - m_awaitingShouldContinue = true; // TODO: make it recursive? const QList<CallerHandle::SignalType> flushedSignals = m_callerHandle->flushFor(newSignal); - const bool wasCanceled = !m_awaitingShouldContinue; - m_awaitingShouldContinue = false; const bool errorOccurred = flushedSignals.contains(CallerHandle::SignalType::Error); if (errorOccurred) - return false; // apparently QProcess behaves like this in case of error + return true; // apparently QProcess behaves like this in case of error const bool newSignalFlushed = flushedSignals.contains(newSignal); if (newSignalFlushed) // so we don't continue waiting return true; - if (wasCanceled) - return true; // or false? is false only in case of timeout? const bool finishedSignalFlushed = flushedSignals.contains(CallerHandle::SignalType::Finished); if (finishedSignalFlushed) - return false; // finish has appeared but we were waiting for other signal + return true; // finish has appeared but we were waiting for other signal } return false; } @@ -806,15 +755,15 @@ void LauncherSocket::sendData(const QByteArray &data) QMetaObject::invokeMethod(this, &LauncherSocket::handleRequests); } -CallerHandle *LauncherSocket::registerHandle(QObject *parent, quintptr token, ProcessMode mode) +CallerHandle *LauncherSocket::registerHandle(QObject *parent, quintptr token) { QTC_ASSERT(!isCalledFromLaunchersThread(), return nullptr); QMutexLocker locker(&m_mutex); if (m_handles.contains(token)) return nullptr; // TODO: issue a warning - CallerHandle *callerHandle = new CallerHandle(parent, token, mode); - LauncherHandle *launcherHandle = new LauncherHandle(token, mode); + CallerHandle *callerHandle = new CallerHandle(parent, token); + LauncherHandle *launcherHandle = new LauncherHandle(token); callerHandle->setLauncherHandle(launcherHandle); launcherHandle->setCallerHandle(callerHandle); launcherHandle->moveToThread(thread()); @@ -860,11 +809,7 @@ void LauncherSocket::setSocket(QLocalSocket *socket) m_socket.store(socket); m_packetParser.setDevice(m_socket); connect(m_socket, -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), -#else &QLocalSocket::errorOccurred, -#endif this, &LauncherSocket::handleSocketError); connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocket::handleSocketDataAvailable); diff --git a/src/libs/utils/launchersocket.h b/src/libs/utils/launchersocket.h index 42b5fa757e8..113b4d5a319 100644 --- a/src/libs/utils/launchersocket.h +++ b/src/libs/utils/launchersocket.h @@ -25,8 +25,10 @@ #pragma once +#include "environment.h" +#include "filepath.h" #include "launcherpackets.h" -#include "processutils.h" +#include "processinterface.h" #include <QDeadlineTimer> #include <QHash> @@ -68,8 +70,8 @@ public: Finished }; Q_ENUM(SignalType) - CallerHandle(QObject *parent, quintptr token, ProcessMode mode) - : QObject(parent), m_token(token), m_processMode(mode) {} + CallerHandle(QObject *parent, quintptr token) + : QObject(parent), m_token(token) {} ~CallerHandle() override; LauncherHandle *launcherHandle() const { return m_launcherHandle; } @@ -98,7 +100,7 @@ public: QString errorString() const; void setErrorString(const QString &str); - void start(const QString &program, const QStringList &arguments, const QByteArray &writeData); + void start(const QString &program, const QStringList &arguments); // Called from caller's or launcher's thread. void startIfNeeded(); @@ -109,21 +111,13 @@ public: QString program() const; // Called from caller's or launcher's thread. QStringList arguments() const; - void setStandardInputFile(const QString &fileName); - void setProcessChannelMode(QProcess::ProcessChannelMode mode); - void setProcessEnvironment(const QProcessEnvironment &environment); - void setWorkingDirectory(const QString &dir); + void setProcessSetupData(const ProcessSetupData::Ptr &setup); QProcess::ExitStatus exitStatus() const; - void setBelowNormalPriority(); - void setNativeArguments(const QString &arguments); - void setLowPriority(); - void setUnixTerminalDisabled(); - signals: void errorOccurred(QProcess::ProcessError error); void started(); - void finished(int exitCode, QProcess::ExitStatus status); + void finished(); void readyReadStandardOutput(); void readyReadStandardError(); @@ -160,7 +154,6 @@ private: QList<LauncherSignal *> m_signals; const quintptr m_token; - const ProcessMode m_processMode; // Modified from caller's thread, read from launcher's thread std::atomic<QProcess::ProcessState> m_processState = QProcess::NotRunning; @@ -170,21 +163,11 @@ private: QProcess::ExitStatus m_exitStatus = QProcess::ExitStatus::NormalExit; QByteArray m_stdout; QByteArray m_stderr; - QString m_errorString; QProcess::ProcessError m_error = QProcess::UnknownError; QString m_command; QStringList m_arguments; - QProcessEnvironment m_environment; - QString m_workingDirectory; - QByteArray m_writeData; - QProcess::ProcessChannelMode m_channelMode = QProcess::SeparateChannels; - QString m_standardInputFile; - - bool m_belowNormalPriority = false; - QString m_nativeArguments; - bool m_lowPriority = false; - bool m_unixTerminalDisabled = false; + ProcessSetupData::Ptr m_setup; }; // Moved to the launcher thread, returned to caller's thread. @@ -196,13 +179,11 @@ class LauncherHandle : public QObject Q_OBJECT public: // Called from caller's thread, moved to launcher's thread afterwards. - LauncherHandle(quintptr token, ProcessMode) : m_token(token) {} + LauncherHandle(quintptr token) : m_token(token) {} // Called from caller's thread exclusively. bool waitForSignal(int msecs, CallerHandle::SignalType newSignal); CallerHandle *callerHandle() const { return m_callerHandle; } void setCallerHandle(CallerHandle *handle) { QMutexLocker locker(&m_mutex); m_callerHandle = handle; } - // Called from caller's thread exclusively. - void setCanceled() { m_awaitingShouldContinue = false; } // Called from launcher's thread exclusively. void handleSocketReady(); @@ -234,8 +215,6 @@ private: // Lives in caller's thread. Modified only in caller's thread. TODO: check usages - all should be with mutex CallerHandle *m_callerHandle = nullptr; - // Modified only in caller's thread. - bool m_awaitingShouldContinue = false; mutable QMutex m_mutex; QWaitCondition m_waitCondition; const quintptr m_token; @@ -254,7 +233,7 @@ public: void sendData(const QByteArray &data); // Called from caller's thread exclusively. - CallerHandle *registerHandle(QObject *parent, quintptr token, ProcessMode mode); + CallerHandle *registerHandle(QObject *parent, quintptr token); void unregisterHandle(quintptr token); signals: diff --git a/src/libs/utils/mimetypes/mimedatabase.cpp b/src/libs/utils/mimetypes/mimedatabase.cpp index 35e0257e5e7..7bf0a4c8fc3 100644 --- a/src/libs/utils/mimetypes/mimedatabase.cpp +++ b/src/libs/utils/mimetypes/mimedatabase.cpp @@ -61,12 +61,6 @@ #include <algorithm> #include <functional> -#ifdef Q_OS_WIN -static struct {const char *source; const char *comment; } ALL_FILES_FILTER = QT_TRANSLATE_NOOP3("Core", "All Files (*.*)", "On Windows"); -#else -static struct {const char *source; const char *comment; } ALL_FILES_FILTER = QT_TRANSLATE_NOOP3("Core", "All Files (*)", "On Linux/macOS"); -#endif - using namespace Utils; using namespace Utils::Internal; @@ -334,66 +328,6 @@ MimeDatabase::~MimeDatabase() d = nullptr; } -void Utils::addMimeTypes(const QString &fileName, const QByteArray &data) -{ - auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); - - if (d->m_startupPhase >= MimeDatabase::PluginsDelayedInitializing) - qWarning("Adding items from %s to MimeDatabase after initialization time", - qPrintable(fileName)); - - auto xmlProvider = static_cast<MimeXMLProvider *>(d->provider()); - xmlProvider->addData(fileName, data); -} - -QString Utils::allFiltersString(QString *allFilesFilter) -{ - MimeDatabase mdb; - QSet<QString> uniqueFilters; - const QList<MimeType> allMimeTypes = mdb.allMimeTypes(); - for (const MimeType &mt : allMimeTypes) { - const QString &filterString = mt.filterString(); - if (!filterString.isEmpty()) - uniqueFilters.insert(mt.filterString()); - } - QStringList filters; - for (const QString &filter : uniqueFilters) - filters.append(filter); - filters.sort(); - const QString allFiles = allFilesFilterString(); - if (allFilesFilter) - *allFilesFilter = allFiles; - - // Prepend all files filter - filters.prepend(allFiles); - - return filters.join(QLatin1String(";;")); -} - -QString Utils::allFilesFilterString() -{ - auto d = MimeDatabasePrivate::instance(); - if (d->m_startupPhase <= MimeDatabase::PluginsInitializing) - qWarning("Accessing MimeDatabase files filter strings before plugins are initialized"); - - return QCoreApplication::translate("Core", ALL_FILES_FILTER.source, ALL_FILES_FILTER.comment); -} - -QStringList Utils::allGlobPatterns() -{ - auto d = MimeDatabasePrivate::instance(); - if (d->m_startupPhase <= MimeDatabase::PluginsInitializing) - qWarning("Accessing MimeDatabase glob patterns before plugins are initialized"); - - MimeDatabase mdb; - QStringList patterns; - const QList<MimeType> allMimeTypes = mdb.allMimeTypes(); - for (const MimeType &mt : allMimeTypes) - patterns.append(mt.globPatterns()); - return patterns; -} - /*! \fn MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const; Returns a MIME type for \a nameOrAlias or an invalid one if none found. @@ -686,77 +620,3 @@ QList<MimeType> MimeDatabase::allMimeTypes() const \value MatchContent The file content is used to look for a match */ - -QMap<int, QList<MimeMagicRule> > Utils::magicRulesForMimeType(const MimeType &mimeType) -{ - auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); - return d->provider()->magicRulesForMimeType(mimeType); -} - -void Utils::setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) -{ - auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); - d->provider()->setGlobPatternsForMimeType(mimeType, patterns); -} - -void Utils::setMagicRulesForMimeType(const MimeType &mimeType, const QMap<int, QList<MimeMagicRule> > &rules) -{ - auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); - d->provider()->setMagicRulesForMimeType(mimeType, rules); -} - -void Utils::setMimeStartupPhase(MimeStartupPhase phase) -{ - auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); - if (int(phase) != d->m_startupPhase + 1) - qWarning("Unexpected jump in MimedDatabase lifetime from %d to %d", d->m_startupPhase, int(phase)); - d->m_startupPhase = int(phase); -} - -MimeType Utils::mimeTypeForName(const QString &nameOrAlias) -{ - MimeDatabase mdb; - return mdb.mimeTypeForName(nameOrAlias); -} - -MimeType Utils::mimeTypeForFile(const QString &fileName, MimeMatchMode mode) -{ - MimeDatabase mdb; - return mdb.mimeTypeForFile(fileName, MimeDatabase::MatchMode(mode)); -} - -MimeType Utils::mimeTypeForFile(const QFileInfo &fileInfo, MimeMatchMode mode) -{ - MimeDatabase mdb; - return mdb.mimeTypeForFile(fileInfo, MimeDatabase::MatchMode(mode)); -} - -MimeType Utils::mimeTypeForFile(const FilePath &filePath, MimeMatchMode mode) -{ - MimeDatabase mdb; - if (filePath.needsDevice()) - return mdb.mimeTypeForUrl(filePath.toUrl()); - return mdb.mimeTypeForFile(filePath.toString(), MimeDatabase::MatchMode(mode)); -} - -QList<MimeType> Utils::mimeTypesForFileName(const QString &fileName) -{ - MimeDatabase mdb; - return mdb.mimeTypesForFileName(fileName); -} - -MimeType Utils::mimeTypeForData(const QByteArray &data) -{ - MimeDatabase mdb; - return mdb.mimeTypeForData(data); -} - -QList<MimeType> Utils::allMimeTypes() -{ - MimeDatabase mdb; - return mdb.allMimeTypes(); -} diff --git a/src/libs/utils/mimetypes/mimedatabase.h b/src/libs/utils/mimetypes/mimedatabase.h index 9e217245421..4808d96a478 100644 --- a/src/libs/utils/mimetypes/mimedatabase.h +++ b/src/libs/utils/mimetypes/mimedatabase.h @@ -40,52 +40,54 @@ #pragma once #include "mimetype.h" -#include "mimemagicrule_p.h" - -#include <utils/utils_global.h> QT_BEGIN_NAMESPACE class QFileInfo; +class QIODevice; +class QUrl; QT_END_NAMESPACE namespace Utils { -class FilePath; +class MimeDatabase +{ + Q_DISABLE_COPY(MimeDatabase) -// Wrapped QMimeDataBase functions -QTCREATOR_UTILS_EXPORT MimeType mimeTypeForName(const QString &nameOrAlias); +public: + MimeDatabase(); + ~MimeDatabase(); -enum class MimeMatchMode { - MatchDefault = 0x0, - MatchExtension = 0x1, - MatchContent = 0x2 -}; + MimeType mimeTypeForName(const QString &nameOrAlias) const; -QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const QString &fileName, MimeMatchMode mode = MimeMatchMode::MatchDefault); -QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const QFileInfo &fileInfo, MimeMatchMode mode = MimeMatchMode::MatchDefault); -QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const FilePath &filePath, MimeMatchMode mode = MimeMatchMode::MatchDefault); -QTCREATOR_UTILS_EXPORT QList<MimeType> mimeTypesForFileName(const QString &fileName); -QTCREATOR_UTILS_EXPORT MimeType mimeTypeForData(const QByteArray &data); -QTCREATOR_UTILS_EXPORT QList<MimeType> allMimeTypes(); - -// Qt Creator additions -// For debugging purposes. -enum class MimeStartupPhase { - BeforeInitialize, - PluginsLoading, - PluginsInitializing, // Register up to here. - PluginsDelayedInitializing, // Use from here on. - UpAndRunning -}; + enum MatchMode { MatchDefault = 0x0, MatchExtension = 0x1, MatchContent = 0x2 }; + + MimeType mimeTypeForFile(const QString &fileName, MatchMode mode = MatchDefault) const; + MimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode = MatchDefault) const; + QList<MimeType> mimeTypesForFileName(const QString &fileName) const; + + MimeType mimeTypeForData(const QByteArray &data) const; + MimeType mimeTypeForData(QIODevice *device) const; -QTCREATOR_UTILS_EXPORT void setMimeStartupPhase(MimeStartupPhase); -QTCREATOR_UTILS_EXPORT void addMimeTypes(const QString &id, const QByteArray &data); -QTCREATOR_UTILS_EXPORT QString allFiltersString(QString *allFilesFilter = nullptr); -QTCREATOR_UTILS_EXPORT QString allFilesFilterString(); -QTCREATOR_UTILS_EXPORT QStringList allGlobPatterns(); -QTCREATOR_UTILS_EXPORT QMap<int, QList<Internal::MimeMagicRule> > magicRulesForMimeType(const MimeType &mimeType); // priority -> rules -QTCREATOR_UTILS_EXPORT void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns); -QTCREATOR_UTILS_EXPORT void setMagicRulesForMimeType(const MimeType &mimeType, - const QMap<int, QList<Internal::MimeMagicRule> > &rules); // priority -> rules + MimeType mimeTypeForUrl(const QUrl &url) const; + MimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const; + MimeType mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const; + + QString suffixForFileName(const QString &fileName) const; + + QList<MimeType> allMimeTypes() const; + + // For debugging purposes. + enum StartupPhase { + BeforeInitialize, + PluginsLoading, + PluginsInitializing, // Register up to here. + PluginsDelayedInitializing, // Use from here on. + UpAndRunning + }; + static void setStartupPhase(StartupPhase); + +private: + Internal::MimeDatabasePrivate *d; +}; } // Utils diff --git a/src/libs/utils/mimetypes/mimedatabase_p.h b/src/libs/utils/mimetypes/mimedatabase_p.h index 6f87b213f34..46211126005 100644 --- a/src/libs/utils/mimetypes/mimedatabase_p.h +++ b/src/libs/utils/mimetypes/mimedatabase_p.h @@ -62,9 +62,7 @@ #include <QtCore/qhash.h> #include <QtCore/qmutex.h> QT_BEGIN_NAMESPACE -class QFileInfo; class QIODevice; -class QUrl; QT_END_NAMESPACE #include "mimetype.h" @@ -108,50 +106,5 @@ public: int m_startupPhase = 0; }; -class MimeDatabase -{ - Q_DISABLE_COPY(MimeDatabase) - -public: - MimeDatabase(); - ~MimeDatabase(); - - MimeType mimeTypeForName(const QString &nameOrAlias) const; - - enum MatchMode { - MatchDefault = 0x0, - MatchExtension = 0x1, - MatchContent = 0x2 - }; - - MimeType mimeTypeForFile(const QString &fileName, MatchMode mode = MatchDefault) const; - MimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode = MatchDefault) const; - QList<MimeType> mimeTypesForFileName(const QString &fileName) const; - - MimeType mimeTypeForData(const QByteArray &data) const; - MimeType mimeTypeForData(QIODevice *device) const; - - MimeType mimeTypeForUrl(const QUrl &url) const; - MimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const; - MimeType mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const; - - QString suffixForFileName(const QString &fileName) const; - - QList<MimeType> allMimeTypes() const; - - // For debugging purposes. - enum StartupPhase { - BeforeInitialize, - PluginsLoading, - PluginsInitializing, // Register up to here. - PluginsDelayedInitializing, // Use from here on. - UpAndRunning - }; - static void setStartupPhase(StartupPhase); - -private: - Internal::MimeDatabasePrivate *d; -}; - } // Internal } // Utils diff --git a/src/libs/utils/mimetypes/mimemagicrule_p.h b/src/libs/utils/mimetypes/mimemagicrule_p.h index 1513750a298..14fdbf9b488 100644 --- a/src/libs/utils/mimetypes/mimemagicrule_p.h +++ b/src/libs/utils/mimetypes/mimemagicrule_p.h @@ -62,8 +62,8 @@ namespace Utils { class MimeType; namespace Internal { - class MimeMagicRulePrivate; +} class QTCREATOR_UTILS_EXPORT MimeMagicRule { @@ -97,12 +97,11 @@ public: static bool matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, int valueLength, const char *valueData, const char *mask); private: - const QScopedPointer<MimeMagicRulePrivate> d; + const QScopedPointer<Internal::MimeMagicRulePrivate> d; }; -} // Internal } // Utils QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(Utils::Internal::MimeMagicRule, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(Utils::MimeMagicRule, Q_MOVABLE_TYPE); QT_END_NAMESPACE diff --git a/src/libs/utils/mimetypes/mimeutils.cpp b/src/libs/utils/mimetypes/mimeutils.cpp new file mode 100644 index 00000000000..cd618daeea0 --- /dev/null +++ b/src/libs/utils/mimetypes/mimeutils.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimeutils.h" + +#include "mimedatabase.h" +#include "mimedatabase_p.h" +#include "mimemagicrule_p.h" +#include "mimeprovider_p.h" + +#include "filepath.h" + +using namespace Utils; +using namespace Utils::Internal; + +void Utils::addMimeTypes(const QString &fileName, const QByteArray &data) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase >= MimeDatabase::PluginsDelayedInitializing) + qWarning("Adding items from %s to MimeDatabase after initialization time", + qPrintable(fileName)); + + auto xmlProvider = static_cast<MimeXMLProvider *>(d->provider()); + xmlProvider->addData(fileName, data); +} + +QMap<int, QList<MimeMagicRule>> Utils::magicRulesForMimeType(const MimeType &mimeType) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + return d->provider()->magicRulesForMimeType(mimeType); +} + +void Utils::setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + d->provider()->setGlobPatternsForMimeType(mimeType, patterns); +} + +void Utils::setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + d->provider()->setMagicRulesForMimeType(mimeType, rules); +} + +void Utils::setMimeStartupPhase(MimeStartupPhase phase) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + if (int(phase) != d->m_startupPhase + 1) + qWarning("Unexpected jump in MimedDatabase lifetime from %d to %d", + d->m_startupPhase, + int(phase)); + d->m_startupPhase = int(phase); +} + +MimeType Utils::mimeTypeForName(const QString &nameOrAlias) +{ + MimeDatabase mdb; + return mdb.mimeTypeForName(nameOrAlias); +} + +MimeType Utils::mimeTypeForFile(const QString &fileName, MimeMatchMode mode) +{ + MimeDatabase mdb; + return mdb.mimeTypeForFile(fileName, MimeDatabase::MatchMode(mode)); +} + +MimeType Utils::mimeTypeForFile(const QFileInfo &fileInfo, MimeMatchMode mode) +{ + MimeDatabase mdb; + return mdb.mimeTypeForFile(fileInfo, MimeDatabase::MatchMode(mode)); +} + +MimeType Utils::mimeTypeForFile(const FilePath &filePath, MimeMatchMode mode) +{ + MimeDatabase mdb; + if (filePath.needsDevice()) + return mdb.mimeTypeForUrl(filePath.toUrl()); + return mdb.mimeTypeForFile(filePath.toString(), MimeDatabase::MatchMode(mode)); +} + +QList<MimeType> Utils::mimeTypesForFileName(const QString &fileName) +{ + MimeDatabase mdb; + return mdb.mimeTypesForFileName(fileName); +} + +MimeType Utils::mimeTypeForData(const QByteArray &data) +{ + MimeDatabase mdb; + return mdb.mimeTypeForData(data); +} + +QList<MimeType> Utils::allMimeTypes() +{ + MimeDatabase mdb; + return mdb.allMimeTypes(); +} diff --git a/src/libs/utils/mimetypes2/mimedatabase.cpp b/src/libs/utils/mimetypes2/mimedatabase.cpp new file mode 100644 index 00000000000..97e8bb8c287 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimedatabase.cpp @@ -0,0 +1,933 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected], author David Faure <[email protected]> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qplatformdefs.h> // always first + +#include "mimedatabase.h" +#include "mimedatabase_p.h" + +#include "mimeprovider_p.h" +#include "mimetype_p.h" +#include "mimeutils.h" + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QStandardPaths> +#include <QtCore/QBuffer> +#include <QtCore/QUrl> +#include <QtCore/QDebug> + +#include <algorithm> +#include <functional> +#include <stack> + +namespace Utils { + +Q_GLOBAL_STATIC(MimeDatabasePrivate, staticMimeDatabase) + +MimeDatabasePrivate *MimeDatabasePrivate::instance() +{ + return staticMimeDatabase(); +} + +MimeDatabasePrivate::MimeDatabasePrivate() + : m_defaultMimeType(QLatin1String("application/octet-stream")) +{ +} + +MimeDatabasePrivate::~MimeDatabasePrivate() +{ +} + +#if 0 //def QT_BUILD_INTERNAL +Q_CORE_EXPORT +#else +static const +#endif +int mime_secondsBetweenChecks = 5; + +bool MimeDatabasePrivate::shouldCheck() +{ +#if 0 + if (m_lastCheck.isValid() && m_lastCheck.elapsed() < mime_secondsBetweenChecks * 1000) + return false; + m_lastCheck.start(); + return true; +#endif + // Qt Creator forces reload manually + return m_forceLoad; +} + +#if defined(Q_OS_UNIX) && !defined(Q_OS_NACL) && !defined(Q_OS_INTEGRITY) +# define QT_USE_MMAP +#endif + +void MimeDatabasePrivate::loadProviders() +{ +#if 0 + // We use QStandardPaths every time to check if new files appeared + const QStringList mimeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory); +#else + // Qt Creator never uses the standard paths, they can conflict with our setup + const QStringList mimeDirs; +#endif + const auto fdoIterator = std::find_if(mimeDirs.constBegin(), mimeDirs.constEnd(), [](const QString &mimeDir) -> bool { + return QFileInfo::exists(mimeDir + QStringLiteral("/packages/freedesktop.org.xml")); } + ); + const bool needInternalDB = MimeXMLProvider::InternalDatabaseAvailable && fdoIterator == mimeDirs.constEnd(); + //qDebug() << "mime dirs:" << mimeDirs; + + Providers currentProviders; + std::swap(m_providers, currentProviders); + + m_providers.reserve(m_additionalData.size() + mimeDirs.size() + (needInternalDB ? 1 : 0)); + + // added for Qt Creator: additional mime data + for (auto dataIt = m_additionalData.cbegin(); dataIt != m_additionalData.cend(); ++dataIt) { + // Check if we already have a provider for this data + const QString id = dataIt.key(); + const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), + [id](const std::unique_ptr<MimeProviderBase> &prov) { + return prov && prov->directory() == id; + }); + std::unique_ptr<MimeProviderBase> provider; + if (it != currentProviders.end()) + provider = std::move(*it); // take provider out of the vector + provider.reset(new MimeXMLProvider(this, id, dataIt.value())); + m_providers.push_back(std::move(provider)); + } + + + for (const QString &mimeDir : mimeDirs) { + const QString cacheFile = mimeDir + QStringLiteral("/mime.cache"); + // Check if we already have a provider for this dir + const auto predicate = [mimeDir](const std::unique_ptr<MimeProviderBase> &prov) + { + return prov && prov->directory() == mimeDir; + }; + const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), predicate); + if (it == currentProviders.end()) { + std::unique_ptr<MimeProviderBase> provider; +#if defined(QT_USE_MMAP) + if (qEnvironmentVariableIsEmpty("QT_NO_MIME_CACHE") && QFileInfo::exists(cacheFile)) { + provider.reset(new MimeBinaryProvider(this, mimeDir)); + //qDebug() << "Created binary provider for" << mimeDir; + if (!provider->isValid()) { + provider.reset(); + } + } +#endif + if (!provider) { + provider.reset(new MimeXMLProvider(this, mimeDir)); + //qDebug() << "Created XML provider for" << mimeDir; + } + m_providers.push_back(std::move(provider)); + } else { + auto provider = std::move(*it); // take provider out of the vector + provider->ensureLoaded(); + if (!provider->isValid()) { + provider.reset(new MimeXMLProvider(this, mimeDir)); + //qDebug() << "Created XML provider to replace binary provider for" << mimeDir; + } + m_providers.push_back(std::move(provider)); + } + } + // mimeDirs is sorted "most local first, most global last" + // so the internal XML DB goes at the end + if (needInternalDB) { + // Check if we already have a provider for the InternalDatabase + const auto isInternal = [](const std::unique_ptr<MimeProviderBase> &prov) + { + return prov && prov->isInternalDatabase(); + }; + const auto it = std::find_if(currentProviders.begin(), currentProviders.end(), isInternal); + if (it == currentProviders.end()) { + m_providers.push_back(Providers::value_type(new MimeXMLProvider(this, MimeXMLProvider::InternalDatabase))); + } else { + m_providers.push_back(std::move(*it)); + } + } +} + +const MimeDatabasePrivate::Providers &MimeDatabasePrivate::providers() +{ +#ifndef Q_OS_WASM // stub implementation always returns true + Q_ASSERT(!mutex.tryLock()); // caller should have locked mutex +#endif + if (m_providers.empty()) { + loadProviders(); + // Qt Creator forces reload manually + // m_lastCheck.start(); + } else { + if (shouldCheck()) + loadProviders(); + } + // Qt Creator forces reload manually + m_forceLoad = false; + return m_providers; +} + +QString MimeDatabasePrivate::resolveAlias(const QString &nameOrAlias) +{ + for (const auto &provider : providers()) { + const QString ret = provider->resolveAlias(nameOrAlias); + if (!ret.isEmpty()) + return ret; + } + return nameOrAlias; +} + +/*! + \internal + Returns a MIME type or an invalid one if none found + */ +MimeType MimeDatabasePrivate::mimeTypeForName(const QString &nameOrAlias) +{ + const QString mimeName = resolveAlias(nameOrAlias); + for (const auto &provider : providers()) { + const MimeType mime = provider->mimeTypeForName(mimeName); + if (mime.isValid()) + return mime; + } + return {}; +} + +QStringList MimeDatabasePrivate::mimeTypeForFileName(const QString &fileName) +{ + if (fileName.endsWith(QLatin1Char('/'))) + return QStringList() << QLatin1String("inode/directory"); + + const MimeGlobMatchResult result = findByFileName(fileName); + QStringList matchingMimeTypes = result.m_matchingMimeTypes; + matchingMimeTypes.sort(); // make it deterministic + return matchingMimeTypes; +} + +MimeGlobMatchResult MimeDatabasePrivate::findByFileName(const QString &fileName) +{ + MimeGlobMatchResult result; + const QString fileNameExcludingPath = QFileInfo(fileName).fileName(); + QList<QString> checkedMimeTypes; + for (const auto &provider : providers()) + provider->addFileNameMatches(fileNameExcludingPath, result, checkedMimeTypes); + return result; +} + +void MimeDatabasePrivate::loadMimeTypePrivate(MimeTypePrivate &mimePrivate) +{ + QMutexLocker locker(&mutex); + if (mimePrivate.name.isEmpty()) + return; // invalid mimetype + if (!mimePrivate.loaded) { // XML provider sets loaded=true, binary provider does this on demand + Q_ASSERT(mimePrivate.fromCache); + bool found = false; + for (const auto &provider : providers()) { + if (provider->loadMimeTypePrivate(mimePrivate)) { + found = true; + break; + } + } + if (!found) { + const QString file = mimePrivate.name + QLatin1String(".xml"); + qWarning() << "No file found for" << file << ", even though update-mime-info said it would exist.\n" + "Either it was just removed, or the directory doesn't have executable permission..." + << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QLatin1String("mime"), QStandardPaths::LocateDirectory); + } + mimePrivate.loaded = true; + } +} + +void MimeDatabasePrivate::loadGenericIcon(MimeTypePrivate &mimePrivate) +{ + QMutexLocker locker(&mutex); + if (mimePrivate.fromCache) { + mimePrivate.genericIconName.clear(); + for (const auto &provider : providers()) { + provider->loadGenericIcon(mimePrivate); + if (!mimePrivate.genericIconName.isEmpty()) + break; + } + } +} + +void MimeDatabasePrivate::loadIcon(MimeTypePrivate &mimePrivate) +{ + QMutexLocker locker(&mutex); + if (mimePrivate.fromCache) { + mimePrivate.iconName.clear(); + for (const auto &provider : providers()) { + provider->loadIcon(mimePrivate); + if (!mimePrivate.iconName.isEmpty()) + break; + } + } +} + +static QString fallbackParent(const QString &mimeTypeName) +{ + const QStringView myGroup = QStringView{mimeTypeName}.left(mimeTypeName.indexOf(QLatin1Char('/'))); + // All text/* types are subclasses of text/plain. + if (myGroup == QLatin1String("text") && mimeTypeName != QLatin1String("text/plain")) + return QLatin1String("text/plain"); + // All real-file mimetypes implicitly derive from application/octet-stream + if (myGroup != QLatin1String("inode") && + // ignore non-file extensions + myGroup != QLatin1String("all") && myGroup != QLatin1String("fonts") && myGroup != QLatin1String("print") && myGroup != QLatin1String("uri") + && mimeTypeName != QLatin1String("application/octet-stream")) { + return QLatin1String("application/octet-stream"); + } + return QString(); +} + +QStringList MimeDatabasePrivate::mimeParents(const QString &mimeName) +{ + QMutexLocker locker(&mutex); + return parents(mimeName); +} + +QStringList MimeDatabasePrivate::parents(const QString &mimeName) +{ + Q_ASSERT(!mutex.tryLock()); + QStringList result; + for (const auto &provider : providers()) { + if (provider->hasMimeTypeForName(mimeName)) { + provider->addParents(mimeName, result); + break; + } + } + const QString parent = fallbackParent(mimeName); + if (!parent.isEmpty()) + result.append(parent); + return result; +} + +QStringList MimeDatabasePrivate::listAliases(const QString &mimeName) +{ + QMutexLocker locker(&mutex); + QStringList result; + for (const auto &provider : providers()) { + if (provider->hasMimeTypeForName(mimeName)) { + provider->addAliases(mimeName, result); + return result; + } + } + return result; +} + +bool MimeDatabasePrivate::mimeInherits(const QString &mime, const QString &parent) +{ + QMutexLocker locker(&mutex); + return inherits(mime, parent); +} + +static inline bool isTextFile(const QByteArray &data) +{ + // UTF16 byte order marks + static const char bigEndianBOM[] = "\xFE\xFF"; + static const char littleEndianBOM[] = "\xFF\xFE"; + if (data.startsWith(bigEndianBOM) || data.startsWith(littleEndianBOM)) + return true; + + // Check the first 128 bytes (see shared-mime spec) + const char *p = data.constData(); + const char *e = p + qMin(128, data.size()); + for ( ; p < e; ++p) { + if (static_cast<unsigned char>(*p) < 32 && *p != 9 && *p !=10 && *p != 13) + return false; + } + + return true; +} + +MimeType MimeDatabasePrivate::findByData(const QByteArray &data, int *accuracyPtr) +{ +#if 0 + if (data.isEmpty()) { + *accuracyPtr = 100; + return mimeTypeForName(QLatin1String("application/x-zerosize")); + } +#endif + + *accuracyPtr = 0; + MimeType candidate; + QList<QString> checkedMimeTypes; + for (const auto &provider : providers()) + provider->findByMagic(data, accuracyPtr, candidate, checkedMimeTypes); + + if (candidate.isValid()) + return candidate; + + if (isTextFile(data)) { + *accuracyPtr = 5; + return mimeTypeForName(QLatin1String("text/plain")); + } + + return mimeTypeForName(defaultMimeType()); +} + +MimeType MimeDatabasePrivate::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *accuracyPtr) +{ + // First, glob patterns are evaluated. If there is a match with max weight, + // this one is selected and we are done. Otherwise, the file contents are + // evaluated and the match with the highest value (either a magic priority or + // a glob pattern weight) is selected. Matching starts from max level (most + // specific) in both cases, even when there is already a suffix matching candidate. + *accuracyPtr = 0; + + // Pass 1) Try to match on the file name + MimeGlobMatchResult candidatesByName = findByFileName(fileName); + if (candidatesByName.m_allMatchingMimeTypes.count() == 1) { + *accuracyPtr = 100; + const MimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0)); + if (mime.isValid()) + return mime; + candidatesByName = {}; + } + + // Extension is unknown, or matches multiple mimetypes. + // Pass 2) Match on content, if we can read the data + const auto matchOnContent = [this, accuracyPtr, &candidatesByName](QIODevice *device) { + if (device->isOpen()) { + // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h). + // This is much faster than seeking back and forth into QIODevice. + const QByteArray data = device->peek(16384); + + int magicAccuracy = 0; + MimeType candidateByData(findByData(data, &magicAccuracy)); + + // Disambiguate conflicting extensions (if magic matching found something) + if (candidateByData.isValid() && magicAccuracy > 0) { + const QString sniffedMime = candidateByData.name(); + // If the sniffedMime matches a highest-weight glob match, use it + if (candidatesByName.m_matchingMimeTypes.contains(sniffedMime)) { + *accuracyPtr = 100; + return candidateByData; + } + for (const QString &m : qAsConst(candidatesByName.m_allMatchingMimeTypes)) { + if (inherits(m, sniffedMime)) { + // We have magic + pattern pointing to this, so it's a pretty good match + *accuracyPtr = 100; + return mimeTypeForName(m); + } + } + if (candidatesByName.m_allMatchingMimeTypes.isEmpty()) { + // No glob, use magic + *accuracyPtr = magicAccuracy; + return candidateByData; + } + } + } + + if (candidatesByName.m_allMatchingMimeTypes.count() > 1) { + candidatesByName.m_matchingMimeTypes.sort(); // make it deterministic + *accuracyPtr = 20; + const MimeType mime = mimeTypeForName(candidatesByName.m_matchingMimeTypes.at(0)); + if (mime.isValid()) + return mime; + } + + return mimeTypeForName(defaultMimeType()); + }; + + if (device) + return matchOnContent(device); + + QFile fallbackFile(fileName); + fallbackFile.open(QIODevice::ReadOnly); // error handling: matchOnContent() will check isOpen() + return matchOnContent(&fallbackFile); +} + +QList<MimeType> MimeDatabasePrivate::allMimeTypes() +{ + QList<MimeType> result; + for (const auto &provider : providers()) + provider->addAllMimeTypes(result); + return result; +} + +bool MimeDatabasePrivate::inherits(const QString &mime, const QString &parent) +{ + const QString resolvedParent = resolveAlias(parent); + std::stack<QString, QStringList> toCheck; + toCheck.push(mime); + while (!toCheck.empty()) { + if (toCheck.top() == resolvedParent) + return true; + const QString mimeName = toCheck.top(); + toCheck.pop(); + const auto parentList = parents(mimeName); + for (const QString &par : parentList) + toCheck.push(resolveAlias(par)); + } + return false; +} + +/*! + \class MimeDatabase + \inmodule QtCore + \brief The MimeDatabase class maintains a database of MIME types. + + \since 5.0 + + The MIME type database is provided by the freedesktop.org shared-mime-info + project. If the MIME type database cannot be found on the system, as is the case + on most Windows, \macos, and iOS systems, Qt will use its own copy of it. + + Applications which want to define custom MIME types need to install an + XML file into the locations searched for MIME definitions. + These locations can be queried with + \snippet code/src_corelib_mimetype_mimedatabase.cpp 1 + On a typical Unix system, this will be /usr/share/mime/packages/, but it is also + possible to extend the list of directories by setting the environment variable + \c XDG_DATA_DIRS. For instance adding /opt/myapp/share to \c XDG_DATA_DIRS will result + in /opt/myapp/share/mime/packages/ being searched for MIME definitions. + + Here is an example of MIME XML: + \snippet code/src_corelib_mimetype_mimedatabase.cpp 2 + + For more details about the syntax of XML MIME definitions, including defining + "magic" in order to detect MIME types based on data as well, read the + Shared Mime Info specification at + http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html + + On Unix systems, a binary cache is used for more performance. This cache is generated + by the command "update-mime-database path", where path would be /opt/myapp/share/mime + in the above example. Make sure to run this command when installing the MIME type + definition file. + + \threadsafe + + \snippet code/src_corelib_mimetype_mimedatabase.cpp 0 + + \sa MimeType, {MIME Type Browser Example} + */ + +/*! + \fn MimeDatabase::MimeDatabase(); + Constructs a MimeDatabase object. + + It is perfectly OK to create an instance of MimeDatabase every time you need to + perform a lookup. + The parsing of mimetypes is done on demand (when shared-mime-info is installed) + or when the very first instance is constructed (when parsing XML files directly). + */ +MimeDatabase::MimeDatabase() : + d(staticMimeDatabase()) +{ +} + +/*! + \fn MimeDatabase::~MimeDatabase(); + Destroys the MimeDatabase object. + */ +MimeDatabase::~MimeDatabase() +{ + d = nullptr; +} + +/*! + \fn MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const; + Returns a MIME type for \a nameOrAlias or an invalid one if none found. + */ +MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const +{ + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) + qWarning("Accessing MimeDatabase for %s before plugins are initialized", + qPrintable(nameOrAlias)); + + return d->mimeTypeForName(nameOrAlias); +} + +/*! + Returns a MIME type for \a fileInfo. + + A valid MIME type is always returned. + + The default matching algorithm looks at both the file name and the file + contents, if necessary. The file extension has priority over the contents, + but the contents will be used if the file extension is unknown, or + matches multiple MIME types. + If \a fileInfo is a Unix symbolic link, the file that it refers to + will be used instead. + If the file doesn't match any known pattern or data, the default MIME type + (application/octet-stream) is returned. + + When \a mode is set to MatchExtension, only the file name is used, not + the file contents. The file doesn't even have to exist. If the file name + doesn't match any known pattern, the default MIME type (application/octet-stream) + is returned. + If multiple MIME types match this file, the first one (alphabetically) is returned. + + When \a mode is set to MatchContent, and the file is readable, only the + file contents are used to determine the MIME type. This is equivalent to + calling mimeTypeForData with a QFile as input device. + + \a fileInfo may refer to an absolute or relative path. + + \sa MimeType::isDefault(), mimeTypeForData() +*/ +MimeType MimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const +{ + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) + qWarning("Accessing MimeDatabase for %s before plugins are initialized", + qPrintable(fileInfo.filePath())); + + if (fileInfo.isDir()) + return d->mimeTypeForName(QLatin1String("inode/directory")); + + const QString filePath = fileInfo.filePath(); + +#ifdef Q_OS_UNIX + // Cannot access statBuf.st_mode from the filesystem engine, so we have to stat again. + // In addition we want to follow symlinks. + const QByteArray nativeFilePath = QFile::encodeName(filePath); + QT_STATBUF statBuffer; + if (QT_STAT(nativeFilePath.constData(), &statBuffer) == 0) { + if (S_ISCHR(statBuffer.st_mode)) + return d->mimeTypeForName(QLatin1String("inode/chardevice")); + if (S_ISBLK(statBuffer.st_mode)) + return d->mimeTypeForName(QLatin1String("inode/blockdevice")); + if (S_ISFIFO(statBuffer.st_mode)) + return d->mimeTypeForName(QLatin1String("inode/fifo")); + if (S_ISSOCK(statBuffer.st_mode)) + return d->mimeTypeForName(QLatin1String("inode/socket")); + } +#endif + + int priority = 0; + switch (mode) { + case MatchDefault: + return d->mimeTypeForFileNameAndData(filePath, nullptr, &priority); + case MatchExtension: + locker.unlock(); + return mimeTypeForFile(filePath, mode); + case MatchContent: { + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + locker.unlock(); + return mimeTypeForData(&file); + } else { + return d->mimeTypeForName(d->defaultMimeType()); + } + } + default: + Q_ASSERT(false); + } + return d->mimeTypeForName(d->defaultMimeType()); +} + +/*! + Returns a MIME type for the file named \a fileName using \a mode. + + \overload +*/ +MimeType MimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) const +{ + if (mode == MatchExtension) { + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) + qWarning("Accessing MimeDatabase for %s before plugins are initialized", + qPrintable(fileName)); + + const QStringList matches = d->mimeTypeForFileName(fileName); + const int matchCount = matches.count(); + if (matchCount == 0) { + return d->mimeTypeForName(d->defaultMimeType()); + } else if (matchCount == 1) { + return d->mimeTypeForName(matches.first()); + } else { + // We have to pick one. + return d->mimeTypeForName(matches.first()); + } + } else { + // Implemented as a wrapper around mimeTypeForFile(QFileInfo), so no mutex. + QFileInfo fileInfo(fileName); + return mimeTypeForFile(fileInfo, mode); + } +} + +/*! + Returns the MIME types for the file name \a fileName. + + If the file name doesn't match any known pattern, an empty list is returned. + If multiple MIME types match this file, they are all returned. + + This function does not try to open the file. To also use the content + when determining the MIME type, use mimeTypeForFile() or + mimeTypeForFileNameAndData() instead. + + \sa mimeTypeForFile() +*/ +QList<MimeType> MimeDatabase::mimeTypesForFileName(const QString &fileName) const +{ + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) + qWarning("Accessing MimeDatabase for %s before plugins are initialized", + qPrintable(fileName)); + + const QStringList matches = d->mimeTypeForFileName(fileName); + QList<MimeType> mimes; + mimes.reserve(matches.count()); + for (const QString &mime : matches) + mimes.append(d->mimeTypeForName(mime)); + return mimes; +} +/*! + Returns the suffix for the file \a fileName, as known by the MIME database. + + This allows to pre-select "tar.bz2" for foo.tar.bz2, but still only + "txt" for my.file.with.dots.txt. +*/ +QString MimeDatabase::suffixForFileName(const QString &fileName) const +{ + QMutexLocker locker(&d->mutex); + const int suffixLength = d->findByFileName(fileName).m_knownSuffixLength; + return fileName.right(suffixLength); +} + +/*! + Returns a MIME type for \a data. + + A valid MIME type is always returned. If \a data doesn't match any + known MIME type data, the default MIME type (application/octet-stream) + is returned. +*/ +MimeType MimeDatabase::mimeTypeForData(const QByteArray &data) const +{ + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) + qWarning("Accessing MimeDatabase for data before plugins are initialized"); + + int accuracy = 0; + return d->findByData(data, &accuracy); +} + +/*! + Returns a MIME type for the data in \a device. + + A valid MIME type is always returned. If the data in \a device doesn't match any + known MIME type data, the default MIME type (application/octet-stream) + is returned. +*/ +MimeType MimeDatabase::mimeTypeForData(QIODevice *device) const +{ + QMutexLocker locker(&d->mutex); + + int accuracy = 0; + const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly); + if (device->isOpen()) { + // Read 16K in one go (QIODEVICE_BUFFERSIZE in qiodevice_p.h). + // This is much faster than seeking back and forth into QIODevice. + const QByteArray data = device->peek(16384); + const MimeType result = d->findByData(data, &accuracy); + if (openedByUs) + device->close(); + return result; + } + return d->mimeTypeForName(d->defaultMimeType()); +} + +/*! + Returns a MIME type for \a url. + + If the URL is a local file, this calls mimeTypeForFile. + + Otherwise the matching is done based on the file name only, + except for schemes where file names don't mean much, like HTTP. + This method always returns the default mimetype for HTTP URLs, + use QNetworkAccessManager to handle HTTP URLs properly. + + A valid MIME type is always returned. If \a url doesn't match any + known MIME type data, the default MIME type (application/octet-stream) + is returned. +*/ +MimeType MimeDatabase::mimeTypeForUrl(const QUrl &url) const +{ + if (url.isLocalFile()) + return mimeTypeForFile(url.toLocalFile()); + + const QString scheme = url.scheme(); + if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto")) + return mimeTypeForName(d->defaultMimeType()); + + return mimeTypeForFile(url.path(), MatchExtension); +} + +/*! + Returns a MIME type for the given \a fileName and \a device data. + + This overload can be useful when the file is remote, and we started to + download some of its data in a device. This allows to do full MIME type + matching for remote files as well. + + If the device is not open, it will be opened by this function, and closed + after the MIME type detection is completed. + + A valid MIME type is always returned. If \a device data doesn't match any + known MIME type data, the default MIME type (application/octet-stream) + is returned. + + This method looks at both the file name and the file contents, + if necessary. The file extension has priority over the contents, + but the contents will be used if the file extension is unknown, or + matches multiple MIME types. +*/ +MimeType MimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const +{ + QMutexLocker locker(&d->mutex); + + if (fileName.endsWith(QLatin1Char('/'))) + return d->mimeTypeForName(QLatin1String("inode/directory")); + + int accuracy = 0; + const bool openedByUs = !device->isOpen() && device->open(QIODevice::ReadOnly); + const MimeType result = d->mimeTypeForFileNameAndData(fileName, device, &accuracy); + if (openedByUs) + device->close(); + return result; +} + +/*! + Returns a MIME type for the given \a fileName and device \a data. + + This overload can be useful when the file is remote, and we started to + download some of its data. This allows to do full MIME type matching for + remote files as well. + + A valid MIME type is always returned. If \a data doesn't match any + known MIME type data, the default MIME type (application/octet-stream) + is returned. + + This method looks at both the file name and the file contents, + if necessary. The file extension has priority over the contents, + but the contents will be used if the file extension is unknown, or + matches multiple MIME types. +*/ +MimeType MimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const +{ + QMutexLocker locker(&d->mutex); + + if (fileName.endsWith(QLatin1Char('/'))) + return d->mimeTypeForName(QLatin1String("inode/directory")); + + QBuffer buffer(const_cast<QByteArray *>(&data)); + buffer.open(QIODevice::ReadOnly); + int accuracy = 0; + return d->mimeTypeForFileNameAndData(fileName, &buffer, &accuracy); +} + +/*! + Returns the list of all available MIME types. + + This can be useful for showing all MIME types to the user, for instance + in a MIME type editor. Do not use unless really necessary in other cases + though, prefer using the \l {mimeTypeForData()}{mimeTypeForXxx()} methods for performance reasons. +*/ +QList<MimeType> MimeDatabase::allMimeTypes() const +{ + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) + qWarning("Accessing MimeDatabase for all mime types before plugins are initialized"); + + return d->allMimeTypes(); +} + +/*! + \enum MimeDatabase::MatchMode + + This enum specifies how matching a file to a MIME type is performed. + + \value MatchDefault Both the file name and content are used to look for a match + + \value MatchExtension Only the file name is used to look for a match + + \value MatchContent The file content is used to look for a match +*/ + +// added for Qt Creator +void MimeDatabasePrivate::addMimeData(const QString &id, const QByteArray &data) +{ + if (m_additionalData.contains(id)) + qWarning("Overwriting data in mime database, id '%s'", qPrintable(id)); + + m_additionalData.insert(id, data); + m_forceLoad = true; +} + +QMap<int, QList<MimeMagicRule>> MimeDatabasePrivate::magicRulesForMimeType(const MimeType &mimeType) +{ + for (const auto &provider : providers()) { + if (provider->hasMimeTypeForName(mimeType.name())) + return provider->magicRulesForMimeType(mimeType); + } + return {}; +} + +void MimeDatabasePrivate::setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules) +{ + for (const auto &provider : providers()) { + if (provider->hasMimeTypeForName(mimeType.name())) { + provider->setMagicRulesForMimeType(mimeType, rules); + return; + } + } +} + +void MimeDatabasePrivate::setGlobPatternsForMimeType(const MimeType &mimeType, + const QStringList &patterns) +{ + for (const auto &provider : providers()) { + if (provider->hasMimeTypeForName(mimeType.name())) { + provider->setGlobPatternsForMimeType(mimeType, patterns); + return; + } + } +} + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimedatabase.h b/src/libs/utils/mimetypes2/mimedatabase.h new file mode 100644 index 00000000000..7516c467ddf --- /dev/null +++ b/src/libs/utils/mimetypes2/mimedatabase.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected], author David Faure <[email protected]> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include "mimetype.h" + +#include "mimemagicrule_p.h" + +#include <utils/utils_global.h> + +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE +class QByteArray; +class QFileInfo; +class QIODevice; +class QUrl; +QT_END_NAMESPACE + +namespace Utils { + +class MimeDatabasePrivate; + +class MimeDatabase +{ + Q_DISABLE_COPY(MimeDatabase) + +public: + MimeDatabase(); + ~MimeDatabase(); + + MimeType mimeTypeForName(const QString &nameOrAlias) const; + + enum MatchMode { + MatchDefault = 0x0, + MatchExtension = 0x1, + MatchContent = 0x2 + }; + + MimeType mimeTypeForFile(const QString &fileName, MatchMode mode = MatchDefault) const; + MimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode = MatchDefault) const; + QList<MimeType> mimeTypesForFileName(const QString &fileName) const; + + MimeType mimeTypeForData(const QByteArray &data) const; + MimeType mimeTypeForData(QIODevice *device) const; + + MimeType mimeTypeForUrl(const QUrl &url) const; + MimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device) const; + MimeType mimeTypeForFileNameAndData(const QString &fileName, const QByteArray &data) const; + + QString suffixForFileName(const QString &fileName) const; + + QList<MimeType> allMimeTypes() const; + +private: + MimeDatabasePrivate *d; +}; + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimedatabase_p.h b/src/libs/utils/mimetypes2/mimedatabase_p.h new file mode 100644 index 00000000000..a85d76af71d --- /dev/null +++ b/src/libs/utils/mimetypes2/mimedatabase_p.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected], author David Faure <[email protected]> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "mimetype.h" + +#include "mimeglobpattern_p.h" +#include "mimemagicrule_p.h" +#include "mimetype_p.h" + +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qlist.h> +#include <QtCore/qmutex.h> + +#include <vector> +#include <memory> + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +namespace Utils { + +class MimeDatabase; +class MimeProviderBase; + +class MimeDatabasePrivate +{ +public: + Q_DISABLE_COPY_MOVE(MimeDatabasePrivate) + + MimeDatabasePrivate(); + ~MimeDatabasePrivate(); + + static MimeDatabasePrivate *instance(); + + inline QString defaultMimeType() const { return m_defaultMimeType; } + + bool inherits(const QString &mime, const QString &parent); + + QList<MimeType> allMimeTypes(); + + QString resolveAlias(const QString &nameOrAlias); + QStringList parents(const QString &mimeName); + MimeType mimeTypeForName(const QString &nameOrAlias); + MimeType mimeTypeForFileNameAndData(const QString &fileName, QIODevice *device, int *priorityPtr); + MimeType findByData(const QByteArray &data, int *priorityPtr); + QStringList mimeTypeForFileName(const QString &fileName); + MimeGlobMatchResult findByFileName(const QString &fileName); + + // API for MimeType. Takes care of locking the mutex. + void loadMimeTypePrivate(MimeTypePrivate &mimePrivate); + void loadGenericIcon(MimeTypePrivate &mimePrivate); + void loadIcon(MimeTypePrivate &mimePrivate); + QStringList mimeParents(const QString &mimeName); + QStringList listAliases(const QString &mimeName); + bool mimeInherits(const QString &mime, const QString &parent); + + // added for Qt Creator + void addMimeData(const QString &id, const QByteArray &data); + QMap<int, QList<MimeMagicRule>> magicRulesForMimeType(const MimeType &mimeType); + void setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules); + void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns); + +private: + using Providers = std::vector<std::unique_ptr<MimeProviderBase>>; + const Providers &providers(); + bool shouldCheck(); + void loadProviders(); + + mutable Providers m_providers; + QElapsedTimer m_lastCheck; + + // added for Qt Creator + QHash<QString, QByteArray> m_additionalData; // id -> data + bool m_forceLoad = true; + +public: + const QString m_defaultMimeType; + QMutex mutex; + + // added for Qt Creator + int m_startupPhase = 0; +}; + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimeglobpattern.cpp b/src/libs/utils/mimetypes2/mimeglobpattern.cpp new file mode 100644 index 00000000000..601a4184a01 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimeglobpattern.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimeglobpattern_p.h" + +#if QT_CONFIG(regularexpression) +#include <QRegularExpression> +#endif +#include <QStringList> +#include <QDebug> + +namespace Utils { + +/*! + \internal + \class MimeGlobMatchResult + \inmodule QtCore + \brief The MimeGlobMatchResult class accumulates results from glob matching. + + Handles glob weights, and preferring longer matches over shorter matches. +*/ + +void MimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QString &pattern, int knownSuffixLength) +{ + if (m_allMatchingMimeTypes.contains(mimeType)) + return; + // Is this a lower-weight pattern than the last match? Skip this match then. + if (weight < m_weight) { + m_allMatchingMimeTypes.append(mimeType); + return; + } + bool replace = weight > m_weight; + if (!replace) { + // Compare the length of the match + if (pattern.length() < m_matchingPatternLength) + return; // too short, ignore + else if (pattern.length() > m_matchingPatternLength) { + // longer: clear any previous match (like *.bz2, when pattern is *.tar.bz2) + replace = true; + } + } + if (replace) { + m_matchingMimeTypes.clear(); + // remember the new "longer" length + m_matchingPatternLength = pattern.length(); + m_weight = weight; + } + if (!m_matchingMimeTypes.contains(mimeType)) { + m_matchingMimeTypes.append(mimeType); + if (replace) + m_allMatchingMimeTypes.prepend(mimeType); // highest-weight first + else + m_allMatchingMimeTypes.append(mimeType); + m_knownSuffixLength = knownSuffixLength; + } +} + +MimeGlobPattern::PatternType MimeGlobPattern::detectPatternType(const QString &pattern) const +{ + const int patternLength = pattern.length(); + if (!patternLength) + return OtherPattern; + + const int starCount = pattern.count(QLatin1Char('*')); + const bool hasSquareBracket = pattern.indexOf(QLatin1Char('[')) != -1; + const bool hasQuestionMark = pattern.indexOf(QLatin1Char('?')) != -1; + + if (!hasSquareBracket && !hasQuestionMark) { + if (starCount == 1) { + // Patterns like "*~", "*.extension" + if (pattern.at(0) == QLatin1Char('*')) + return SuffixPattern; + // Patterns like "README*" (well this is currently the only one like that...) + if (pattern.at(patternLength - 1) == QLatin1Char('*')) + return PrefixPattern; + } else if (starCount == 0) { + // Names without any wildcards like "README" + return LiteralPattern; + } + } + + if (pattern == QLatin1String("[0-9][0-9][0-9].vdr")) + return VdrPattern; + + if (pattern == QLatin1String("*.anim[1-9j]")) + return AnimPattern; + + return OtherPattern; +} + + +/*! + \internal + \class MimeGlobPattern + \inmodule QtCore + \brief The MimeGlobPattern class contains the glob pattern for file names for MIME type matching. + + \sa MimeType, MimeDatabase, MimeMagicRuleMatcher, MimeMagicRule +*/ + +bool MimeGlobPattern::matchFileName(const QString &inputFileName) const +{ + // "Applications MUST match globs case-insensitively, except when the case-sensitive + // attribute is set to true." + // The constructor takes care of putting case-insensitive patterns in lowercase. + const QString fileName = m_caseSensitivity == Qt::CaseInsensitive + ? inputFileName.toLower() : inputFileName; + + const int patternLength = m_pattern.length(); + if (!patternLength) + return false; + const int fileNameLength = fileName.length(); + + switch (m_patternType) { + case SuffixPattern: { + if (fileNameLength + 1 < patternLength) + return false; + + const QChar *c1 = m_pattern.unicode() + patternLength - 1; + const QChar *c2 = fileName.unicode() + fileNameLength - 1; + int cnt = 1; + while (cnt < patternLength && *c1-- == *c2--) + ++cnt; + return cnt == patternLength; + } + case PrefixPattern: { + if (fileNameLength + 1 < patternLength) + return false; + + const QChar *c1 = m_pattern.unicode(); + const QChar *c2 = fileName.unicode(); + int cnt = 1; + while (cnt < patternLength && *c1++ == *c2++) + ++cnt; + return cnt == patternLength; + } + case LiteralPattern: + return (m_pattern == fileName); + case VdrPattern: // "[0-9][0-9][0-9].vdr" case + return fileNameLength == 7 + && fileName.at(0).isDigit() && fileName.at(1).isDigit() && fileName.at(2).isDigit() + && QStringView{fileName}.mid(3, 4) == QLatin1String(".vdr"); + case AnimPattern: { // "*.anim[1-9j]" case + if (fileNameLength < 6) + return false; + const QChar lastChar = fileName.at(fileNameLength - 1); + const bool lastCharOK = (lastChar.isDigit() && lastChar != QLatin1Char('0')) + || lastChar == QLatin1Char('j'); + return lastCharOK && QStringView{fileName}.mid(fileNameLength - 6, 5) == QLatin1String(".anim"); + } + case OtherPattern: +#if QT_CONFIG(regularexpression) + // Other fallback patterns: slow but correct method + const QRegularExpression rx(QRegularExpression::anchoredPattern( + QRegularExpression::wildcardToRegularExpression(m_pattern))); + return rx.match(fileName).hasMatch(); +#else + return false; +#endif + } + return false; +} + +static bool isSimplePattern(const QString &pattern) +{ + // starts with "*.", has no other '*' + return pattern.lastIndexOf(QLatin1Char('*')) == 0 + && pattern.length() > 1 + && pattern.at(1) == QLatin1Char('.') // (other dots are OK, like *.tar.bz2) + // and contains no other special character + && !pattern.contains(QLatin1Char('?')) + && !pattern.contains(QLatin1Char('[')) + ; +} + +static bool isFastPattern(const QString &pattern) +{ + // starts with "*.", has no other '*' and no other '.' + return pattern.lastIndexOf(QLatin1Char('*')) == 0 + && pattern.lastIndexOf(QLatin1Char('.')) == 1 + // and contains no other special character + && !pattern.contains(QLatin1Char('?')) + && !pattern.contains(QLatin1Char('[')) + ; +} + +void MimeAllGlobPatterns::addGlob(const MimeGlobPattern &glob) +{ + const QString &pattern = glob.pattern(); + Q_ASSERT(!pattern.isEmpty()); + + // Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50) + // or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50) + // or lowWeightPatternOffset (<=50) + + if (glob.weight() == 50 && isFastPattern(pattern) && !glob.isCaseSensitive()) { + // The bulk of the patterns is *.foo with weight 50 --> those go into the fast patterns hash. + const QString extension = pattern.mid(2).toLower(); + QStringList &patterns = m_fastPatterns[extension]; // find or create + if (!patterns.contains(glob.mimeType())) + patterns.append(glob.mimeType()); + } else { + if (glob.weight() > 50) { + if (!m_highWeightGlobs.hasPattern(glob.mimeType(), glob.pattern())) + m_highWeightGlobs.append(glob); + } else { + if (!m_lowWeightGlobs.hasPattern(glob.mimeType(), glob.pattern())) + m_lowWeightGlobs.append(glob); + } + } +} + +void MimeAllGlobPatterns::removeMimeType(const QString &mimeType) +{ + for (auto &x : m_fastPatterns) + x.removeAll(mimeType); + m_highWeightGlobs.removeMimeType(mimeType); + m_lowWeightGlobs.removeMimeType(mimeType); +} + +void MimeGlobPatternList::match(MimeGlobMatchResult &result, + const QString &fileName, + const QList<QString> &ignoreMimeTypes) const +{ + + MimeGlobPatternList::const_iterator it = this->constBegin(); + const MimeGlobPatternList::const_iterator endIt = this->constEnd(); + for (; it != endIt; ++it) { + const MimeGlobPattern &glob = *it; + if (ignoreMimeTypes.contains(glob.mimeType())) + continue; + if (glob.matchFileName(fileName)) { + const QString pattern = glob.pattern(); + const int suffixLen = isSimplePattern(pattern) ? pattern.length() - 2 : 0; + result.addMatch(glob.mimeType(), glob.weight(), pattern, suffixLen); + } + } +} + +void MimeAllGlobPatterns::matchingGlobs(const QString &fileName, + MimeGlobMatchResult &result, + const QList<QString> &ignoreMimeTypes) const +{ + // First try the high weight matches (>50), if any. + m_highWeightGlobs.match(result, fileName, ignoreMimeTypes); + + // Now use the "fast patterns" dict, for simple *.foo patterns with weight 50 + // (which is most of them, so this optimization is definitely worth it) + const int lastDot = fileName.lastIndexOf(QLatin1Char('.')); + if (lastDot != -1) { // if no '.', skip the extension lookup + const int ext_len = fileName.length() - lastDot - 1; + const QString simpleExtension = fileName.right(ext_len).toLower(); + // (toLower because fast patterns are always case-insensitive and saved as lowercase) + + const QStringList matchingMimeTypes = m_fastPatterns.value(simpleExtension); + const QString simplePattern = QLatin1String("*.") + simpleExtension; + for (const QString &mime : matchingMimeTypes) { + if (!ignoreMimeTypes.contains(mime)) + result.addMatch(mime, 50, simplePattern, simpleExtension.size()); + } + // Can't return yet; *.tar.bz2 has to win over *.bz2, so we need the low-weight mimetypes anyway, + // at least those with weight 50. + } + + // Finally, try the low weight matches (<=50) + m_lowWeightGlobs.match(result, fileName, ignoreMimeTypes); +} + +void MimeAllGlobPatterns::clear() +{ + m_fastPatterns.clear(); + m_highWeightGlobs.clear(); + m_lowWeightGlobs.clear(); +} + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimeglobpattern_p.h b/src/libs/utils/mimetypes2/mimeglobpattern_p.h new file mode 100644 index 00000000000..6519acb66b8 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimeglobpattern_p.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstringlist.h> +#include <QtCore/qhash.h> + +namespace Utils { + +struct MimeGlobMatchResult +{ + void addMatch(const QString &mimeType, int weight, const QString &pattern, int knownSuffixLength = 0); + + QStringList m_matchingMimeTypes; // only those with highest weight + QStringList m_allMatchingMimeTypes; + int m_weight = 0; + int m_matchingPatternLength = 0; + int m_knownSuffixLength = 0; +}; + +class MimeGlobPattern +{ +public: + static const unsigned MaxWeight = 100; + static const unsigned DefaultWeight = 50; + static const unsigned MinWeight = 1; + + explicit MimeGlobPattern(const QString &thePattern, const QString &theMimeType, unsigned theWeight = DefaultWeight, Qt::CaseSensitivity s = Qt::CaseInsensitive) : + m_pattern(s == Qt::CaseInsensitive ? thePattern.toLower() : thePattern), + m_mimeType(theMimeType), + m_weight(theWeight), + m_caseSensitivity(s), + m_patternType(detectPatternType(m_pattern)) + { + } + + void swap(MimeGlobPattern &other) noexcept + { + qSwap(m_pattern, other.m_pattern); + qSwap(m_mimeType, other.m_mimeType); + qSwap(m_weight, other.m_weight); + qSwap(m_caseSensitivity, other.m_caseSensitivity); + qSwap(m_patternType, other.m_patternType); + } + + bool matchFileName(const QString &inputFileName) const; + + inline const QString &pattern() const { return m_pattern; } + inline unsigned weight() const { return m_weight; } + inline const QString &mimeType() const { return m_mimeType; } + inline bool isCaseSensitive() const { return m_caseSensitivity == Qt::CaseSensitive; } + +private: + enum PatternType { + SuffixPattern, + PrefixPattern, + LiteralPattern, + VdrPattern, // special handling for "[0-9][0-9][0-9].vdr" pattern + AnimPattern, // special handling for "*.anim[1-9j]" pattern + OtherPattern + }; + PatternType detectPatternType(const QString &pattern) const; + + QString m_pattern; + QString m_mimeType; + int m_weight; + Qt::CaseSensitivity m_caseSensitivity; + PatternType m_patternType; +}; + +class MimeGlobPatternList : public QList<MimeGlobPattern> +{ +public: + bool hasPattern(const QString &mimeType, const QString &pattern) const + { + const_iterator it = begin(); + const const_iterator myend = end(); + for (; it != myend; ++it) + if ((*it).pattern() == pattern && (*it).mimeType() == mimeType) + return true; + return false; + } + + /*! + "noglobs" is very rare occurrence, so it's ok if it's slow + */ + void removeMimeType(const QString &mimeType) + { + auto isMimeTypeEqual = [&mimeType](const MimeGlobPattern &pattern) { + return pattern.mimeType() == mimeType; + }; + erase(std::remove_if(begin(), end(), isMimeTypeEqual), end()); + } + + void match(MimeGlobMatchResult &result, + const QString &fileName, + const QList<QString> &ignoreMimeTypes) const; +}; + +/*! + Result of the globs parsing, as data structures ready for efficient MIME type matching. + This contains: + 1) a map of fast regular patterns (e.g. *.txt is stored as "txt" in a qhash's key) + 2) a linear list of high-weight globs + 3) a linear list of low-weight globs + */ +class MimeAllGlobPatterns +{ +public: + typedef QHash<QString, QStringList> PatternsMap; // MIME type -> patterns + + void addGlob(const MimeGlobPattern &glob); + void removeMimeType(const QString &mimeType); + void matchingGlobs(const QString &fileName, + MimeGlobMatchResult &result, + const QList<QString> &ignoreMimeTypes) const; + void clear(); + + PatternsMap m_fastPatterns; // example: "doc" -> "application/msword", "text/plain" + MimeGlobPatternList m_highWeightGlobs; + MimeGlobPatternList m_lowWeightGlobs; // <= 50, including the non-fast 50 patterns +}; + +} // namespace Utils + +QT_BEGIN_NAMESPACE +Q_DECLARE_SHARED(Utils::MimeGlobPattern) +QT_END_NAMESPACE diff --git a/src/libs/utils/mimetypes2/mimemagicrule.cpp b/src/libs/utils/mimetypes2/mimemagicrule.cpp new file mode 100644 index 00000000000..fc69a9ec9b0 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimemagicrule.cpp @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimemagicrule_p.h" + +#include "mimetypeparser_p.h" +#include <QtCore/QList> +#include <QtCore/QDebug> +#include <qendian.h> + +namespace Utils { + +// in the same order as Type! +static const char magicRuleTypes_string[] = + "invalid\0" + "string\0" + "regexp\0" + "host16\0" + "host32\0" + "big16\0" + "big32\0" + "little16\0" + "little32\0" + "byte\0" + "\0"; + +static const int magicRuleTypes_indices[] = { + 0, 8, 15, 22, 29, 36, 42, 48, 57, 66, 71, 0 +}; + +MimeMagicRule::Type MimeMagicRule::type(const QByteArray &theTypeName) +{ + for (int i = String; i <= Byte; ++i) { + if (theTypeName == magicRuleTypes_string + magicRuleTypes_indices[i]) + return Type(i); + } + return Invalid; +} + +QByteArray MimeMagicRule::typeName(MimeMagicRule::Type theType) +{ + return magicRuleTypes_string + magicRuleTypes_indices[theType]; +} + +bool MimeMagicRule::operator==(const MimeMagicRule &other) const +{ + return m_type == other.m_type && + m_value == other.m_value && + m_startPos == other.m_startPos && + m_endPos == other.m_endPos && + m_mask == other.m_mask && + m_regexp == other.m_regexp && + m_pattern == other.m_pattern && + m_number == other.m_number && + m_numberMask == other.m_numberMask && + m_matchFunction == other.m_matchFunction; +} + +// Used by both providers +bool MimeMagicRule::matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, + int valueLength, const char *valueData, const char *mask) +{ + // Size of searched data. + // Example: value="ABC", rangeLength=3 -> we need 3+3-1=5 bytes (ABCxx,xABCx,xxABC would match) + const int dataNeeded = qMin(rangeLength + valueLength - 1, dataSize - rangeStart); + + if (!mask) { + // callgrind says QByteArray::indexOf is much slower, since our strings are typically too + // short for be worth Boyer-Moore matching (1 to 71 bytes, 11 bytes on average). + bool found = false; + for (int i = rangeStart; i < rangeStart + rangeLength; ++i) { + if (i + valueLength > dataSize) + break; + + if (memcmp(valueData, dataPtr + i, valueLength) == 0) { + found = true; + break; + } + } + if (!found) + return false; + } else { + bool found = false; + const char *readDataBase = dataPtr + rangeStart; + // Example (continued from above): + // deviceSize is 4, so dataNeeded was max'ed to 4. + // maxStartPos = 4 - 3 + 1 = 2, and indeed + // we need to check for a match a positions 0 and 1 (ABCx and xABC). + const int maxStartPos = dataNeeded - valueLength + 1; + for (int i = 0; i < maxStartPos; ++i) { + const char *d = readDataBase + i; + bool valid = true; + for (int idx = 0; idx < valueLength; ++idx) { + if (((*d++) & mask[idx]) != (valueData[idx] & mask[idx])) { + valid = false; + break; + } + } + if (valid) + found = true; + } + if (!found) + return false; + } + //qDebug() << "Found" << value << "in" << searchedData; + return true; +} + +bool MimeMagicRule::matchString(const QByteArray &data) const +{ + const int rangeLength = m_endPos - m_startPos + 1; + return MimeMagicRule::matchSubstring(data.constData(), data.size(), m_startPos, rangeLength, m_pattern.size(), m_pattern.constData(), m_mask.constData()); +} + +template <typename T> +bool MimeMagicRule::matchNumber(const QByteArray &data) const +{ + const T value(m_number); + const T mask(m_numberMask); + + //qDebug() << "matchNumber" << "0x" << QString::number(m_number, 16) << "size" << sizeof(T); + //qDebug() << "mask" << QString::number(m_numberMask, 16); + + const char *p = data.constData() + m_startPos; + const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), m_endPos); + for ( ; p <= e; ++p) { + if ((qFromUnaligned<T>(p) & mask) == (value & mask)) + return true; + } + + return false; +} + +static inline QByteArray makePattern(const QByteArray &value) +{ + QByteArray pattern(value.size(), Qt::Uninitialized); + char *data = pattern.data(); + + const char *p = value.constData(); + const char *e = p + value.size(); + for ( ; p < e; ++p) { + if (*p == '\\' && ++p < e) { + if (*p == 'x') { // hex (\\xff) + char c = 0; + for (int i = 0; i < 2 && p + 1 < e; ++i) { + ++p; + if (*p >= '0' && *p <= '9') + c = (c << 4) + *p - '0'; + else if (*p >= 'a' && *p <= 'f') + c = (c << 4) + *p - 'a' + 10; + else if (*p >= 'A' && *p <= 'F') + c = (c << 4) + *p - 'A' + 10; + else + continue; + } + *data++ = c; + } else if (*p >= '0' && *p <= '7') { // oct (\\7, or \\77, or \\377) + char c = *p - '0'; + if (p + 1 < e && p[1] >= '0' && p[1] <= '7') { + c = (c << 3) + *(++p) - '0'; + if (p + 1 < e && p[1] >= '0' && p[1] <= '7' && p[-1] <= '3') + c = (c << 3) + *(++p) - '0'; + } + *data++ = c; + } else if (*p == 'n') { + *data++ = '\n'; + } else if (*p == 'r') { + *data++ = '\r'; + } else if (*p == 't') { + *data++ = '\t'; + } else { // escaped + *data++ = *p; + } + } else { + *data++ = *p; + } + } + pattern.truncate(data - pattern.data()); + + return pattern; +} + +bool MimeMagicRule::matchRegExp(const QByteArray &data) const +{ + const QString str = QString::fromUtf8(data); + int length = m_endPos; + if (length == m_startPos) + length = -1; // from startPos to end of string + const QString subStr = str.left(length); + return m_regexp.match(subStr, m_startPos).hasMatch(); +} + +MimeMagicRule::MimeMagicRule(const Type &type, + const QByteArray &value, + int startPos, + int endPos, + const QByteArray &mask, + QString *errorString) + : m_type(type) + , m_value(value) + , m_startPos(startPos) + , m_endPos(endPos) + , m_mask(mask) +{ + init(errorString); +} + +// Evaluate a magic match rule like +// <match value="must be converted with BinHex" type="string" offset="11"/> +// <match value="0x9501" type="big16" offset="0:64"/> + +MimeMagicRule::MimeMagicRule(const QString &type, + const QByteArray &value, + const QString &offsets, + const QByteArray &mask, + QString *errorString) + : m_type(MimeMagicRule::type(type.toLatin1())), + m_value(value), + m_mask(mask), + m_matchFunction(nullptr) +{ + if (Q_UNLIKELY(m_type == Invalid)) + *errorString = QLatin1String("Type ") + type + QLatin1String(" is not supported"); + + // Parse for offset as "1" or "1:10" + const int colonIndex = offsets.indexOf(QLatin1Char(':')); + const QStringView startPosStr = QStringView{offsets}.mid(0, colonIndex); // \ These decay to returning 'offsets' + const QStringView endPosStr = QStringView{offsets}.mid(colonIndex + 1);// / unchanged when colonIndex == -1 + if (Q_UNLIKELY(!MimeTypeParserBase::parseNumber(startPosStr, &m_startPos, errorString)) || + Q_UNLIKELY(!MimeTypeParserBase::parseNumber(endPosStr, &m_endPos, errorString))) { + m_type = Invalid; + return; + } + + if (Q_UNLIKELY(m_value.isEmpty())) { + m_type = Invalid; + if (errorString) + *errorString = QStringLiteral("Invalid empty magic rule value"); + return; + } + + init(errorString); +} + +void MimeMagicRule::init(QString *errorString) +{ + if (m_type >= Host16 && m_type <= Byte) { + bool ok; + m_number = m_value.toUInt(&ok, 0); // autodetect base + if (Q_UNLIKELY(!ok)) { + m_type = Invalid; + if (errorString) + *errorString = QLatin1String("Invalid magic rule value \"") + QLatin1String(m_value) + QLatin1Char('"'); + return; + } + m_numberMask = !m_mask.isEmpty() ? m_mask.toUInt(&ok, 0) : 0; // autodetect base + } + + switch (m_type) { + case String: + m_pattern = makePattern(m_value); + m_pattern.squeeze(); + if (!m_mask.isEmpty()) { + if (Q_UNLIKELY(m_mask.size() < 4 || !m_mask.startsWith("0x"))) { + m_type = Invalid; + if (errorString) + *errorString = QLatin1String("Invalid magic rule mask \"") + QLatin1String(m_mask) + QLatin1Char('"'); + return; + } + const QByteArray &tempMask = QByteArray::fromHex(QByteArray::fromRawData( + m_mask.constData() + 2, m_mask.size() - 2)); + if (Q_UNLIKELY(tempMask.size() != m_pattern.size())) { + m_type = Invalid; + if (errorString) + *errorString = QLatin1String("Invalid magic rule mask size \"") + QLatin1String(m_mask) + QLatin1Char('"'); + return; + } + m_mask = tempMask; + } else { + m_mask.fill(char(-1), m_pattern.size()); + } + m_mask.squeeze(); + m_matchFunction = &MimeMagicRule::matchString; + break; + case RegExp: + m_regexp.setPatternOptions(QRegularExpression::MultilineOption + | QRegularExpression::DotMatchesEverythingOption); + m_regexp.setPattern(QString::fromLatin1(m_value)); + if (!m_regexp.isValid()) { + m_type = Invalid; + if (errorString) + *errorString = QString::fromLatin1("Invalid magic rule regexp value \"%1\"") + .arg(QString::fromLatin1(m_value)); + return; + } + m_matchFunction = &MimeMagicRule::matchRegExp; + break; + case Byte: + if (m_number <= quint8(-1)) { + if (m_numberMask == 0) + m_numberMask = quint8(-1); + m_matchFunction = &MimeMagicRule::matchNumber<quint8>; + } + break; + case Big16: + case Little16: + if (m_number <= quint16(-1)) { + m_number = m_type == Little16 ? qFromLittleEndian<quint16>(m_number) : qFromBigEndian<quint16>(m_number); + if (m_numberMask != 0) + m_numberMask = m_type == Little16 ? qFromLittleEndian<quint16>(m_numberMask) : qFromBigEndian<quint16>(m_numberMask); + } + Q_FALLTHROUGH(); + case Host16: + if (m_number <= quint16(-1)) { + if (m_numberMask == 0) + m_numberMask = quint16(-1); + m_matchFunction = &MimeMagicRule::matchNumber<quint16>; + } + break; + case Big32: + case Little32: + m_number = m_type == Little32 ? qFromLittleEndian<quint32>(m_number) : qFromBigEndian<quint32>(m_number); + if (m_numberMask != 0) + m_numberMask = m_type == Little32 ? qFromLittleEndian<quint32>(m_numberMask) : qFromBigEndian<quint32>(m_numberMask); + Q_FALLTHROUGH(); + case Host32: + if (m_numberMask == 0) + m_numberMask = quint32(-1); + m_matchFunction = &MimeMagicRule::matchNumber<quint32>; + break; + default: + break; + } +} + +QByteArray MimeMagicRule::mask() const +{ + QByteArray result = m_mask; + if (m_type == String) { + // restore '0x' + result = "0x" + result.toHex(); + } + return result; +} + +bool MimeMagicRule::matches(const QByteArray &data) const +{ + const bool ok = m_matchFunction && (this->*m_matchFunction)(data); + if (!ok) + return false; + + // No submatch? Then we are done. + if (m_subMatches.isEmpty()) + return true; + + //qDebug() << "Checking" << m_subMatches.count() << "sub-rules"; + // Check that one of the submatches matches too + for ( QList<MimeMagicRule>::const_iterator it = m_subMatches.begin(), end = m_subMatches.end() ; + it != end ; ++it ) { + if ((*it).matches(data)) { + // One of the hierarchies matched -> mimetype recognized. + return true; + } + } + return false; + + +} + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimemagicrule_p.h b/src/libs/utils/mimetypes2/mimemagicrule_p.h new file mode 100644 index 00000000000..8aa48fd51ee --- /dev/null +++ b/src/libs/utils/mimetypes2/mimemagicrule_p.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <utils/utils_global.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qlist.h> +#include <QtCore/qregularexpression.h> +#include <QtCore/qscopedpointer.h> + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT MimeMagicRule +{ +public: + enum Type { + Invalid = 0, + String, + RegExp, + Host16, + Host32, + Big16, + Big32, + Little16, + Little32, + Byte + }; + + MimeMagicRule(const QString &typeStr, const QByteArray &value, const QString &offsets, + const QByteArray &mask, QString *errorString); + // added for Qt Creator + MimeMagicRule(const Type &type, const QByteArray &value, int startPos, int endPos, + const QByteArray &mask = {}, QString *errorString = nullptr); + + void swap(MimeMagicRule &other) noexcept + { + qSwap(m_type, other.m_type); + qSwap(m_value, other.m_value); + qSwap(m_startPos, other.m_startPos); + qSwap(m_endPos, other.m_endPos); + qSwap(m_mask, other.m_mask); + qSwap(m_pattern, other.m_pattern); + qSwap(m_number, other.m_number); + qSwap(m_numberMask, other.m_numberMask); + qSwap(m_matchFunction, other.m_matchFunction); + } + + bool operator==(const MimeMagicRule &other) const; + + Type type() const { return m_type; } + QByteArray value() const { return m_value; } + int startPos() const { return m_startPos; } + int endPos() const { return m_endPos; } + QByteArray mask() const; + + bool isValid() const { return m_matchFunction != nullptr; } + + bool matches(const QByteArray &data) const; + + QList<MimeMagicRule> m_subMatches; + + static Type type(const QByteArray &type); + static QByteArray typeName(Type type); + + static bool matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, int valueLength, const char *valueData, const char *mask); + +private: + // added for Qt Creator + void init(QString *errorString); + + Type m_type; + QByteArray m_value; + int m_startPos; + int m_endPos; + QByteArray m_mask; + + QRegularExpression m_regexp; + QByteArray m_pattern; + quint32 m_number; + quint32 m_numberMask; + + typedef bool (MimeMagicRule::*MatchFunction)(const QByteArray &data) const; + MatchFunction m_matchFunction; + +private: + // match functions + bool matchString(const QByteArray &data) const; + template <typename T> + bool matchNumber(const QByteArray &data) const; + bool matchRegExp(const QByteArray &data) const; +}; + +} // namespace Utils + +QT_BEGIN_NAMESPACE +Q_DECLARE_SHARED(Utils::MimeMagicRule) +QT_END_NAMESPACE diff --git a/src/libs/utils/mimetypes2/mimemagicrulematcher.cpp b/src/libs/utils/mimetypes2/mimemagicrulematcher.cpp new file mode 100644 index 00000000000..a624efefa2f --- /dev/null +++ b/src/libs/utils/mimetypes2/mimemagicrulematcher.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimemagicrulematcher_p.h" + +#include "mimetype_p.h" + +namespace Utils { + +/*! + \internal + \class MimeMagicRuleMatcher + \inmodule QtCore + + \brief The MimeMagicRuleMatcher class checks a number of rules based on operator "or". + + It is used for rules parsed from XML files. + + \sa MimeType, MimeDatabase, MagicRule, MagicStringRule, MagicByteRule, GlobPattern + \sa MimeTypeParserBase, MimeTypeParser +*/ + +MimeMagicRuleMatcher::MimeMagicRuleMatcher(const QString &mime, unsigned thePriority) : + m_list(), + m_priority(thePriority), + m_mimetype(mime) +{ +} + +bool MimeMagicRuleMatcher::operator==(const MimeMagicRuleMatcher &other) const +{ + return m_list == other.m_list && + m_priority == other.m_priority; +} + +void MimeMagicRuleMatcher::addRule(const MimeMagicRule &rule) +{ + m_list.append(rule); +} + +void MimeMagicRuleMatcher::addRules(const QList<MimeMagicRule> &rules) +{ + m_list.append(rules); +} + +QList<MimeMagicRule> MimeMagicRuleMatcher::magicRules() const +{ + return m_list; +} + +// Check for a match on contents of a file +bool MimeMagicRuleMatcher::matches(const QByteArray &data) const +{ + for (const MimeMagicRule &magicRule : m_list) { + if (magicRule.matches(data)) + return true; + } + + return false; +} + +// Return a priority value from 1..100 +unsigned MimeMagicRuleMatcher::priority() const +{ + return m_priority; +} + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimemagicrulematcher_p.h b/src/libs/utils/mimetypes2/mimemagicrulematcher_p.h new file mode 100644 index 00000000000..4a4cd1f67f6 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimemagicrulematcher_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "mimemagicrule_p.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qlist.h> +#include <QtCore/qstring.h> + +namespace Utils { + +class MimeMagicRuleMatcher +{ +public: + explicit MimeMagicRuleMatcher(const QString &mime, unsigned priority = 65535); + + void swap(MimeMagicRuleMatcher &other) noexcept + { + qSwap(m_list, other.m_list); + qSwap(m_priority, other.m_priority); + qSwap(m_mimetype, other.m_mimetype); + } + + bool operator==(const MimeMagicRuleMatcher &other) const; + + void addRule(const MimeMagicRule &rule); + void addRules(const QList<MimeMagicRule> &rules); + QList<MimeMagicRule> magicRules() const; + + bool matches(const QByteArray &data) const; + + unsigned priority() const; + + QString mimetype() const { return m_mimetype; } + +private: + QList<MimeMagicRule> m_list; + unsigned m_priority; + QString m_mimetype; +}; + +} // namespace Utils + +QT_BEGIN_NAMESPACE +Q_DECLARE_SHARED(Utils::MimeMagicRuleMatcher) +QT_END_NAMESPACE diff --git a/src/libs/utils/mimetypes2/mimeprovider.cpp b/src/libs/utils/mimetypes2/mimeprovider.cpp new file mode 100644 index 00000000000..5d8c4c134ef --- /dev/null +++ b/src/libs/utils/mimetypes2/mimeprovider.cpp @@ -0,0 +1,1061 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected], author David Faure <[email protected]> +** Copyright (C) 2019 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimeprovider_p.h" + +#include "mimetypeparser_p.h" +#include "mimemagicrulematcher_p.h" + +#include <qstandardpaths.h> +#include <QXmlStreamReader> +#include <QBuffer> +#include <QDir> +#include <QFile> +#include <QByteArrayMatcher> +#include <QDebug> +#include <QDateTime> +#include <QtEndian> + +#if 0 // QT_CONFIG(mimetype_database) +# if defined(Q_CC_MSVC) +# pragma section(".qtmimedatabase", read, shared) +__declspec(allocate(".qtmimedatabase")) __declspec(align(4096)) +# elif defined(Q_OS_DARWIN) +__attribute__((section("__TEXT,.qtmimedatabase"), aligned(4096))) +# elif (defined(Q_OF_ELF) || defined(Q_OS_WIN)) && defined(Q_CC_GNU) +__attribute__((section(".qtmimedatabase"), aligned(4096))) +# endif + +# include "qmimeprovider_database.cpp" + +# ifdef MIME_DATABASE_IS_ZSTD +# if !QT_CONFIG(zstd) +# error "MIME database is zstd but no support compiled in!" +# endif +# include <zstd.h> +# endif +# ifdef MIME_DATABASE_IS_GZIP +# ifdef QT_NO_COMPRESS +# error "MIME database is zlib but no support compiled in!" +# endif +# define ZLIB_CONST +# include <zconf.h> +# include <zlib.h> +# endif +#endif + +namespace Utils { + +MimeProviderBase::MimeProviderBase(MimeDatabasePrivate *db, const QString &directory) + : m_db(db), m_directory(directory) +{ +} + + +MimeBinaryProvider::MimeBinaryProvider(MimeDatabasePrivate *db, const QString &directory) + : MimeProviderBase(db, directory), m_mimetypeListLoaded(false) +{ + ensureLoaded(); +} + +struct MimeBinaryProvider::CacheFile +{ + CacheFile(const QString &fileName); + ~CacheFile(); + + bool isValid() const { return m_valid; } + inline quint16 getUint16(int offset) const + { + return qFromBigEndian(*reinterpret_cast<quint16 *>(data + offset)); + } + inline quint32 getUint32(int offset) const + { + return qFromBigEndian(*reinterpret_cast<quint32 *>(data + offset)); + } + inline const char *getCharStar(int offset) const + { + return reinterpret_cast<const char *>(data + offset); + } + bool load(); + bool reload(); + + QFile file; + uchar *data; + QDateTime m_mtime; + bool m_valid; +}; + +MimeBinaryProvider::CacheFile::CacheFile(const QString &fileName) + : file(fileName), m_valid(false) +{ + load(); +} + +MimeBinaryProvider::CacheFile::~CacheFile() +{ +} + +bool MimeBinaryProvider::CacheFile::load() +{ + if (!file.open(QIODevice::ReadOnly)) + return false; + data = file.map(0, file.size()); + if (data) { + const int major = getUint16(0); + const int minor = getUint16(2); + m_valid = (major == 1 && minor >= 1 && minor <= 2); + } + m_mtime = QFileInfo(file).lastModified(); + return m_valid; +} + +bool MimeBinaryProvider::CacheFile::reload() +{ + m_valid = false; + if (file.isOpen()) { + file.close(); + } + data = nullptr; + return load(); +} + +MimeBinaryProvider::~MimeBinaryProvider() +{ + delete m_cacheFile; +} + +bool MimeBinaryProvider::isValid() +{ + return m_cacheFile != nullptr; +} + +bool MimeBinaryProvider::isInternalDatabase() const +{ + return false; +} + +// Position of the "list offsets" values, at the beginning of the mime.cache file +enum { + PosAliasListOffset = 4, + PosParentListOffset = 8, + PosLiteralListOffset = 12, + PosReverseSuffixTreeOffset = 16, + PosGlobListOffset = 20, + PosMagicListOffset = 24, + // PosNamespaceListOffset = 28, + PosIconsListOffset = 32, + PosGenericIconsListOffset = 36 +}; + +bool MimeBinaryProvider::checkCacheChanged() +{ + QFileInfo fileInfo(m_cacheFile->file); + if (fileInfo.lastModified() > m_cacheFile->m_mtime) { + // Deletion can't happen by just running update-mime-database. + // But the user could use rm -rf :-) + m_cacheFile->reload(); // will mark itself as invalid on failure + return true; + } + return false; +} + +void MimeBinaryProvider::ensureLoaded() +{ + if (!m_cacheFile) { + const QString cacheFileName = m_directory + QLatin1String("/mime.cache"); + m_cacheFile = new CacheFile(cacheFileName); + m_mimetypeListLoaded = false; + m_mimetypeExtra.clear(); + } else { + if (checkCacheChanged()) { + m_mimetypeListLoaded = false; + m_mimetypeExtra.clear(); + } else { + return; // nothing to do + } + } + if (!m_cacheFile->isValid()) { // verify existence and version + delete m_cacheFile; + m_cacheFile = nullptr; + } +} + +static MimeType mimeTypeForNameUnchecked(const QString &name) +{ + MimeTypePrivate data; + data.name = name; + data.fromCache = true; + // The rest is retrieved on demand. + // comment and globPatterns: in loadMimeTypePrivate + // iconName: in loadIcon + // genericIconName: in loadGenericIcon + return MimeType(data); +} + +MimeType MimeBinaryProvider::mimeTypeForName(const QString &name) +{ + if (!m_mimetypeListLoaded) + loadMimeTypeList(); + if (!m_mimetypeNames.contains(name)) + return MimeType(); // unknown mimetype + return mimeTypeForNameUnchecked(name); +} + +void MimeBinaryProvider::addFileNameMatches(const QString &fileName, + MimeGlobMatchResult &result, + QList<QString> &checkedMimeTypes) +{ + // TODO checkedMimeTypes + if (fileName.isEmpty()) + return; + Q_ASSERT(m_cacheFile); + const QString lowerFileName = fileName.toLower(); + // Check literals (e.g. "Makefile") + matchGlobList(result, + m_cacheFile, + m_cacheFile->getUint32(PosLiteralListOffset), + fileName, + checkedMimeTypes); + // Check the very common *.txt cases with the suffix tree + if (result.m_matchingMimeTypes.isEmpty()) { + const int reverseSuffixTreeOffset = m_cacheFile->getUint32(PosReverseSuffixTreeOffset); + const int numRoots = m_cacheFile->getUint32(reverseSuffixTreeOffset); + const int firstRootOffset = m_cacheFile->getUint32(reverseSuffixTreeOffset + 4); + matchSuffixTree(result, + m_cacheFile, + numRoots, + firstRootOffset, + lowerFileName, + lowerFileName.length() - 1, + false, + checkedMimeTypes); + if (result.m_matchingMimeTypes.isEmpty()) + matchSuffixTree(result, + m_cacheFile, + numRoots, + firstRootOffset, + fileName, + fileName.length() - 1, + true, + checkedMimeTypes); + } + // Check complex globs (e.g. "callgrind.out[0-9]*" or "README*") + if (result.m_matchingMimeTypes.isEmpty()) + matchGlobList(result, + m_cacheFile, + m_cacheFile->getUint32(PosGlobListOffset), + fileName, + checkedMimeTypes); + // add all mime types from this provider to checkedMimeTypes, so they + // don't get checked again by another provider (if this provider overrides + // a mime type from another provider) + addAllMimeTypeNames(checkedMimeTypes); +} + +void MimeBinaryProvider::matchGlobList(MimeGlobMatchResult &result, + CacheFile *cacheFile, + int off, + const QString &fileName, + const QList<QString> &ignoreMimeTypes) +{ + const int numGlobs = cacheFile->getUint32(off); + //qDebug() << "Loading" << numGlobs << "globs from" << cacheFile->file.fileName() << "at offset" << cacheFile->globListOffset; + for (int i = 0; i < numGlobs; ++i) { + const int globOffset = cacheFile->getUint32(off + 4 + 12 * i); + const int mimeTypeOffset = cacheFile->getUint32(off + 4 + 12 * i + 4); + const int flagsAndWeight = cacheFile->getUint32(off + 4 + 12 * i + 8); + const int weight = flagsAndWeight & 0xff; + const bool caseSensitive = flagsAndWeight & 0x100; + const Qt::CaseSensitivity qtCaseSensitive = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + const QString pattern = QLatin1String(cacheFile->getCharStar(globOffset)); + + const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); + if (ignoreMimeTypes.contains(QLatin1String(mimeType))) + continue; + //qDebug() << pattern << mimeType << weight << caseSensitive; + MimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive); + + if (glob.matchFileName(fileName)) + result.addMatch(QLatin1String(mimeType), weight, pattern); + } +} + +bool MimeBinaryProvider::matchSuffixTree(MimeGlobMatchResult &result, + MimeBinaryProvider::CacheFile *cacheFile, + int numEntries, + int firstOffset, + const QString &fileName, + int charPos, + bool caseSensitiveCheck, + const QList<QString> &ignoreMimeTypes) +{ + QChar fileChar = fileName[charPos]; + int min = 0; + int max = numEntries - 1; + while (min <= max) { + const int mid = (min + max) / 2; + const int off = firstOffset + 12 * mid; + const QChar ch = char16_t(cacheFile->getUint32(off)); + if (ch < fileChar) + min = mid + 1; + else if (ch > fileChar) + max = mid - 1; + else { + --charPos; + int numChildren = cacheFile->getUint32(off + 4); + int childrenOffset = cacheFile->getUint32(off + 8); + bool success = false; + if (charPos > 0) + success = matchSuffixTree(result, + cacheFile, + numChildren, + childrenOffset, + fileName, + charPos, + caseSensitiveCheck, + ignoreMimeTypes); + if (!success) { + for (int i = 0; i < numChildren; ++i) { + const int childOff = childrenOffset + 12 * i; + const int mch = cacheFile->getUint32(childOff); + if (mch != 0) + break; + const int mimeTypeOffset = cacheFile->getUint32(childOff + 4); + const char *mimeType = cacheFile->getCharStar(mimeTypeOffset); + if (ignoreMimeTypes.contains(QLatin1String(mimeType))) + continue; + const int flagsAndWeight = cacheFile->getUint32(childOff + 8); + const int weight = flagsAndWeight & 0xff; + const bool caseSensitive = flagsAndWeight & 0x100; + if (caseSensitiveCheck || !caseSensitive) { + result.addMatch(QLatin1String(mimeType), weight, + QLatin1Char('*') + QStringView{fileName}.mid(charPos + 1), fileName.size() - charPos - 2); + success = true; + } + } + } + return success; + } + } + return false; +} + +bool MimeBinaryProvider::matchMagicRule(MimeBinaryProvider::CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data) +{ + const char *dataPtr = data.constData(); + const int dataSize = data.size(); + for (int matchlet = 0; matchlet < numMatchlets; ++matchlet) { + const int off = firstOffset + matchlet * 32; + const int rangeStart = cacheFile->getUint32(off); + const int rangeLength = cacheFile->getUint32(off + 4); + //const int wordSize = cacheFile->getUint32(off + 8); + const int valueLength = cacheFile->getUint32(off + 12); + const int valueOffset = cacheFile->getUint32(off + 16); + const int maskOffset = cacheFile->getUint32(off + 20); + const char *mask = maskOffset ? cacheFile->getCharStar(maskOffset) : nullptr; + + if (!MimeMagicRule::matchSubstring(dataPtr, dataSize, rangeStart, rangeLength, valueLength, cacheFile->getCharStar(valueOffset), mask)) + continue; + + const int numChildren = cacheFile->getUint32(off + 24); + const int firstChildOffset = cacheFile->getUint32(off + 28); + if (numChildren == 0) // No submatch? Then we are done. + return true; + // Check that one of the submatches matches too + if (matchMagicRule(cacheFile, numChildren, firstChildOffset, data)) + return true; + } + return false; +} + +void MimeBinaryProvider::findByMagic(const QByteArray &data, + int *accuracyPtr, + MimeType &candidate, + QList<QString> &checkedMimeTypes) +{ + const int magicListOffset = m_cacheFile->getUint32(PosMagicListOffset); + const int numMatches = m_cacheFile->getUint32(magicListOffset); + //const int maxExtent = cacheFile->getUint32(magicListOffset + 4); + const int firstMatchOffset = m_cacheFile->getUint32(magicListOffset + 8); + + for (int i = 0; i < numMatches; ++i) { + const int off = firstMatchOffset + i * 16; + const int numMatchlets = m_cacheFile->getUint32(off + 8); + const int firstMatchletOffset = m_cacheFile->getUint32(off + 12); + if (matchMagicRule(m_cacheFile, numMatchlets, firstMatchletOffset, data)) { + const int mimeTypeOffset = m_cacheFile->getUint32(off + 4); + const char *mimeType = m_cacheFile->getCharStar(mimeTypeOffset); + if (checkedMimeTypes.contains(QLatin1String(mimeType))) + continue; + *accuracyPtr = m_cacheFile->getUint32(off); + // Return the first match. We have no rules for conflicting magic data... + // (mime.cache itself is sorted, but what about local overrides with a lower prio?) + candidate = mimeTypeForNameUnchecked(QLatin1String(mimeType)); + return; + } + } + // add all mime types from this provider to checkedMimeTypes, so they + // don't get checked again by another provider (if this provider overrides + // a mime type from another provider) + addAllMimeTypeNames(checkedMimeTypes); +} + +void MimeBinaryProvider::addParents(const QString &mime, QStringList &result) +{ + const QByteArray mimeStr = mime.toLatin1(); + const int parentListOffset = m_cacheFile->getUint32(PosParentListOffset); + const int numEntries = m_cacheFile->getUint32(parentListOffset); + + int begin = 0; + int end = numEntries - 1; + while (begin <= end) { + const int medium = (begin + end) / 2; + const int off = parentListOffset + 4 + 8 * medium; + const int mimeOffset = m_cacheFile->getUint32(off); + const char *aMime = m_cacheFile->getCharStar(mimeOffset); + const int cmp = qstrcmp(aMime, mimeStr); + if (cmp < 0) { + begin = medium + 1; + } else if (cmp > 0) { + end = medium - 1; + } else { + const int parentsOffset = m_cacheFile->getUint32(off + 4); + const int numParents = m_cacheFile->getUint32(parentsOffset); + for (int i = 0; i < numParents; ++i) { + const int parentOffset = m_cacheFile->getUint32(parentsOffset + 4 + 4 * i); + const char *aParent = m_cacheFile->getCharStar(parentOffset); + const QString strParent = QString::fromLatin1(aParent); + if (!result.contains(strParent)) + result.append(strParent); + } + break; + } + } +} + +QString MimeBinaryProvider::resolveAlias(const QString &name) +{ + const QByteArray input = name.toLatin1(); + const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset); + const int numEntries = m_cacheFile->getUint32(aliasListOffset); + int begin = 0; + int end = numEntries - 1; + while (begin <= end) { + const int medium = (begin + end) / 2; + const int off = aliasListOffset + 4 + 8 * medium; + const int aliasOffset = m_cacheFile->getUint32(off); + const char *alias = m_cacheFile->getCharStar(aliasOffset); + const int cmp = qstrcmp(alias, input); + if (cmp < 0) { + begin = medium + 1; + } else if (cmp > 0) { + end = medium - 1; + } else { + const int mimeOffset = m_cacheFile->getUint32(off + 4); + const char *mimeType = m_cacheFile->getCharStar(mimeOffset); + return QLatin1String(mimeType); + } + } + return QString(); +} + +void MimeBinaryProvider::addAliases(const QString &name, QStringList &result) +{ + const QByteArray input = name.toLatin1(); + const int aliasListOffset = m_cacheFile->getUint32(PosAliasListOffset); + const int numEntries = m_cacheFile->getUint32(aliasListOffset); + for (int pos = 0; pos < numEntries; ++pos) { + const int off = aliasListOffset + 4 + 8 * pos; + const int mimeOffset = m_cacheFile->getUint32(off + 4); + const char *mimeType = m_cacheFile->getCharStar(mimeOffset); + + if (input == mimeType) { + const int aliasOffset = m_cacheFile->getUint32(off); + const char *alias = m_cacheFile->getCharStar(aliasOffset); + const QString strAlias = QString::fromLatin1(alias); + if (!result.contains(strAlias)) + result.append(strAlias); + } + } +} + +void MimeBinaryProvider::loadMimeTypeList() +{ + if (!m_mimetypeListLoaded) { + m_mimetypeListLoaded = true; + m_mimetypeNames.clear(); + // Unfortunately mime.cache doesn't have a full list of all mimetypes. + // So we have to parse the plain-text files called "types". + QFile file(m_directory + QStringLiteral("/types")); + if (file.open(QIODevice::ReadOnly)) { + while (!file.atEnd()) { + QByteArray line = file.readLine(); + if (line.endsWith('\n')) + line.chop(1); + m_mimetypeNames.insert(QString::fromLatin1(line)); + } + } + } +} + +void MimeBinaryProvider::addAllMimeTypes(QList<MimeType> &result) +{ + loadMimeTypeList(); + if (result.isEmpty()) { + result.reserve(m_mimetypeNames.count()); + for (const QString &name : qAsConst(m_mimetypeNames)) + result.append(mimeTypeForNameUnchecked(name)); + } else { + for (const QString &name : qAsConst(m_mimetypeNames)) + if (std::find_if(result.constBegin(), result.constEnd(), [name](const MimeType &mime) -> bool { return mime.name() == name; }) + == result.constEnd()) + result.append(mimeTypeForNameUnchecked(name)); + } +} + +bool MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) +{ +#ifdef QT_NO_XMLSTREAMREADER + Q_UNUSED(data); + qWarning("Cannot load mime type since QXmlStreamReader is not available."); + return false; +#else + if (data.loaded) + return true; + + auto it = m_mimetypeExtra.constFind(data.name); + if (it == m_mimetypeExtra.constEnd()) { + // load comment and globPatterns + + // shared-mime-info since 1.3 lowercases the xml files + QString mimeFile = m_directory + QLatin1Char('/') + data.name.toLower() + QLatin1String(".xml"); + if (!QFile::exists(mimeFile)) + mimeFile = m_directory + QLatin1Char('/') + data.name + QLatin1String(".xml"); // pre-1.3 + + QFile qfile(mimeFile); + if (!qfile.open(QFile::ReadOnly)) + return false; + + auto insertIt = m_mimetypeExtra.insert(data.name, MimeTypeExtra{}); + it = insertIt; + MimeTypeExtra &extra = insertIt.value(); + QString mainPattern; + + QXmlStreamReader xml(&qfile); + if (xml.readNextStartElement()) { + if (xml.name() != QLatin1String("mime-type")) { + return false; + } + const auto name = xml.attributes().value(QLatin1String("type")); + if (name.isEmpty()) + return false; + if (name.compare(data.name, Qt::CaseInsensitive)) + qWarning() << "Got name" << name << "in file" << mimeFile << "expected" << data.name; + + while (xml.readNextStartElement()) { + const auto tag = xml.name(); + if (tag == QLatin1String("comment")) { + QString lang = xml.attributes().value(QLatin1String("xml:lang")).toString(); + const QString text = xml.readElementText(); + if (lang.isEmpty()) { + lang = QLatin1String("default"); // no locale attribute provided, treat it as default. + } + extra.localeComments.insert(lang, text); + continue; // we called readElementText, so we're at the EndElement already. + } else if (tag == QLatin1String("glob-deleteall")) { // as written out by shared-mime-info >= 0.70 + extra.globPatterns.clear(); + mainPattern.clear(); + } else if (tag == QLatin1String("glob")) { // as written out by shared-mime-info >= 0.70 + const QString pattern = xml.attributes().value(QLatin1String("pattern")).toString(); + if (mainPattern.isEmpty() && pattern.startsWith(QLatin1Char('*'))) { + mainPattern = pattern; + } + if (!extra.globPatterns.contains(pattern)) + extra.globPatterns.append(pattern); + } + xml.skipCurrentElement(); + } + Q_ASSERT(xml.name() == QLatin1String("mime-type")); + } + + // Let's assume that shared-mime-info is at least version 0.70 + // Otherwise we would need 1) a version check, and 2) code for parsing patterns from the globs file. + if (!mainPattern.isEmpty() && + (extra.globPatterns.isEmpty() || extra.globPatterns.constFirst() != mainPattern)) { + // ensure it's first in the list of patterns + extra.globPatterns.removeAll(mainPattern); + extra.globPatterns.prepend(mainPattern); + } + } + const MimeTypeExtra &e = it.value(); + data.localeComments = e.localeComments; + data.globPatterns = e.globPatterns; + return true; +#endif //QT_NO_XMLSTREAMREADER +} + +// Binary search in the icons or generic-icons list +QLatin1String MimeBinaryProvider::iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime) +{ + const int iconsListOffset = cacheFile->getUint32(posListOffset); + const int numIcons = cacheFile->getUint32(iconsListOffset); + int begin = 0; + int end = numIcons - 1; + while (begin <= end) { + const int medium = (begin + end) / 2; + const int off = iconsListOffset + 4 + 8 * medium; + const int mimeOffset = cacheFile->getUint32(off); + const char *mime = cacheFile->getCharStar(mimeOffset); + const int cmp = qstrcmp(mime, inputMime); + if (cmp < 0) + begin = medium + 1; + else if (cmp > 0) + end = medium - 1; + else { + const int iconOffset = cacheFile->getUint32(off + 4); + return QLatin1String(cacheFile->getCharStar(iconOffset)); + } + } + return QLatin1String(); +} + +void MimeBinaryProvider::loadIcon(MimeTypePrivate &data) +{ + const QByteArray inputMime = data.name.toLatin1(); + const QLatin1String icon = iconForMime(m_cacheFile, PosIconsListOffset, inputMime); + if (!icon.isEmpty()) { + data.iconName = icon; + } +} + +void MimeBinaryProvider::loadGenericIcon(MimeTypePrivate &data) +{ + const QByteArray inputMime = data.name.toLatin1(); + const QLatin1String icon = iconForMime(m_cacheFile, PosGenericIconsListOffset, inputMime); + if (!icon.isEmpty()) { + data.genericIconName = icon; + } +} + +//// + +#if 0 +#if QT_CONFIG(mimetype_database) +static QString internalMimeFileName() +{ + return QStringLiteral("<internal MIME data>"); +} + +QMimeXMLProvider::QMimeXMLProvider(QMimeDatabasePrivate *db, InternalDatabaseEnum) + : QMimeProviderBase(db, internalMimeFileName()) +{ + static_assert(sizeof(mimetype_database), "Bundled MIME database is empty"); + static_assert(sizeof(mimetype_database) <= MimeTypeDatabaseOriginalSize, + "Compressed MIME database is larger than the original size"); + static_assert(MimeTypeDatabaseOriginalSize <= 16*1024*1024, + "Bundled MIME database is too big"); + const char *data = reinterpret_cast<const char *>(mimetype_database); + qsizetype size = MimeTypeDatabaseOriginalSize; + +#ifdef MIME_DATABASE_IS_ZSTD + // uncompress with libzstd + std::unique_ptr<char []> uncompressed(new char[size]); + size = ZSTD_decompress(uncompressed.get(), size, mimetype_database, sizeof(mimetype_database)); + Q_ASSERT(!ZSTD_isError(size)); + data = uncompressed.get(); +#elif defined(MIME_DATABASE_IS_GZIP) + std::unique_ptr<char []> uncompressed(new char[size]); + z_stream zs = {}; + zs.next_in = const_cast<Bytef *>(mimetype_database); + zs.avail_in = sizeof(mimetype_database); + zs.next_out = reinterpret_cast<Bytef *>(uncompressed.get()); + zs.avail_out = size; + + int res = inflateInit2(&zs, MAX_WBITS | 32); + Q_ASSERT(res == Z_OK); + res = inflate(&zs, Z_FINISH); + Q_ASSERT(res == Z_STREAM_END); + res = inflateEnd(&zs); + Q_ASSERT(res == Z_OK); + + data = uncompressed.get(); + size = zs.total_out; +#endif + + load(data, size); +} +#else // !QT_CONFIG(mimetype_database) +// never called in release mode, but some debug builds may need +// this to be defined. +MimeXMLProvider::MimeXMLProvider(MimeDatabasePrivate *db, InternalDatabaseEnum) + : MimeProviderBase(db, QString()) +{ + Q_UNREACHABLE(); +} +#endif // QT_CONFIG(mimetype_database) +#endif + +static const char internalMimeFileName[] = ":/utils/mimetypes/freedesktop.org.xml"; + +// for Qt Creator: internal database from resources +MimeXMLProvider::MimeXMLProvider(MimeDatabasePrivate *db, InternalDatabaseEnum) + : MimeProviderBase(db, internalMimeFileName) +{ + load(internalMimeFileName); +} + +MimeXMLProvider::MimeXMLProvider(MimeDatabasePrivate *db, const QString &directory) + : MimeProviderBase(db, directory) +{ + ensureLoaded(); +} + +MimeXMLProvider::~MimeXMLProvider() +{ +} + +bool MimeXMLProvider::isValid() +{ + // If you change this method, adjust the logic in MimeDatabasePrivate::loadProviders, + // which assumes isValid==false is only possible in MimeBinaryProvider. + return true; +} + +bool MimeXMLProvider::isInternalDatabase() const +{ +#if 1 //QT_CONFIG(mimetype_database) + return m_directory == internalMimeFileName; +#else + return false; +#endif +} + +MimeType MimeXMLProvider::mimeTypeForName(const QString &name) +{ + return m_nameMimeTypeMap.value(name); +} + +void MimeXMLProvider::addFileNameMatches(const QString &fileName, + MimeGlobMatchResult &result, + QList<QString> &checkedMimeTypes) +{ + m_mimeTypeGlobs.matchingGlobs(fileName, result, checkedMimeTypes); + // add all mime types from this provider to checkedMimeTypes, so they + // don't get checked again by another provider (if this provider overrides + // a mime type from another provider) + addAllMimeTypeNames(checkedMimeTypes); +} + +void MimeXMLProvider::findByMagic(const QByteArray &data, + int *accuracyPtr, + MimeType &candidate, + QList<QString> &checkedMimeTypes) +{ + QString candidateName; + bool foundOne = false; + for (const MimeMagicRuleMatcher &matcher : qAsConst(m_magicMatchers)) { + if (checkedMimeTypes.contains(matcher.mimetype())) + continue; + if (matcher.matches(data)) { + const int priority = matcher.priority(); + if (priority > *accuracyPtr) { + *accuracyPtr = priority; + candidateName = matcher.mimetype(); + foundOne = true; + } + } + } + if (foundOne) + candidate = mimeTypeForName(candidateName); + // add all mime types from this provider to checkedMimeTypes, so they + // don't get checked again by another provider (if this provider overrides + // a mime type from another provider) + addAllMimeTypeNames(checkedMimeTypes); +} + +void MimeXMLProvider::ensureLoaded() +{ + QStringList allFiles; + const QString packageDir = m_directory + QStringLiteral("/packages"); + QDir dir(packageDir); + const QStringList files = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); + allFiles.reserve(files.count()); + for (const QString &xmlFile : files) + allFiles.append(packageDir + QLatin1Char('/') + xmlFile); + + if (m_allFiles == allFiles) + return; + m_allFiles = allFiles; + + m_nameMimeTypeMap.clear(); + m_aliases.clear(); + m_parents.clear(); + m_mimeTypeGlobs.clear(); + m_magicMatchers.clear(); + + //qDebug() << "Loading" << m_allFiles; + + for (const QString &file : qAsConst(allFiles)) + load(file); +} + +void MimeXMLProvider::load(const QString &fileName) +{ + QString errorMessage; + if (!load(fileName, &errorMessage)) + qWarning("MimeDatabase: Error loading %ls\n%ls", qUtf16Printable(fileName), qUtf16Printable(errorMessage)); +} + +bool MimeXMLProvider::load(const QString &fileName, QString *errorMessage) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (errorMessage) + *errorMessage = QLatin1String("Cannot open ") + fileName + QLatin1String(": ") + file.errorString(); + return false; + } + + if (errorMessage) + errorMessage->clear(); + + MimeTypeParser parser(*this); + return parser.parse(&file, fileName, errorMessage); +} + +#if 0 //QT_CONFIG(mimetype_database) +void MimeXMLProvider::load(const char *data, qsizetype len) +{ + QBuffer buffer; + buffer.setData(QByteArray::fromRawData(data, len)); + buffer.open(QIODevice::ReadOnly); + QString errorMessage; + MimeTypeParser parser(*this); + if (!parser.parse(&buffer, internalMimeFileName(), &errorMessage)) + qWarning("MimeDatabase: Error loading internal MIME data\n%s", qPrintable(errorMessage)); +} +#endif + +void MimeXMLProvider::addGlobPattern(const MimeGlobPattern &glob) +{ + m_mimeTypeGlobs.addGlob(glob); +} + +void MimeXMLProvider::addMimeType(const MimeType &mt) +{ + Q_ASSERT(!mt.d.data()->fromCache); + m_nameMimeTypeMap.insert(mt.name(), mt); +} + +void MimeXMLProvider::addParents(const QString &mime, QStringList &result) +{ + for (const QString &parent : m_parents.value(mime)) { + if (!result.contains(parent)) + result.append(parent); + } +} + +void MimeXMLProvider::addParent(const QString &child, const QString &parent) +{ + m_parents[child].append(parent); +} + +void MimeXMLProvider::addAliases(const QString &name, QStringList &result) +{ + // Iterate through the whole hash. This method is rarely used. + for (auto it = m_aliases.constBegin(), end = m_aliases.constEnd() ; it != end ; ++it) { + if (it.value() == name) { + if (!result.contains(it.key())) + result.append(it.key()); + } + } + +} + +QString MimeXMLProvider::resolveAlias(const QString &name) +{ + return m_aliases.value(name); +} + +void MimeXMLProvider::addAlias(const QString &alias, const QString &name) +{ + m_aliases.insert(alias, name); +} + +void MimeXMLProvider::addAllMimeTypes(QList<MimeType> &result) +{ + if (result.isEmpty()) { // fast path + result = m_nameMimeTypeMap.values(); + } else { + for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd() ; it != end ; ++it) { + const QString newMime = it.key(); + if (std::find_if(result.constBegin(), result.constEnd(), [newMime](const MimeType &mime) -> bool { return mime.name() == newMime; }) + == result.constEnd()) + result.append(it.value()); + } + } +} + +void MimeXMLProvider::addMagicMatcher(const MimeMagicRuleMatcher &matcher) +{ + m_magicMatchers.append(matcher); +} + +// added for Qt Creator +MimeXMLProvider::MimeXMLProvider(MimeDatabasePrivate *db, + const QString &directory, + const QByteArray &data) + : MimeProviderBase(db, directory) +{ + QString errorMessage; + MimeTypeParser parser(*this); + QByteArray bufferData(data); + QBuffer buffer(&bufferData); + if (!buffer.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning("MimeDatabase: Error creating buffer for data with ID %ls.", + qUtf16Printable(directory)); + } + if (!parser.parse(&buffer, directory, &errorMessage)) { + qWarning("MimeDatabase: Error loading data for ID %ls\n%ls", + qUtf16Printable(directory), + qUtf16Printable(errorMessage)); + } +} + +bool MimeBinaryProvider::hasMimeTypeForName(const QString &name) +{ + loadMimeTypeList(); + return m_mimetypeNames.contains(name); +} + +void MimeBinaryProvider::addAllMimeTypeNames(QList<QString> &result) +{ + // similar to addAllMimeTypes + loadMimeTypeList(); + if (result.isEmpty()) { // fast path + result = QList(m_mimetypeNames.cbegin(), m_mimetypeNames.cend()); + } else { + for (const QString &name : qAsConst(m_mimetypeNames)) + if (!result.contains(name)) + result.append(name); + } +} + +bool MimeXMLProvider::hasMimeTypeForName(const QString &name) +{ + return m_nameMimeTypeMap.contains(name); +} + +void MimeXMLProvider::addAllMimeTypeNames(QList<QString> &result) +{ + // similar to addAllMimeTypes + if (result.isEmpty()) { // fast path + result = m_nameMimeTypeMap.keys(); + } else { + for (auto it = m_nameMimeTypeMap.constBegin(), end = m_nameMimeTypeMap.constEnd(); + it != end; + ++it) { + const QString newMime = it.key(); + if (!result.contains(newMime)) + result.append(newMime); + } + } +} + +QMap<int, QList<MimeMagicRule>> MimeBinaryProvider::magicRulesForMimeType(const MimeType &mimeType) const +{ + qWarning("Mimetypes: magicRulesForMimeType not implemented for binary provider"); + return {}; +} + +void MimeBinaryProvider::setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules) +{ + qWarning("Mimetypes: setMagicRulesForMimeType not implemented for binary provider"); +} + +void MimeBinaryProvider::setGlobPatternsForMimeType(const MimeType &mimeType, + const QStringList &patterns) +{ + qWarning("Mimetypes: setGlobPatternsForMimeType not implemented for binary provider"); +} + +QMap<int, QList<MimeMagicRule>> MimeXMLProvider::magicRulesForMimeType(const MimeType &mimeType) const +{ + QMap<int, QList<MimeMagicRule>> result; + for (const MimeMagicRuleMatcher &matcher : m_magicMatchers) { + if (mimeType.name() == matcher.mimetype()) + result[matcher.priority()].append(matcher.magicRules()); + } + return result; +} + +void MimeXMLProvider::setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules) +{ + // remove previous rules + m_magicMatchers.erase(std::remove_if(m_magicMatchers.begin(), + m_magicMatchers.end(), + [mimeType](const MimeMagicRuleMatcher &matcher) { + return mimeType.name() == matcher.mimetype(); + }), + m_magicMatchers.end()); + // add new rules + for (auto it = rules.cbegin(); it != rules.cend(); ++it) { + MimeMagicRuleMatcher matcher(mimeType.name(), it.key() /*priority*/); + matcher.addRules(it.value()); + addMagicMatcher(matcher); + } +} + +void MimeXMLProvider::setGlobPatternsForMimeType(const MimeType &mimeType, + const QStringList &patterns) +{ + // remove all previous globs + m_mimeTypeGlobs.removeMimeType(mimeType.name()); + // add new patterns as case-insensitive default-weight patterns + for (const QString &pattern : patterns) + addGlobPattern(MimeGlobPattern(pattern, mimeType.name())); + // the following is safe, because for XML provider mimetype private is always "loaded" + // (see comment in MimeDatabasePrivate::loadMimeTypePrivate) + mimeType.d->globPatterns = patterns; +} + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimeprovider_p.h b/src/libs/utils/mimetypes2/mimeprovider_p.h new file mode 100644 index 00000000000..dddef04393c --- /dev/null +++ b/src/libs/utils/mimetypes2/mimeprovider_p.h @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected], author David Faure <[email protected]> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "mimedatabase_p.h" + +#include "mimeglobpattern_p.h" +#include <QtCore/qdatetime.h> +#include <QtCore/qset.h> + +namespace Utils { + +class MimeMagicRuleMatcher; + +class MimeProviderBase +{ +public: + MimeProviderBase(MimeDatabasePrivate *db, const QString &directory); + virtual ~MimeProviderBase() {} + + virtual bool isValid() = 0; + virtual bool isInternalDatabase() const = 0; + virtual MimeType mimeTypeForName(const QString &name) = 0; + virtual void addFileNameMatches(const QString &fileName, + MimeGlobMatchResult &result, + QList<QString> &checkedMimeTypes) + = 0; + virtual void addParents(const QString &mime, QStringList &result) = 0; + virtual QString resolveAlias(const QString &name) = 0; + virtual void addAliases(const QString &name, QStringList &result) = 0; + virtual void findByMagic(const QByteArray &data, + int *accuracyPtr, + MimeType &candidate, + QList<QString> &checkedMimeTypes) + = 0; + virtual void addAllMimeTypes(QList<MimeType> &result) = 0; + virtual bool loadMimeTypePrivate(MimeTypePrivate &) { return false; } + virtual void loadIcon(MimeTypePrivate &) {} + virtual void loadGenericIcon(MimeTypePrivate &) {} + virtual void ensureLoaded() {} + + QString directory() const { return m_directory; } + + // added for Qt Creator + virtual bool hasMimeTypeForName(const QString &name) = 0; + virtual void addAllMimeTypeNames(QList<QString> &result) = 0; + virtual QMap<int, QList<MimeMagicRule>> magicRulesForMimeType(const MimeType &mimeType) const = 0; + virtual void setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules) = 0; + virtual void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) + = 0; + + MimeDatabasePrivate *m_db; + QString m_directory; +}; + +/* + Parses the files 'mime.cache' and 'types' on demand + */ +class MimeBinaryProvider : public MimeProviderBase +{ +public: + MimeBinaryProvider(MimeDatabasePrivate *db, const QString &directory); + virtual ~MimeBinaryProvider(); + + bool isValid() override; + bool isInternalDatabase() const override; + MimeType mimeTypeForName(const QString &name) override; + void addFileNameMatches(const QString &fileName, + MimeGlobMatchResult &result, + QList<QString> &checkedMimeTypes) override; + void addParents(const QString &mime, QStringList &result) override; + QString resolveAlias(const QString &name) override; + void addAliases(const QString &name, QStringList &result) override; + void findByMagic(const QByteArray &data, + int *accuracyPtr, + MimeType &candidate, + QList<QString> &checkedMimeTypes) override; + void addAllMimeTypes(QList<MimeType> &result) override; + bool loadMimeTypePrivate(MimeTypePrivate &) override; + void loadIcon(MimeTypePrivate &) override; + void loadGenericIcon(MimeTypePrivate &) override; + void ensureLoaded() override; + + // added for Qt Creator + bool hasMimeTypeForName(const QString &name) override; + void addAllMimeTypeNames(QList<QString> &result) override; + QMap<int, QList<MimeMagicRule>> magicRulesForMimeType(const MimeType &mimeType) const override; + void setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules) override; + void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) override; + +private: + struct CacheFile; + + void matchGlobList(MimeGlobMatchResult &result, + CacheFile *cacheFile, + int offset, + const QString &fileName, + const QList<QString> &ignoreMimeTypes); + bool matchSuffixTree(MimeGlobMatchResult &result, + CacheFile *cacheFile, + int numEntries, + int firstOffset, + const QString &fileName, + int charPos, + bool caseSensitiveCheck, + const QList<QString> &ignoreMimeTypes); + bool matchMagicRule(CacheFile *cacheFile, int numMatchlets, int firstOffset, const QByteArray &data); + QLatin1String iconForMime(CacheFile *cacheFile, int posListOffset, const QByteArray &inputMime); + void loadMimeTypeList(); + bool checkCacheChanged(); + + CacheFile *m_cacheFile = nullptr; + QStringList m_cacheFileNames; + QSet<QString> m_mimetypeNames; + bool m_mimetypeListLoaded; + struct MimeTypeExtra + { + QHash<QString, QString> localeComments; + QStringList globPatterns; + }; + QMap<QString, MimeTypeExtra> m_mimetypeExtra; +}; + +/* + Parses the raw XML files (slower) + */ +class MimeXMLProvider : public MimeProviderBase +{ +public: + enum InternalDatabaseEnum { InternalDatabase }; +#if 1 // QT_CONFIG(mimetype_database) + enum : bool { InternalDatabaseAvailable = true }; +#else + enum : bool { InternalDatabaseAvailable = false }; +#endif + MimeXMLProvider(MimeDatabasePrivate *db, InternalDatabaseEnum); + MimeXMLProvider(MimeDatabasePrivate *db, const QString &directory); + // added for Qt Creator + MimeXMLProvider(MimeDatabasePrivate *db, const QString &directory, const QByteArray &data); + ~MimeXMLProvider(); + + bool isValid() override; + bool isInternalDatabase() const override; + MimeType mimeTypeForName(const QString &name) override; + void addFileNameMatches(const QString &fileName, + MimeGlobMatchResult &result, + QList<QString> &checkedMimeTypes) override; + void addParents(const QString &mime, QStringList &result) override; + QString resolveAlias(const QString &name) override; + void addAliases(const QString &name, QStringList &result) override; + void findByMagic(const QByteArray &data, + int *accuracyPtr, + MimeType &candidate, + QList<QString> &checkedMimeTypes) override; + void addAllMimeTypes(QList<MimeType> &result) override; + void ensureLoaded() override; + + bool load(const QString &fileName, QString *errorMessage); + + // Called by the mimetype xml parser + void addMimeType(const MimeType &mt); + void addGlobPattern(const MimeGlobPattern &glob); + void addParent(const QString &child, const QString &parent); + void addAlias(const QString &alias, const QString &name); + void addMagicMatcher(const MimeMagicRuleMatcher &matcher); + + // added for Qt Creator + bool hasMimeTypeForName(const QString &name) override; + void addAllMimeTypeNames(QList<QString> &result) override; + QMap<int, QList<MimeMagicRule>> magicRulesForMimeType(const MimeType &mimeType) const override; + void setMagicRulesForMimeType(const MimeType &mimeType, + const QMap<int, QList<MimeMagicRule>> &rules) override; + void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) override; + +private: + void load(const QString &fileName); + void load(const char *data, qsizetype len); + + typedef QHash<QString, MimeType> NameMimeTypeMap; + NameMimeTypeMap m_nameMimeTypeMap; + + typedef QHash<QString, QString> AliasHash; + AliasHash m_aliases; + + typedef QHash<QString, QStringList> ParentsHash; + ParentsHash m_parents; + MimeAllGlobPatterns m_mimeTypeGlobs; + + QList<MimeMagicRuleMatcher> m_magicMatchers; + QStringList m_allFiles; +}; + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimetype.cpp b/src/libs/utils/mimetypes2/mimetype.cpp new file mode 100644 index 00000000000..ca1964ed9eb --- /dev/null +++ b/src/libs/utils/mimetypes2/mimetype.cpp @@ -0,0 +1,570 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected], author David Faure <[email protected]> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimetype.h" + +#include "mimetype_p.h" +#include "mimedatabase_p.h" +#include "mimeprovider_p.h" + +#include "mimeglobpattern_p.h" + +#include <QtCore/QDebug> +#include <QtCore/QLocale> +#include <QtCore/QHashFunctions> + +#include <memory> + +namespace Utils { + +static QString suffixFromPattern(const QString &pattern) +{ + // Not a simple suffix if it looks like: README or *. or *.* or *.JP*G or *.JP? + if (pattern.startsWith(QLatin1String("*.")) && + pattern.length() > 2 && + pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) { + return pattern.mid(2); + } + return {}; +} + +MimeTypePrivate::MimeTypePrivate() + : loaded(false), fromCache(false) +{} + +MimeTypePrivate::MimeTypePrivate(const MimeType &other) + : loaded(other.d->loaded), + name(other.d->name), + localeComments(other.d->localeComments), + genericIconName(other.d->genericIconName), + iconName(other.d->iconName), + globPatterns(other.d->globPatterns) +{} + +void MimeTypePrivate::clear() +{ + name.clear(); + localeComments.clear(); + genericIconName.clear(); + iconName.clear(); + globPatterns.clear(); +} + +void MimeTypePrivate::addGlobPattern(const QString &pattern) +{ + globPatterns.append(pattern); +} + +/*! + \class MimeType + \inmodule QtCore + \ingroup shared + \brief The MimeType class describes types of file or data, represented by a MIME type string. + + \since 5.0 + + For instance a file named "readme.txt" has the MIME type "text/plain". + The MIME type can be determined from the file name, or from the file + contents, or from both. MIME type determination can also be done on + buffers of data not coming from files. + + Determining the MIME type of a file can be useful to make sure your + application supports it. It is also useful in file-manager-like applications + or widgets, in order to display an appropriate \l {MimeType::iconName}{icon} for the file, or even + the descriptive \l {MimeType::comment()}{comment} in detailed views. + + To check if a file has the expected MIME type, you should use inherits() + rather than a simple string comparison based on the name(). This is because + MIME types can inherit from each other: for instance a C source file is + a specific type of plain text file, so text/x-csrc inherits text/plain. + + \sa MimeDatabase, {MIME Type Browser Example} + */ + +/*! + \fn MimeType &MimeType::operator=(MimeType &&other) + + Move-assigns \a other to this MimeType instance. + + \since 5.2 +*/ + +/*! + \fn MimeType::MimeType(); + Constructs this MimeType object initialized with default property values that indicate an invalid MIME type. + */ +MimeType::MimeType() : + d(new MimeTypePrivate()) +{ +} + +/*! + \fn MimeType::MimeType(const MimeType &other); + Constructs this MimeType object as a copy of \a other. + */ +MimeType::MimeType(const MimeType &other) : + d(other.d) +{ +} + +/*! + \fn MimeType &MimeType::operator=(const MimeType &other); + Assigns the data of \a other to this MimeType object, and returns a reference to this object. + */ +MimeType &MimeType::operator=(const MimeType &other) +{ + if (d != other.d) + d = other.d; + return *this; +} + +/*! + \fn MimeType::MimeType(const MimeTypePrivate &dd); + Assigns the data of the MimeTypePrivate \a dd to this MimeType object, and returns a reference to this object. + \internal + */ +MimeType::MimeType(const MimeTypePrivate &dd) : + d(new MimeTypePrivate(dd)) +{ +} + +/*! + \fn void MimeType::swap(MimeType &other); + Swaps MimeType \a other with this MimeType object. + + This operation is very fast and never fails. + + The swap() method helps with the implementation of assignment + operators in an exception-safe way. For more information consult + \l {http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap} + {More C++ Idioms - Copy-and-swap}. + */ + +/*! + \fn MimeType::~MimeType(); + Destroys the MimeType object, and releases the d pointer. + */ +MimeType::~MimeType() +{ +} + +/*! + \fn bool MimeType::operator==(const MimeType &other) const; + Returns \c true if \a other equals this MimeType object, otherwise returns \c false. + The name is the unique identifier for a mimetype, so two mimetypes with + the same name, are equal. + */ +bool MimeType::operator==(const MimeType &other) const +{ + return d == other.d || d->name == other.d->name; +} + +/*! + \since 5.6 + \relates MimeType + + Returns the hash value for \a key, using + \a seed to seed the calculation. + */ +size_t qHash(const MimeType &key, size_t seed) noexcept +{ + return qHash(key.d->name, seed); +} + +/*! + \fn bool MimeType::operator!=(const MimeType &other) const; + Returns \c true if \a other does not equal this MimeType object, otherwise returns \c false. + */ + +/*! + \property MimeType::valid + \brief \c true if the MimeType object contains valid data, \c false otherwise + + A valid MIME type has a non-empty name(). + The invalid MIME type is the default-constructed MimeType. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +bool MimeType::isValid() const +{ + return !d->name.isEmpty(); +} + +/*! + \property MimeType::isDefault + \brief \c true if this MIME type is the default MIME type which + applies to all files: application/octet-stream. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +bool MimeType::isDefault() const +{ + return d->name == MimeDatabasePrivate::instance()->defaultMimeType(); +} + +/*! + \property MimeType::name + \brief the name of the MIME type + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +QString MimeType::name() const +{ + return d->name; +} + +/*! + \property MimeType::comment + \brief the description of the MIME type to be displayed on user interfaces + + The default language (QLocale().name()) is used to select the appropriate translation. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +QString MimeType::comment() const +{ + MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<MimeTypePrivate&>(*d)); + + QStringList languageList; + languageList << QLocale().name(); + languageList << QLocale().uiLanguages(); + languageList << QLatin1String("default"); // use the default locale if possible. + for (const QString &language : qAsConst(languageList)) { + const QString lang = language == QLatin1String("C") ? QLatin1String("en_US") : language; + const QString comm = d->localeComments.value(lang); + if (!comm.isEmpty()) + return comm; + const int pos = lang.indexOf(QLatin1Char('_')); + if (pos != -1) { + // "pt_BR" not found? try just "pt" + const QString shortLang = lang.left(pos); + const QString commShort = d->localeComments.value(shortLang); + if (!commShort.isEmpty()) + return commShort; + } + } + + // Use the mimetype name as fallback + return d->name; +} + +/*! + \property MimeType::genericIconName + \brief the file name of a generic icon that represents the MIME type + + This should be used if the icon returned by iconName() cannot be found on + the system. It is used for categories of similar types (like spreadsheets + or archives) that can use a common icon. + The freedesktop.org Icon Naming Specification lists a set of such icon names. + + The icon name can be given to QIcon::fromTheme() in order to load the icon. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +QString MimeType::genericIconName() const +{ + MimeDatabasePrivate::instance()->loadGenericIcon(const_cast<MimeTypePrivate&>(*d)); + if (d->genericIconName.isEmpty()) { + // From the spec: + // If the generic icon name is empty (not specified by the mimetype definition) + // then the mimetype is used to generate the generic icon by using the top-level + // media type (e.g. "video" in "video/ogg") and appending "-x-generic" + // (i.e. "video-x-generic" in the previous example). + const QString group = name(); + QStringView groupRef(group); + const int slashindex = groupRef.indexOf(QLatin1Char('/')); + if (slashindex != -1) + groupRef = groupRef.left(slashindex); + return groupRef + QLatin1String("-x-generic"); + } + return d->genericIconName; +} + +static QString make_default_icon_name_from_mimetype_name(QString iconName) +{ + const int slashindex = iconName.indexOf(QLatin1Char('/')); + if (slashindex != -1) + iconName[slashindex] = QLatin1Char('-'); + return iconName; +} + +/*! + \property MimeType::iconName + \brief the file name of an icon image that represents the MIME type + + The icon name can be given to QIcon::fromTheme() in order to load the icon. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +QString MimeType::iconName() const +{ + MimeDatabasePrivate::instance()->loadIcon(const_cast<MimeTypePrivate&>(*d)); + if (d->iconName.isEmpty()) { + return make_default_icon_name_from_mimetype_name(name()); + } + return d->iconName; +} + +/*! + \property MimeType::globPatterns + \brief the list of glob matching patterns + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +QStringList MimeType::globPatterns() const +{ + MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<MimeTypePrivate&>(*d)); + return d->globPatterns; +} + +/*! + \property MimeType::parentMimeTypes + \brief the names of parent MIME types + + A type is a subclass of another type if any instance of the first type is + also an instance of the second. For example, all image/svg+xml files are also + text/xml, text/plain and application/octet-stream files. Subclassing is about + the format, rather than the category of the data (for example, there is no + 'generic spreadsheet' class that all spreadsheets inherit from). + Conversely, the parent mimetype of image/svg+xml is text/xml. + + A mimetype can have multiple parents. For instance application/x-perl + has two parents: application/x-executable and text/plain. This makes + it possible to both execute perl scripts, and to open them in text editors. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. +*/ +QStringList MimeType::parentMimeTypes() const +{ + return MimeDatabasePrivate::instance()->mimeParents(d->name); +} + +static void collectParentMimeTypes(const QString &mime, QStringList &allParents) +{ + const QStringList parents = MimeDatabasePrivate::instance()->mimeParents(mime); + for (const QString &parent : parents) { + // I would use QSet, but since order matters I better not + if (!allParents.contains(parent)) + allParents.append(parent); + } + // We want a breadth-first search, so that the least-specific parent (octet-stream) is last + // This means iterating twice, unfortunately. + for (const QString &parent : parents) + collectParentMimeTypes(parent, allParents); +} + +/*! + \property MimeType::allAncestors + \brief the names of direct and indirect parent MIME types + + Return all the parent mimetypes of this mimetype, direct and indirect. + This includes the parent(s) of its parent(s), etc. + + For instance, for image/svg+xml the list would be: + application/xml, text/plain, application/octet-stream. + + Note that application/octet-stream is the ultimate parent for all types + of files (but not directories). + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. +*/ +QStringList MimeType::allAncestors() const +{ + QStringList allParents; + collectParentMimeTypes(d->name, allParents); + return allParents; +} + +/*! + \property MimeType::aliases + \brief the list of aliases of this mimetype + + For instance, for text/csv, the returned list would be: + text/x-csv, text/x-comma-separated-values. + + Note that all MimeType instances refer to proper mimetypes, + never to aliases directly. + + The order of the aliases in the list is undefined. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. +*/ +QStringList MimeType::aliases() const +{ + return MimeDatabasePrivate::instance()->listAliases(d->name); +} + +/*! + \property MimeType::suffixes + \brief the known suffixes for the MIME type + + No leading dot is included, so for instance this would return "jpg", "jpeg" for image/jpeg. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +QStringList MimeType::suffixes() const +{ + MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<MimeTypePrivate&>(*d)); + + QStringList result; + for (const QString &pattern : qAsConst(d->globPatterns)) { + const QString suffix = suffixFromPattern(pattern); + if (!suffix.isEmpty()) + result.append(suffix); + } + + return result; +} + +/*! + \property MimeType::preferredSuffix + \brief the preferred suffix for the MIME type + + No leading dot is included, so for instance this would return "pdf" for application/pdf. + The return value can be empty, for mime types which do not have any suffixes associated. + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. + */ +QString MimeType::preferredSuffix() const +{ + if (isDefault()) // workaround for unwanted *.bin suffix for octet-stream, https://bugs.freedesktop.org/show_bug.cgi?id=101667, fixed upstream in 1.10 + return QString(); + const QStringList suffixList = suffixes(); + return suffixList.isEmpty() ? QString() : suffixList.at(0); +} + +/*! + \property MimeType::filterString + \brief a filter string usable for a file dialog + + While this property was introduced in 5.10, the + corresponding accessor method has always been there. +*/ +QString MimeType::filterString() const +{ + MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<MimeTypePrivate&>(*d)); + QString filter; + + if (!d->globPatterns.empty()) { + filter += comment() + QLatin1String(" ("); + for (int i = 0; i < d->globPatterns.size(); ++i) { + if (i != 0) + filter += QLatin1Char(' '); + filter += d->globPatterns.at(i); + } + filter += QLatin1Char(')'); + } + + return filter; +} + +/*! + \fn bool MimeType::inherits(const QString &mimeTypeName) const; + Returns \c true if this mimetype is \a mimeTypeName, + or inherits \a mimeTypeName (see parentMimeTypes()), + or \a mimeTypeName is an alias for this mimetype. + + This method has been made invokable from QML since 5.10. + */ +bool MimeType::inherits(const QString &mimeTypeName) const +{ + if (d->name == mimeTypeName) + return true; + return MimeDatabasePrivate::instance()->mimeInherits(d->name, mimeTypeName); +} + +/*! + Returns \c true if the name or alias of the MIME type matches + \a nameOrAlias. +*/ +bool MimeType::matchesName(const QString &nameOrAlias) const +{ + if (d->name == nameOrAlias) + return true; + auto dbp = MimeDatabasePrivate::instance(); + QMutexLocker locker(&dbp->mutex); + return MimeDatabasePrivate::instance()->resolveAlias(nameOrAlias) == d->name; +} + +/*! + Sets the preferred filename suffix for the MIME type to \a suffix. +*/ +void MimeType::setPreferredSuffix(const QString &suffix) +{ + MimeDatabasePrivate::instance()->loadMimeTypePrivate(const_cast<MimeTypePrivate&>(*d)); + + auto it = std::find_if(d->globPatterns.begin(), + d->globPatterns.end(), + [suffix](const QString &pattern) { + return suffixFromPattern(pattern) == suffix; + }); + if (it != d->globPatterns.end()) + d->globPatterns.erase(it); + d->globPatterns.prepend(QLatin1String("*.") + suffix); +} + +} // namespace Utils + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const Utils::MimeType &mime) +{ + QDebugStateSaver saver(debug); + if (!mime.isValid()) { + debug.nospace() << "MimeType(invalid)"; + } else { + debug.nospace() << "MimeType(" << mime.name() << ")"; + } + return debug; +} +#endif + +#include "moc_mimetype.cpp" diff --git a/src/libs/utils/mimetypes2/mimetype.h b/src/libs/utils/mimetypes2/mimetype.h new file mode 100644 index 00000000000..b9da254ea8f --- /dev/null +++ b/src/libs/utils/mimetypes2/mimetype.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected], author David Faure <[email protected]> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <utils/utils_global.h> + +#include <QtCore/qobjectdefs.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> + +namespace Utils { + +class MimeTypePrivate; +class MimeType; + +QTCREATOR_UTILS_EXPORT size_t qHash(const MimeType &key, size_t seed = 0) noexcept; + +class QTCREATOR_UTILS_EXPORT MimeType +{ + Q_GADGET + Q_PROPERTY(bool valid READ isValid CONSTANT) + Q_PROPERTY(bool isDefault READ isDefault CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString comment READ comment CONSTANT) + Q_PROPERTY(QString genericIconName READ genericIconName CONSTANT) + Q_PROPERTY(QString iconName READ iconName CONSTANT) + Q_PROPERTY(QStringList globPatterns READ globPatterns CONSTANT) + Q_PROPERTY(QStringList parentMimeTypes READ parentMimeTypes CONSTANT) + Q_PROPERTY(QStringList allAncestors READ allAncestors CONSTANT) + Q_PROPERTY(QStringList aliases READ aliases CONSTANT) + Q_PROPERTY(QStringList suffixes READ suffixes CONSTANT) + Q_PROPERTY(QString preferredSuffix READ preferredSuffix CONSTANT) + Q_PROPERTY(QString filterString READ filterString CONSTANT) + +public: + MimeType(); + MimeType(const MimeType &other); + MimeType &operator=(const MimeType &other); + MimeType &operator=(MimeType &&other) noexcept + { + swap(other); + return *this; + } + void swap(MimeType &other) noexcept + { + d.swap(other.d); + } + explicit MimeType(const MimeTypePrivate &dd); + ~MimeType(); + + bool operator==(const MimeType &other) const; + + inline bool operator!=(const MimeType &other) const + { + return !operator==(other); + } + + bool isValid() const; + + bool isDefault() const; + + QString name() const; + QString comment() const; + QString genericIconName() const; + QString iconName() const; + QStringList globPatterns() const; + QStringList parentMimeTypes() const; + QStringList allAncestors() const; + QStringList aliases() const; + QStringList suffixes() const; + QString preferredSuffix() const; + + Q_INVOKABLE bool inherits(const QString &mimeTypeName) const; + + QString filterString() const; + + // Qt Creator additions + bool matchesName(const QString &nameOrAlias) const; + void setPreferredSuffix(const QString &suffix); + +protected: + friend class MimeTypeParserBase; + friend class MimeTypeMapEntry; + friend class MimeDatabasePrivate; + friend class MimeXMLProvider; + friend class MimeBinaryProvider; + friend class MimeTypePrivate; + friend QTCREATOR_UTILS_EXPORT size_t qHash(const MimeType &key, size_t seed) noexcept; + + QExplicitlySharedDataPointer<MimeTypePrivate> d; +}; + + +} // namespace Utils + +QT_BEGIN_NAMESPACE +#ifndef QT_NO_DEBUG_STREAM +class QDebug; +QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const Utils::MimeType &mime); +#endif + +Q_DECLARE_SHARED(Utils::MimeType) +QT_END_NAMESPACE diff --git a/src/libs/utils/mimetypes2/mimetype_p.h b/src/libs/utils/mimetypes2/mimetype_p.h new file mode 100644 index 00000000000..1158333a5c6 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimetype_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "mimetype.h" + +#include <QtCore/qhash.h> +#include <QtCore/qstringlist.h> + +namespace Utils { + +class MimeTypePrivate : public QSharedData +{ +public: + typedef QHash<QString, QString> LocaleHash; + + MimeTypePrivate(); + explicit MimeTypePrivate(const MimeType &other); + + void clear(); + + void addGlobPattern(const QString &pattern); + + bool loaded; // QSharedData leaves a 4 byte gap, so don't put 8 byte members first + bool fromCache; // true if this comes from the binary provider + QString name; + LocaleHash localeComments; + QString genericIconName; + QString iconName; + QStringList globPatterns; +}; + +} // namespace Utils + +#if 0 +#define QMIMETYPE_BUILDER_FROM_RVALUE_REFS \ + QT_BEGIN_NAMESPACE \ + static QMimeType buildQMimeType ( \ + QString &&name, \ + QString &&genericIconName, \ + QString &&iconName, \ + QStringList &&globPatterns \ + ) \ + { \ + QMimeTypePrivate qMimeTypeData; \ + qMimeTypeData.loaded = true; \ + qMimeTypeData.name = std::move(name); \ + qMimeTypeData.genericIconName = std::move(genericIconName); \ + qMimeTypeData.iconName = std::move(iconName); \ + qMimeTypeData.globPatterns = std::move(globPatterns); \ + return QMimeType(qMimeTypeData); \ + } \ + QT_END_NAMESPACE +#endif diff --git a/src/libs/utils/mimetypes2/mimetypeparser.cpp b/src/libs/utils/mimetypes2/mimetypeparser.cpp new file mode 100644 index 00000000000..40ecfd90400 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimetypeparser.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimetypeparser_p.h" + +#include "mimetype_p.h" +#include "mimemagicrulematcher_p.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QXmlStreamWriter> +#include <QtCore/QStack> + +namespace Utils { + +// XML tags in MIME files +static const char mimeInfoTagC[] = "mime-info"; +static const char mimeTypeTagC[] = "mime-type"; +static const char mimeTypeAttributeC[] = "type"; +static const char subClassTagC[] = "sub-class-of"; +static const char commentTagC[] = "comment"; +static const char genericIconTagC[] = "generic-icon"; +static const char iconTagC[] = "icon"; +static const char nameAttributeC[] = "name"; +static const char globTagC[] = "glob"; +static const char globDeleteAllTagC[] = "glob-deleteall"; +static const char aliasTagC[] = "alias"; +static const char patternAttributeC[] = "pattern"; +static const char weightAttributeC[] = "weight"; +static const char caseSensitiveAttributeC[] = "case-sensitive"; +static const char localeAttributeC[] = "xml:lang"; + +static const char magicTagC[] = "magic"; +static const char priorityAttributeC[] = "priority"; + +static const char matchTagC[] = "match"; +static const char matchValueAttributeC[] = "value"; +static const char matchTypeAttributeC[] = "type"; +static const char matchOffsetAttributeC[] = "offset"; +static const char matchMaskAttributeC[] = "mask"; + +/*! + \class MimeTypeParser + \inmodule QtCore + \internal + \brief The MimeTypeParser class parses MIME types, and builds a MIME database hierarchy by adding to MimeDatabase. + + Populates MimeDataBase + + \sa MimeDatabase, MimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern + \sa MimeTypeParser +*/ + +/*! + \class MimeTypeParserBase + \inmodule QtCore + \internal + \brief The MimeTypeParserBase class parses for a sequence of <mime-type> in a generic way. + + Calls abstract handler function process for MimeType it finds. + + \sa MimeDatabase, MimeMagicRuleMatcher, MagicRule, MagicStringRule, MagicByteRule, GlobPattern + \sa MimeTypeParser +*/ + +/*! + \fn virtual bool MimeTypeParserBase::process(const MimeType &t, QString *errorMessage) = 0; + Overwrite to process the sequence of parsed data +*/ + +MimeTypeParserBase::ParseState MimeTypeParserBase::nextState(ParseState currentState, QStringView startElement) +{ + switch (currentState) { + case ParseBeginning: + if (startElement == QLatin1String(mimeInfoTagC)) + return ParseMimeInfo; + if (startElement == QLatin1String(mimeTypeTagC)) + return ParseMimeType; + return ParseError; + case ParseMimeInfo: + return startElement == QLatin1String(mimeTypeTagC) ? ParseMimeType : ParseError; + case ParseMimeType: + case ParseComment: + case ParseGenericIcon: + case ParseIcon: + case ParseGlobPattern: + case ParseGlobDeleteAll: + case ParseSubClass: + case ParseAlias: + case ParseOtherMimeTypeSubTag: + case ParseMagicMatchRule: + if (startElement == QLatin1String(mimeTypeTagC)) // Sequence of <mime-type> + return ParseMimeType; + if (startElement == QLatin1String(commentTagC)) + return ParseComment; + if (startElement == QLatin1String(genericIconTagC)) + return ParseGenericIcon; + if (startElement == QLatin1String(iconTagC)) + return ParseIcon; + if (startElement == QLatin1String(globTagC)) + return ParseGlobPattern; + if (startElement == QLatin1String(globDeleteAllTagC)) + return ParseGlobDeleteAll; + if (startElement == QLatin1String(subClassTagC)) + return ParseSubClass; + if (startElement == QLatin1String(aliasTagC)) + return ParseAlias; + if (startElement == QLatin1String(magicTagC)) + return ParseMagic; + if (startElement == QLatin1String(matchTagC)) + return ParseMagicMatchRule; + return ParseOtherMimeTypeSubTag; + case ParseMagic: + if (startElement == QLatin1String(matchTagC)) + return ParseMagicMatchRule; + break; + case ParseError: + break; + } + return ParseError; +} + +// Parse int number from an (attribute) string +bool MimeTypeParserBase::parseNumber(QStringView n, int *target, QString *errorMessage) +{ + bool ok; + *target = n.toInt(&ok); + if (Q_UNLIKELY(!ok)) { + if (errorMessage) + *errorMessage = QLatin1String("Not a number '") + n + QLatin1String("'."); + return false; + } + return true; +} + +#ifndef QT_NO_XMLSTREAMREADER +struct CreateMagicMatchRuleResult +{ + QString errorMessage; // must be first + MimeMagicRule rule; + + CreateMagicMatchRuleResult(QStringView type, QStringView value, QStringView offsets, QStringView mask) + : errorMessage(), rule(type.toString(), value.toUtf8(), offsets.toString(), mask.toLatin1(), &errorMessage) + { + + } +}; + +static CreateMagicMatchRuleResult createMagicMatchRule(const QXmlStreamAttributes &atts) +{ + const auto type = atts.value(QLatin1String(matchTypeAttributeC)); + const auto value = atts.value(QLatin1String(matchValueAttributeC)); + const auto offsets = atts.value(QLatin1String(matchOffsetAttributeC)); + const auto mask = atts.value(QLatin1String(matchMaskAttributeC)); + return CreateMagicMatchRuleResult(type, value, offsets, mask); +} +#endif + +bool MimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString *errorMessage) +{ +#ifdef QT_NO_XMLSTREAMREADER + Q_UNUSED(dev); + if (errorMessage) + *errorMessage = QString::fromLatin1("QXmlStreamReader is not available, cannot parse '%1'.").arg(fileName); + return false; +#else + MimeTypePrivate data; + data.loaded = true; + int priority = 50; + QStack<MimeMagicRule *> currentRules; // stack for the nesting of rules + QList<MimeMagicRule> rules; // toplevel rules + QXmlStreamReader reader(dev); + ParseState ps = ParseBeginning; + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: { + ps = nextState(ps, reader.name()); + const QXmlStreamAttributes atts = reader.attributes(); + switch (ps) { + case ParseMimeType: { // start parsing a MIME type name + const QString name = atts.value(QLatin1String(mimeTypeAttributeC)).toString(); + if (name.isEmpty()) { + reader.raiseError(QStringLiteral("Missing 'type'-attribute")); + } else { + data.name = name; + } + } + break; + case ParseGenericIcon: + data.genericIconName = atts.value(QLatin1String(nameAttributeC)).toString(); + break; + case ParseIcon: + data.iconName = atts.value(QLatin1String(nameAttributeC)).toString(); + break; + case ParseGlobPattern: { + const QString pattern = atts.value(QLatin1String(patternAttributeC)).toString(); + unsigned weight = atts.value(QLatin1String(weightAttributeC)).toInt(); + const bool caseSensitive = atts.value(QLatin1String(caseSensitiveAttributeC)) == QLatin1String("true"); + + if (weight == 0) + weight = MimeGlobPattern::DefaultWeight; + + Q_ASSERT(!data.name.isEmpty()); + const MimeGlobPattern glob(pattern, data.name, weight, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive); + if (!process(glob, errorMessage)) // for actual glob matching + return false; + data.addGlobPattern(pattern); // just for MimeType::globPatterns() + } + break; + case ParseGlobDeleteAll: + data.globPatterns.clear(); + break; + case ParseSubClass: { + const QString inheritsFrom = atts.value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!inheritsFrom.isEmpty()) + processParent(data.name, inheritsFrom); + } + break; + case ParseComment: { + // comments have locale attributes. + QString locale = atts.value(QLatin1String(localeAttributeC)).toString(); + const QString comment = reader.readElementText(); + if (locale.isEmpty()) + locale = QString::fromLatin1("default"); + data.localeComments.insert(locale, comment); + } + break; + case ParseAlias: { + const QString alias = atts.value(QLatin1String(mimeTypeAttributeC)).toString(); + if (!alias.isEmpty()) + processAlias(alias, data.name); + } + break; + case ParseMagic: { + priority = 50; + const auto priorityS = atts.value(QLatin1String(priorityAttributeC)); + if (!priorityS.isEmpty()) { + if (!parseNumber(priorityS, &priority, errorMessage)) + return false; + + } + currentRules.clear(); + //qDebug() << "MAGIC start for mimetype" << data.name; + } + break; + case ParseMagicMatchRule: { + auto result = createMagicMatchRule(atts); + if (Q_UNLIKELY(!result.rule.isValid())) + qWarning("MimeDatabase: Error parsing %ls\n%ls", + qUtf16Printable(fileName), qUtf16Printable(result.errorMessage)); + QList<MimeMagicRule> *ruleList; + if (currentRules.isEmpty()) + ruleList = &rules; + else // nest this rule into the proper parent + ruleList = ¤tRules.top()->m_subMatches; + ruleList->append(std::move(result.rule)); + //qDebug() << " MATCH added. Stack size was" << currentRules.size(); + currentRules.push(&ruleList->last()); + break; + } + case ParseError: + reader.raiseError(QLatin1String("Unexpected element <") + reader.name() + QLatin1Char('>')); + break; + default: + break; + } + } + break; + // continue switch QXmlStreamReader::Token... + case QXmlStreamReader::EndElement: // Finished element + { + const auto elementName = reader.name(); + if (elementName == QLatin1String(mimeTypeTagC)) { + if (!process(MimeType(data), errorMessage)) + return false; + data.clear(); + } else if (elementName == QLatin1String(matchTagC)) { + // Closing a <match> tag, pop stack + currentRules.pop(); + //qDebug() << " MATCH closed. Stack size is now" << currentRules.size(); + } else if (elementName == QLatin1String(magicTagC)) { + //qDebug() << "MAGIC ended, we got" << rules.count() << "rules, with prio" << priority; + // Finished a <magic> sequence + MimeMagicRuleMatcher ruleMatcher(data.name, priority); + ruleMatcher.addRules(rules); + processMagicMatcher(ruleMatcher); + rules.clear(); + } + break; + } + default: + break; + } + } + + if (Q_UNLIKELY(reader.hasError())) { + if (errorMessage) { + *errorMessage = QString::asprintf("An error has been encountered at line %lld of %ls: %ls:", + reader.lineNumber(), + qUtf16Printable(fileName), + qUtf16Printable(reader.errorString())); + } + return false; + } + + return true; +#endif //QT_NO_XMLSTREAMREADER +} + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimetypeparser_p.h b/src/libs/utils/mimetypes2/mimetypeparser_p.h new file mode 100644 index 00000000000..87efc19eec7 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimetypeparser_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "mimedatabase_p.h" + +#include "mimeprovider_p.h" + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +namespace Utils { + +class MimeTypeParserBase +{ + Q_DISABLE_COPY_MOVE(MimeTypeParserBase) + +public: + MimeTypeParserBase() {} + virtual ~MimeTypeParserBase() {} + + bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage); + + static bool parseNumber(QStringView n, int *target, QString *errorMessage); + +protected: + virtual bool process(const MimeType &t, QString *errorMessage) = 0; + virtual bool process(const MimeGlobPattern &t, QString *errorMessage) = 0; + virtual void processParent(const QString &child, const QString &parent) = 0; + virtual void processAlias(const QString &alias, const QString &name) = 0; + virtual void processMagicMatcher(const MimeMagicRuleMatcher &matcher) = 0; + +private: + enum ParseState { + ParseBeginning, + ParseMimeInfo, + ParseMimeType, + ParseComment, + ParseGenericIcon, + ParseIcon, + ParseGlobPattern, + ParseGlobDeleteAll, + ParseSubClass, + ParseAlias, + ParseMagic, + ParseMagicMatchRule, + ParseOtherMimeTypeSubTag, + ParseError + }; + + static ParseState nextState(ParseState currentState, QStringView startElement); +}; + + +class MimeTypeParser : public MimeTypeParserBase +{ +public: + explicit MimeTypeParser(MimeXMLProvider &provider) : m_provider(provider) {} + +protected: + inline bool process(const MimeType &t, QString *) override + { m_provider.addMimeType(t); return true; } + + inline bool process(const MimeGlobPattern &glob, QString *) override + { m_provider.addGlobPattern(glob); return true; } + + inline void processParent(const QString &child, const QString &parent) override + { m_provider.addParent(child, parent); } + + inline void processAlias(const QString &alias, const QString &name) override + { m_provider.addAlias(alias, name); } + + inline void processMagicMatcher(const MimeMagicRuleMatcher &matcher) override + { m_provider.addMagicMatcher(matcher); } + +private: + MimeXMLProvider &m_provider; +}; + +} // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimeutils.cpp b/src/libs/utils/mimetypes2/mimeutils.cpp new file mode 100644 index 00000000000..ffa2488df19 --- /dev/null +++ b/src/libs/utils/mimetypes2/mimeutils.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mimeutils.h" + +#include "mimedatabase.h" +#include "mimedatabase_p.h" +#include "mimemagicrule_p.h" +#include "mimeprovider_p.h" + +#include "filepath.h" + +namespace Utils { + +MimeType mimeTypeForName(const QString &nameOrAlias) +{ + MimeDatabase mdb; + return mdb.mimeTypeForName(nameOrAlias); +} + +MimeType mimeTypeForFile(const QString &fileName, MimeMatchMode mode) +{ + MimeDatabase mdb; + return mdb.mimeTypeForFile(fileName, MimeDatabase::MatchMode(mode)); +} + +MimeType mimeTypeForFile(const QFileInfo &fileInfo, MimeMatchMode mode) +{ + MimeDatabase mdb; + return mdb.mimeTypeForFile(fileInfo, MimeDatabase::MatchMode(mode)); +} + +MimeType mimeTypeForFile(const FilePath &filePath, MimeMatchMode mode) +{ + MimeDatabase mdb; + if (filePath.needsDevice()) + return mdb.mimeTypeForUrl(filePath.toUrl()); + return mdb.mimeTypeForFile(filePath.toString(), MimeDatabase::MatchMode(mode)); +} + +QList<MimeType> mimeTypesForFileName(const QString &fileName) +{ + MimeDatabase mdb; + return mdb.mimeTypesForFileName(fileName); +} + +MimeType mimeTypeForData(const QByteArray &data) +{ + MimeDatabase mdb; + return mdb.mimeTypeForData(data); +} + +QList<MimeType> allMimeTypes() +{ + MimeDatabase mdb; + return mdb.allMimeTypes(); +} + +void setMimeStartupPhase(MimeStartupPhase phase) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + if (int(phase) != d->m_startupPhase + 1) { + qWarning("Unexpected jump in MimedDatabase lifetime from %d to %d", + d->m_startupPhase, + int(phase)); + } + d->m_startupPhase = int(phase); +} + +void addMimeTypes(const QString &id, const QByteArray &data) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + + if (d->m_startupPhase >= int(MimeStartupPhase::PluginsDelayedInitializing)) { + qWarning("Adding items for ID \"%s\" to MimeDatabase after initialization time", + qPrintable(id)); + } + + d->addMimeData(id, data); +} + +QMap<int, QList<MimeMagicRule>> magicRulesForMimeType(const MimeType &mimeType) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + return d->magicRulesForMimeType(mimeType); +} + +void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + d->setGlobPatternsForMimeType(mimeType, patterns); +} + +void setMagicRulesForMimeType(const MimeType &mimeType, const QMap<int, QList<MimeMagicRule>> &rules) +{ + auto d = MimeDatabasePrivate::instance(); + QMutexLocker locker(&d->mutex); + d->setMagicRulesForMimeType(mimeType, rules); +} + +} // namespace Utils diff --git a/src/libs/utils/mimeutils.h b/src/libs/utils/mimeutils.h new file mode 100644 index 00000000000..ceb05180c4b --- /dev/null +++ b/src/libs/utils/mimeutils.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <mimemagicrule_p.h> +#include <mimetype.h> + +#include <utils/utils_global.h> + +QT_BEGIN_NAMESPACE +class QFileInfo; +QT_END_NAMESPACE + +namespace Utils { + +class FilePath; + +// Wrapped QMimeDataBase functions +QTCREATOR_UTILS_EXPORT MimeType mimeTypeForName(const QString &nameOrAlias); + +enum class MimeMatchMode { MatchDefault = 0x0, MatchExtension = 0x1, MatchContent = 0x2 }; + +QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const QString &fileName, + MimeMatchMode mode = MimeMatchMode::MatchDefault); +QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const QFileInfo &fileInfo, + MimeMatchMode mode = MimeMatchMode::MatchDefault); +QTCREATOR_UTILS_EXPORT MimeType mimeTypeForFile(const FilePath &filePath, + MimeMatchMode mode = MimeMatchMode::MatchDefault); +QTCREATOR_UTILS_EXPORT QList<MimeType> mimeTypesForFileName(const QString &fileName); +QTCREATOR_UTILS_EXPORT MimeType mimeTypeForData(const QByteArray &data); +QTCREATOR_UTILS_EXPORT QList<MimeType> allMimeTypes(); + +// Qt Creator additions +// For debugging purposes. +enum class MimeStartupPhase { + BeforeInitialize, + PluginsLoading, + PluginsInitializing, // Register up to here. + PluginsDelayedInitializing, // Use from here on. + UpAndRunning +}; + +QTCREATOR_UTILS_EXPORT void setMimeStartupPhase(MimeStartupPhase); +QTCREATOR_UTILS_EXPORT void addMimeTypes(const QString &id, const QByteArray &data); +QTCREATOR_UTILS_EXPORT QMap<int, QList<MimeMagicRule>> magicRulesForMimeType( + const MimeType &mimeType); // priority -> rules +QTCREATOR_UTILS_EXPORT void setGlobPatternsForMimeType(const MimeType &mimeType, + const QStringList &patterns); +QTCREATOR_UTILS_EXPORT void setMagicRulesForMimeType( + const MimeType &mimeType, const QMap<int, QList<MimeMagicRule>> &rules); // priority -> rules + +} // namespace Utils diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index e1bb3c4eb50..d1c7e642b0c 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -153,7 +153,7 @@ QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd) proc.setTimeoutS(1); proc.setCommand(cmd); proc.runBlocking(); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return QString(); return proc.allOutput(); } diff --git a/src/libs/utils/pathlisteditor.cpp b/src/libs/utils/pathlisteditor.cpp index d24be4995b5..82e5ece0b53 100644 --- a/src/libs/utils/pathlisteditor.cpp +++ b/src/libs/utils/pathlisteditor.cpp @@ -196,6 +196,11 @@ void PathListEditor::setFileDialogTitle(const QString &l) d->fileDialogTitle = l; } +void PathListEditor::setPlaceholderText(const QString &placeholder) +{ + d->edit->setPlaceholderText(placeholder); +} + void PathListEditor::clear() { d->edit->clear(); diff --git a/src/libs/utils/pathlisteditor.h b/src/libs/utils/pathlisteditor.h index edd70336c09..a7c61448d18 100644 --- a/src/libs/utils/pathlisteditor.h +++ b/src/libs/utils/pathlisteditor.h @@ -57,6 +57,7 @@ public: void setPathList(const QStringList &l); void setPathList(const QString &pathString); void setFileDialogTitle(const QString &l); + void setPlaceholderText(const QString &placeholder); signals: void changed(); diff --git a/src/libs/utils/porting.h b/src/libs/utils/porting.h index f8d945a0c41..c84ea60dfb4 100644 --- a/src/libs/utils/porting.h +++ b/src/libs/utils/porting.h @@ -34,13 +34,6 @@ namespace Utils { -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) -// Keep the support code for lower Qt versions for sdktool -constexpr QString::SplitBehavior SkipEmptyParts = QString::SkipEmptyParts; -#else -constexpr Qt::SplitBehaviorFlags SkipEmptyParts = Qt::SkipEmptyParts; -#endif - #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) using QHashValueType = uint; #else @@ -71,7 +64,6 @@ inline StringView make_stringview(const QString &s) #endif } -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) // QStringView::mid in Qt5 does not do bounds checking, in Qt6 it does inline QStringView midView(const QString &s, int offset, int length) { @@ -92,7 +84,6 @@ inline QStringView midView(const QString &s, int offset, int length) return QStringView(s).mid(offset, length); #endif } -#endif #ifdef QT_GUI_LIB #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) diff --git a/src/libs/utils/processenums.h b/src/libs/utils/processenums.h new file mode 100644 index 00000000000..7e9bd7d0d43 --- /dev/null +++ b/src/libs/utils/processenums.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QMetaType> +#include <functional> + +namespace Utils { + +enum class ProcessMode { + Reader, // This opens in ReadOnly mode if no write data or in ReadWrite mode otherwise, + // closes the write channel afterwards. + Writer // This opens in ReadWrite mode and doesn't close the write channel +}; + +enum class ProcessImpl { + QProcess, + ProcessLauncher, + Default // Defaults to ProcessLauncherImpl, if QTC_USE_QPROCESS env var is set + // it equals to QProcessImpl. +}; + +enum class TerminalMode { + Off, + Run, + Debug, + Suspend, + On = Run // Default mode for terminal set to on +}; + +// Miscellaneous, not process core + +enum class EventLoopMode { + Off, + On // Avoid +}; + +enum class ProcessResult { + // Finished successfully. Unless an ExitCodeInterpreter is set + // this corresponds to a return code 0. + FinishedWithSuccess, + Finished = FinishedWithSuccess, // FIXME: Kept to ease downstream transition + // Finished unsuccessfully. Unless an ExitCodeInterpreter is set + // this corresponds to a return code different from 0. + FinishedWithError, + FinishedError = FinishedWithError, // FIXME: Kept to ease downstream transition + // Process terminated abnormally (kill) + TerminatedAbnormally, + // Executable could not be started + StartFailed, + // Hang, no output after time out + Hang +}; + +using ExitCodeInterpreter = std::function<ProcessResult(int /*exitCode*/)>; + +} // namespace Utils + +Q_DECLARE_METATYPE(Utils::ProcessMode); diff --git a/src/libs/utils/processinfo.cpp b/src/libs/utils/processinfo.cpp new file mode 100644 index 00000000000..67c6177efd1 --- /dev/null +++ b/src/libs/utils/processinfo.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "processinfo.h" + +#include "qtcprocess.h" + +#if defined(Q_OS_UNIX) +#include <QDir> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#elif defined(Q_OS_WIN) +#include <windows.h> +#include <utils/winutils.h> +#include <tlhelp32.h> +#include <psapi.h> +#endif + +namespace Utils { + +bool ProcessInfo::operator<(const ProcessInfo &other) const +{ + if (processId != other.processId) + return processId < other.processId; + if (executable != other.executable) + return executable < other.executable; + return commandLine < other.commandLine; +} + +#if defined(Q_OS_UNIX) + +static bool isUnixProcessId(const QString &procname) +{ + for (int i = 0; i != procname.size(); ++i) + if (!procname.at(i).isDigit()) + return false; + return true; +} + +// Determine UNIX processes by reading "/proc". Default to ps if +// it does not exist + +static const char procDirC[] = "/proc/"; + +static QList<ProcessInfo> getLocalProcessesUsingProc() +{ + QList<ProcessInfo> processes; + const QString procDirPath = QLatin1String(procDirC); + const QDir procDir = QDir(QLatin1String(procDirC)); + const QStringList procIds = procDir.entryList(); + for (const QString &procId : procIds) { + if (!isUnixProcessId(procId)) + continue; + ProcessInfo proc; + proc.processId = procId.toInt(); + const QString root = procDirPath + procId; + + const QFile exeFile(root + QLatin1String("/exe")); + proc.executable = exeFile.symLinkTarget(); + + QFile cmdLineFile(root + QLatin1String("/cmdline")); + if (cmdLineFile.open(QIODevice::ReadOnly)) { // process may have exited + const QList<QByteArray> tokens = cmdLineFile.readAll().split('\0'); + if (!tokens.isEmpty()) { + if (proc.executable.isEmpty()) + proc.executable = QString::fromLocal8Bit(tokens.front()); + for (const QByteArray &t : tokens) { + if (!proc.commandLine.isEmpty()) + proc.commandLine.append(QLatin1Char(' ')); + proc.commandLine.append(QString::fromLocal8Bit(t)); + } + } + } + + if (proc.executable.isEmpty()) { + QFile statFile(root + QLatin1String("/stat")); + if (!statFile.open(QIODevice::ReadOnly)) { + const QStringList data = QString::fromLocal8Bit(statFile.readAll()).split(QLatin1Char(' ')); + if (data.size() < 2) + continue; + proc.executable = data.at(1); + proc.commandLine = data.at(1); // PPID is element 3 + if (proc.executable.startsWith(QLatin1Char('(')) && proc.executable.endsWith(QLatin1Char(')'))) { + proc.executable.truncate(proc.executable.size() - 1); + proc.executable.remove(0, 1); + } + } + } + if (!proc.executable.isEmpty()) + processes.push_back(proc); + } + return processes; +} + +// Determine UNIX processes by running ps +static QMap<qint64, QString> getLocalProcessDataUsingPs(const QString &column) +{ + QMap<qint64, QString> result; + Utils::QtcProcess psProcess; + psProcess.setCommand({"ps", {"-e", "-o", "pid," + column}}); + psProcess.start(); + if (psProcess.waitForStarted()) { + QByteArray output; + if (psProcess.readDataFromProcess(30, &output, nullptr, false)) { + // Split "457 /Users/foo.app arg1 arg2" + const QStringList lines = QString::fromLocal8Bit(output).split(QLatin1Char('\n')); + const int lineCount = lines.size(); + const QChar blank = QLatin1Char(' '); + for (int l = 1; l < lineCount; l++) { // Skip header + const QString line = lines.at(l).trimmed(); + const int pidSep = line.indexOf(blank); + const qint64 pid = line.left(pidSep).toLongLong(); + result[pid] = line.mid(pidSep + 1); + } + } + } + return result; +} + +static QList<ProcessInfo> getLocalProcessesUsingPs() +{ + QList<ProcessInfo> processes; + + // cmdLines are full command lines, usually with absolute path, + // exeNames only the file part of the executable's path. + const QMap<qint64, QString> exeNames = getLocalProcessDataUsingPs("comm"); + const QMap<qint64, QString> cmdLines = getLocalProcessDataUsingPs("args"); + + for (auto it = exeNames.begin(), end = exeNames.end(); it != end; ++it) { + const qint64 pid = it.key(); + if (pid <= 0) + continue; + const QString cmdLine = cmdLines.value(pid); + if (cmdLines.isEmpty()) + continue; + const QString exeName = it.value(); + if (exeName.isEmpty()) + continue; + const int pos = cmdLine.indexOf(exeName); + if (pos == -1) + continue; + processes.append({pid, cmdLine.left(pos + exeName.size()), cmdLine}); + } + + return processes; +} + +QList<ProcessInfo> ProcessInfo::processInfoList() +{ + const QDir procDir = QDir(QLatin1String(procDirC)); + return procDir.exists() ? getLocalProcessesUsingProc() : getLocalProcessesUsingPs(); +} + +#elif defined(Q_OS_WIN) + +QList<ProcessInfo> ProcessInfo::processInfoList() +{ + QList<ProcessInfo> processes; + + PROCESSENTRY32 pe; + pe.dwSize = sizeof(PROCESSENTRY32); + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) + return processes; + + for (bool hasNext = Process32First(snapshot, &pe); hasNext; hasNext = Process32Next(snapshot, &pe)) { + ProcessInfo p; + p.processId = pe.th32ProcessID; + // Image has the absolute path, but can fail. + const QString image = Utils::imageName(pe.th32ProcessID); + p.executable = p.commandLine = image.isEmpty() ? + QString::fromWCharArray(pe.szExeFile) : image; + processes << p; + } + CloseHandle(snapshot); + return processes; +} + +#endif //Q_OS_WIN + +} // namespace Utils diff --git a/src/libs/ssh/sshprocess.h b/src/libs/utils/processinfo.h index 00610bcd69a..807adcefaa8 100644 --- a/src/libs/ssh/sshprocess.h +++ b/src/libs/utils/processinfo.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,16 +25,23 @@ #pragma once -#include "ssh_global.h" +#include "utils_global.h" -#include <utils/qtcprocess.h> +#include <QList> +#include <QString> -namespace QSsh { +namespace Utils { -class QSSH_EXPORT SshProcess : public Utils::QtcProcess +class QTCREATOR_UTILS_EXPORT ProcessInfo { public: - SshProcess(Utils::ProcessMode processMode = Utils::ProcessMode::Reader); + qint64 processId = 0; + QString executable; + QString commandLine; + + bool operator<(const ProcessInfo &other) const; + + static QList<ProcessInfo> processInfoList(); }; -} // namespace QSsh +} // namespace Utils diff --git a/src/libs/utils/processinterface.h b/src/libs/utils/processinterface.h new file mode 100644 index 00000000000..1d94f8deb85 --- /dev/null +++ b/src/libs/utils/processinterface.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "utils_global.h" + +#include "environment.h" +#include "commandline.h" +#include "processenums.h" + +#include <QProcess> + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT ProcessSetupData +{ +public: + using Ptr = std::shared_ptr<ProcessSetupData>; + + ProcessImpl m_processImpl = ProcessImpl::Default; + ProcessMode m_processMode = ProcessMode::Reader; + TerminalMode m_terminalMode = TerminalMode::Off; + + CommandLine m_commandLine; + FilePath m_workingDirectory; + Environment m_environment; + Environment m_remoteEnvironment; + QByteArray m_writeData; + QProcess::ProcessChannelMode m_processChannelMode = QProcess::SeparateChannels; + QVariantHash m_extraData; + QString m_standardInputFile; + QString m_errorString; // partial internal + QString m_nativeArguments; // internal, dependent on specific code path + + bool m_abortOnMetaChars = true; + bool m_runAsRoot = false; + bool m_haveEnv = false; + bool m_lowPriority = false; + bool m_unixTerminalDisabled = false; + bool m_useCtrlCStub = false; // debug only + bool m_belowNormalPriority = false; // internal, dependent on other fields and specific code path +}; + +class QTCREATOR_UTILS_EXPORT ProcessInterface : public QObject +{ + Q_OBJECT + +public: + ProcessInterface(QObject *parent = nullptr) : QObject(parent), m_setup(new ProcessSetupData) {} + ProcessInterface(ProcessSetupData::Ptr setup) : m_setup(setup) {} + + virtual void start() = 0; + virtual void interrupt() = 0; + virtual void terminate() = 0; + virtual void kill() = 0; + virtual void close() = 0; + + virtual QByteArray readAllStandardOutput() = 0; + virtual QByteArray readAllStandardError() = 0; + virtual qint64 write(const QByteArray &data) = 0; + + virtual qint64 processId() const = 0; + virtual QProcess::ProcessState state() const = 0; + virtual int exitCode() const = 0; + virtual QProcess::ExitStatus exitStatus() const = 0; + + virtual QProcess::ProcessError error() const = 0; + virtual QString errorString() const = 0; + virtual void setErrorString(const QString &str) = 0; + + virtual bool waitForStarted(int msecs) = 0; + virtual bool waitForReadyRead(int msecs) = 0; + virtual bool waitForFinished(int msecs) = 0; + + virtual void kickoffProcess(); + virtual qint64 applicationMainThreadID() const; + +signals: + void started(); + void finished(); + void errorOccurred(QProcess::ProcessError error); + void readyReadStandardOutput(); + void readyReadStandardError(); + +protected: + ProcessSetupData::Ptr m_setup; + friend class ProcessProxyInterface; + friend class QtcProcess; +}; + +class QTCREATOR_UTILS_EXPORT ProcessProxyInterface : public ProcessInterface +{ + Q_OBJECT + +public: + ProcessProxyInterface(ProcessInterface *target) + : ProcessInterface(target->m_setup) + , m_target(target) + { + m_target->setParent(this); + connect(m_target, &ProcessInterface::started, this, &ProcessInterface::started); + connect(m_target, &ProcessInterface::finished, this, &ProcessInterface::finished); + connect(m_target, &ProcessInterface::errorOccurred, this, &ProcessInterface::errorOccurred); + connect(m_target, &ProcessInterface::readyReadStandardOutput, + this, &ProcessInterface::readyReadStandardOutput); + connect(m_target, &ProcessInterface::readyReadStandardError, + this, &ProcessInterface::readyReadStandardError); + } + + void start() override { m_target->start(); } + void interrupt() override { m_target->interrupt(); }; + void terminate() override { m_target->terminate(); } + void kill() override { m_target->kill(); } + void close() override { m_target->close(); } + + QByteArray readAllStandardOutput() override { return m_target->readAllStandardOutput(); } + QByteArray readAllStandardError() override { return m_target->readAllStandardError(); } + qint64 write(const QByteArray &data) override { return m_target->write(data); } + + qint64 processId() const override { return m_target->processId(); } + QProcess::ProcessState state() const override { return m_target->state(); } + int exitCode() const override { return m_target->exitCode(); } + QProcess::ExitStatus exitStatus() const override { return m_target->exitStatus(); } + + QProcess::ProcessError error() const override { return m_target->error(); } + QString errorString() const override { return m_target->errorString(); } + void setErrorString(const QString &str) override { m_target->setErrorString(str); } + + bool waitForStarted(int msecs) override { return m_target->waitForStarted(msecs); } + bool waitForReadyRead(int msecs) override { return m_target->waitForReadyRead(msecs); } + bool waitForFinished(int msecs) override { return m_target->waitForFinished(msecs); } + + void kickoffProcess() override { m_target->kickoffProcess(); } + qint64 applicationMainThreadID() const override { return m_target->applicationMainThreadID(); } + +protected: + ProcessInterface *m_target; +}; + + +} // namespace Utils diff --git a/src/libs/utils/processutils.h b/src/libs/utils/processutils.h index 74f3c1c8e20..90405704e1f 100644 --- a/src/libs/utils/processutils.h +++ b/src/libs/utils/processutils.h @@ -25,17 +25,13 @@ #pragma once +#include "processenums.h" + #include <QIODevice> #include <QProcess> namespace Utils { -enum class ProcessMode { - Reader, // This opens in ReadOnly mode if no write data or in ReadWrite mode otherwise, - // closes the write channel afterwards - Writer // This opens in ReadWrite mode and doesn't close the write channel -}; - class ProcessStartHandler { public: ProcessStartHandler(QProcess *process) : m_process(process) {} @@ -83,5 +79,3 @@ private: }; } // namespace Utils - -Q_DECLARE_METATYPE(Utils::ProcessMode); diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index 6e2e38ff7a9..9c32b69dafa 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -32,7 +32,7 @@ #include "launcherpackets.h" #include "launchersocket.h" #include "processreaper.h" -#include "qtcassert.h" +#include "processutils.h" #include "stringutils.h" #include "terminalprocess_p.h" @@ -54,6 +54,7 @@ #include <algorithm> #include <atomic> +#include <chrono> #include <functional> #include <limits> #include <memory> @@ -70,6 +71,10 @@ using namespace Utils::Internal; namespace Utils { namespace Internal { +const char QTC_PROCESS_BLOCKING_TYPE[] = "__BLOCKING_TYPE__"; +const char QTC_PROCESS_NUMBER[] = "__NUMBER__"; +const char QTC_PROCESS_STARTTIME[] = "__STARTTIME__"; + class MeasureAndRun { public: @@ -190,6 +195,8 @@ enum { syncDebug = 0 }; enum { defaultMaxHangTimerCount = 10 }; static Q_LOGGING_CATEGORY(processLog, "qtc.utils.qtcprocess", QtWarningMsg) +static Q_LOGGING_CATEGORY(processStdoutLog, "qtc.utils.qtcprocess.stdout", QtWarningMsg) +static Q_LOGGING_CATEGORY(processStderrLog, "qtc.utils.qtcprocess.stderr", QtWarningMsg) static DeviceProcessHooks s_deviceHooks; @@ -212,87 +219,10 @@ public: bool keepRawData = true; }; -class ProcessInterface : public QObject -{ - Q_OBJECT -public: - ProcessInterface(QObject *parent, ProcessMode processMode) - : QObject(parent) - , m_processMode(processMode) {} - - virtual QByteArray readAllStandardOutput() = 0; - virtual QByteArray readAllStandardError() = 0; - - virtual void setProcessEnvironment(const QProcessEnvironment &environment) = 0; - virtual void setWorkingDirectory(const QString &dir) = 0; - virtual void start(const QString &program, const QStringList &arguments, - const QByteArray &writeData) = 0; - virtual void customStart(const CommandLine &, const FilePath &workingDirectory, - const Environment &) { Q_UNUSED(workingDirectory); QTC_CHECK(false); } - virtual bool isCustomStart() const { return false; } - virtual void terminate() = 0; - virtual void kill() = 0; - virtual void close() = 0; - virtual qint64 write(const QByteArray &data) = 0; - - virtual void setStandardInputFile(const QString &fileName) = 0; - virtual void setProcessChannelMode(QProcess::ProcessChannelMode mode) = 0; - - virtual QString program() const = 0; - virtual QProcess::ProcessError error() const = 0; - virtual QProcess::ProcessState state() const = 0; - virtual qint64 processId() const = 0; - virtual int exitCode() const = 0; - virtual QProcess::ExitStatus exitStatus() const = 0; - virtual QString errorString() const = 0; - virtual void setErrorString(const QString &str) = 0; - - virtual bool waitForStarted(int msecs) = 0; - virtual bool waitForReadyRead(int msecs) = 0; - virtual bool waitForFinished(int msecs) = 0; - - virtual void kickoffProcess() { QTC_CHECK(false); } - virtual void interruptProcess() { QTC_CHECK(false); } - virtual qint64 applicationMainThreadID() const { QTC_CHECK(false); return -1; } - - void setLowPriority() { m_lowPriority = true; } - bool isLowPriority() const { return m_lowPriority; } - void setUnixTerminalDisabled() { m_unixTerminalDisabled = true; } - bool isUnixTerminalDisabled() const { return m_unixTerminalDisabled; } - - void setAbortOnMetaChars(bool abort) { m_abortOnMetaChars = abort; } - bool isAbortOnMetaChars() const { return m_abortOnMetaChars; } - - void setBelowNormalPriority() { m_belowNormalPriority = true; } - bool isBelowNormalPriority() const { return m_belowNormalPriority; } - void setNativeArguments(const QString &arguments) { m_nativeArguments = arguments; } - QString nativeArguments() const { return m_nativeArguments; } - -signals: - void started(); - void finished(int exitCode, QProcess::ExitStatus status); - void errorOccurred(QProcess::ProcessError error); - void readyReadStandardOutput(); - void readyReadStandardError(); - -protected: - ProcessMode processMode() const { return m_processMode; } -private: - const ProcessMode m_processMode; - bool m_belowNormalPriority = false; - QString m_nativeArguments; - bool m_lowPriority = false; - bool m_unixTerminalDisabled = false; - bool m_abortOnMetaChars = true; -}; - class TerminalImpl : public ProcessInterface { public: - TerminalImpl(QObject *parent, QtcProcess::ProcessImpl processImpl, - QtcProcess::TerminalMode terminalMode) - : ProcessInterface(parent, ProcessMode::Reader) - , m_terminal(this, processImpl, terminalMode) + TerminalImpl() : m_terminal(this) { connect(&m_terminal, &Internal::TerminalProcess::started, this, &ProcessInterface::started); @@ -308,30 +238,22 @@ public: QByteArray readAllStandardOutput() override { QTC_CHECK(false); return {}; } QByteArray readAllStandardError() override { QTC_CHECK(false); return {}; } - void setProcessEnvironment(const QProcessEnvironment &) override { QTC_CHECK(false); } - void setWorkingDirectory(const QString &) override { QTC_CHECK(false); } - void start(const QString &, const QStringList &, const QByteArray &) override - { QTC_CHECK(false); } - void customStart(const CommandLine &command, const FilePath &workingDirectory, - const Environment &environment) override + void start() override { - m_terminal.setAbortOnMetaChars(isAbortOnMetaChars()); - m_terminal.setCommand(command); - m_terminal.setWorkingDirectory(workingDirectory); - m_terminal.setEnvironment(environment); + m_terminal.setProcessImpl(m_setup->m_processImpl); + m_terminal.setTerminalMode(m_setup->m_terminalMode); + m_terminal.setAbortOnMetaChars(m_setup->m_abortOnMetaChars); + m_terminal.setCommand(m_setup->m_commandLine); + m_terminal.setWorkingDirectory(m_setup->m_workingDirectory); + m_terminal.setEnvironment(m_setup->m_environment); m_terminal.start(); } - bool isCustomStart() const override { return true; } + void interrupt() override { m_terminal.interrupt(); } void terminate() override { m_terminal.stopProcess(); } void kill() override { m_terminal.stopProcess(); } void close() override { m_terminal.stopProcess(); } qint64 write(const QByteArray &) override { QTC_CHECK(false); return -1; } - void setStandardInputFile(const QString &fileName) override { Q_UNUSED(fileName) QTC_CHECK(false); } - // intentionally no-op without an assert - void setProcessChannelMode(QProcess::ProcessChannelMode mode) override { Q_UNUSED(mode) } - - QString program() const override { QTC_CHECK(false); return {}; } QProcess::ProcessError error() const override { return m_terminal.error(); } QProcess::ProcessState state() const override { return m_terminal.state(); } qint64 processId() const override { return m_terminal.processId(); } @@ -347,19 +269,128 @@ public: bool waitForFinished(int) override { return false; } void kickoffProcess() override { m_terminal.kickoffProcess(); } - void interruptProcess() override { m_terminal.interruptProcess(); } qint64 applicationMainThreadID() const override { return m_terminal.applicationMainThreadID(); } private: Internal::TerminalProcess m_terminal; }; -class QProcessImpl : public ProcessInterface +class DefaultImpl : public ProcessInterface +{ +public: + virtual void start() { defaultStart(); } + +protected: + void defaultStart(); + +private: + virtual void doDefaultStart(const QString &program, const QStringList &arguments) = 0; + bool dissolveCommand(QString *program, QStringList *arguments); + bool ensureProgramExists(const QString &program); +}; + +static QString blockingMessage(const QVariant &variant) +{ + if (!variant.isValid()) + return "non blocking"; + if (variant.toInt() == int(EventLoopMode::On)) + return "blocking with event loop"; + return "blocking without event loop"; +} + +void DefaultImpl::defaultStart() +{ + if (processLog().isDebugEnabled()) { + using namespace std::chrono; + const quint64 msSinceEpoc = + duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); + setProperty(QTC_PROCESS_STARTTIME, msSinceEpoc); + + static std::atomic_int startCounter = 0; + const int currentNumber = startCounter.fetch_add(1); + qCDebug(processLog).nospace().noquote() + << "Process " << currentNumber << " starting (" + << qPrintable(blockingMessage(property(QTC_PROCESS_BLOCKING_TYPE))) + << "): " + << m_setup->m_commandLine.toUserOutput(); + setProperty(QTC_PROCESS_NUMBER, currentNumber); + } + + QString program; + QStringList arguments; + if (!dissolveCommand(&program, &arguments)) + return; + if (!ensureProgramExists(program)) + return; + s_start.measureAndRun(&DefaultImpl::doDefaultStart, this, program, arguments); +} + +bool DefaultImpl::dissolveCommand(QString *program, QStringList *arguments) +{ + const CommandLine &commandLine = m_setup->m_commandLine; + QString commandString; + ProcessArgs processArgs; + const bool success = ProcessArgs::prepareCommand(commandLine, &commandString, &processArgs, + &m_setup->m_environment, + &m_setup->m_workingDirectory); + + if (commandLine.executable().osType() == OsTypeWindows) { + QString args; + if (m_setup->m_useCtrlCStub) { + if (m_setup->m_lowPriority) + ProcessArgs::addArg(&args, "-nice"); + ProcessArgs::addArg(&args, QDir::toNativeSeparators(commandString)); + commandString = QCoreApplication::applicationDirPath() + + QLatin1String("/qtcreator_ctrlc_stub.exe"); + } else if (m_setup->m_lowPriority) { + m_setup->m_belowNormalPriority = true; + } + ProcessArgs::addArgs(&args, processArgs.toWindowsArgs()); + m_setup->m_nativeArguments = args; + // Note: Arguments set with setNativeArgs will be appended to the ones + // passed with start() below. + *arguments = QStringList(); + } else { + if (!success) { + setErrorString(tr("Error in command line.")); + // TODO: in fact it's WrongArgumentsFailure + emit errorOccurred(QProcess::FailedToStart); + return false; + } + *arguments = processArgs.toUnixArgs(); + } + *program = commandString; + return true; +} + +static FilePath resolve(const FilePath &workingDir, const FilePath &filePath) +{ + if (filePath.isAbsolutePath()) + return filePath; + + const FilePath fromWorkingDir = workingDir.resolvePath(filePath); + if (fromWorkingDir.exists() && fromWorkingDir.isExecutableFile()) + return fromWorkingDir; + return filePath.searchInPath(); +} + +bool DefaultImpl::ensureProgramExists(const QString &program) +{ + const FilePath programFilePath = resolve(m_setup->m_workingDirectory, + FilePath::fromString(program)); + if (programFilePath.exists() && programFilePath.isExecutableFile()) + return true; + + setErrorString(QLatin1String("The program \"%1\" does not exist or is not executable.") + .arg(program)); + emit errorOccurred(QProcess::FailedToStart); + return false; +} + +class QProcessImpl : public DefaultImpl { public: - QProcessImpl(QObject *parent, ProcessMode processMode) - : ProcessInterface(parent, processMode) - , m_process(new ProcessHelper(parent)) + QProcessImpl() : m_process(new ProcessHelper(this)) { connect(m_process, &QProcess::started, this, &QProcessImpl::handleStarted); @@ -380,25 +411,8 @@ public: QByteArray readAllStandardOutput() override { return m_process->readAllStandardOutput(); } QByteArray readAllStandardError() override { return m_process->readAllStandardError(); } - void setProcessEnvironment(const QProcessEnvironment &environment) override - { m_process->setProcessEnvironment(environment); } - void setWorkingDirectory(const QString &dir) override - { m_process->setWorkingDirectory(dir); } - void start(const QString &program, const QStringList &arguments, const QByteArray &writeData) override - { - ProcessStartHandler *handler = m_process->processStartHandler(); - handler->setProcessMode(processMode()); - handler->setWriteData(writeData); - if (isBelowNormalPriority()) - handler->setBelowNormalPriority(); - handler->setNativeArguments(nativeArguments()); - if (isLowPriority()) - m_process->setLowPriority(); - if (isUnixTerminalDisabled()) - m_process->setUnixTerminalDisabled(); - m_process->start(program, arguments, handler->openMode()); - handler->handleProcessStart(); - } + void interrupt() override + { QTC_CHECK(false); } // TODO: provide default impl void terminate() override { m_process->terminate(); } void kill() override @@ -408,13 +422,6 @@ public: qint64 write(const QByteArray &data) override { return m_process->write(data); } - void setStandardInputFile(const QString &fileName) override - { m_process->setStandardInputFile(fileName); } - void setProcessChannelMode(QProcess::ProcessChannelMode mode) override - { m_process->setProcessChannelMode(mode); } - - QString program() const override - { return m_process->program(); } QProcess::ProcessError error() const override { return m_process->error(); } QProcess::ProcessState state() const override @@ -438,6 +445,27 @@ public: { return m_process->waitForFinished(msecs); } private: + void doDefaultStart(const QString &program, const QStringList &arguments) override + { + ProcessStartHandler *handler = m_process->processStartHandler(); + handler->setProcessMode(m_setup->m_processMode); + handler->setWriteData(m_setup->m_writeData); + if (m_setup->m_belowNormalPriority) + handler->setBelowNormalPriority(); + handler->setNativeArguments(m_setup->m_nativeArguments); + m_process->setProcessEnvironment(m_setup->m_environment.toProcessEnvironment()); + m_process->setWorkingDirectory(m_setup->m_workingDirectory.path()); + m_process->setStandardInputFile(m_setup->m_standardInputFile); + m_process->setProcessChannelMode(m_setup->m_processChannelMode); + m_process->setErrorString(m_setup->m_errorString); + if (m_setup->m_lowPriority) + m_process->setLowPriority(); + if (m_setup->m_unixTerminalDisabled) + m_process->setUnixTerminalDisabled(); + m_process->start(program, arguments, handler->openMode()); + handler->handleProcessStart(); + } + void handleStarted() { m_process->processStartHandler()->handleProcessStarted(); @@ -452,14 +480,14 @@ static uint uniqueToken() return ++globalUniqueToken; } -class ProcessLauncherImpl : public ProcessInterface +class ProcessLauncherImpl : public DefaultImpl { Q_OBJECT public: - ProcessLauncherImpl(QObject *parent, ProcessMode processMode) - : ProcessInterface(parent, processMode), m_token(uniqueToken()) + ProcessLauncherImpl() : m_token(uniqueToken()) { - m_handle = LauncherInterface::registerHandle(parent, token(), processMode); + m_handle = LauncherInterface::registerHandle(this, token()); + m_handle->setProcessSetupData(m_setup); connect(m_handle, &CallerHandle::errorOccurred, this, &ProcessInterface::errorOccurred); connect(m_handle, &CallerHandle::started, @@ -481,29 +509,13 @@ public: QByteArray readAllStandardOutput() override { return m_handle->readAllStandardOutput(); } QByteArray readAllStandardError() override { return m_handle->readAllStandardError(); } - void setProcessEnvironment(const QProcessEnvironment &environment) override - { m_handle->setProcessEnvironment(environment); } - void setWorkingDirectory(const QString &dir) override { m_handle->setWorkingDirectory(dir); } - void start(const QString &program, const QStringList &arguments, const QByteArray &writeData) override - { - if (isBelowNormalPriority()) - m_handle->setBelowNormalPriority(); - m_handle->setNativeArguments(nativeArguments()); - if (isLowPriority()) - m_handle->setLowPriority(); - if (isUnixTerminalDisabled()) - m_handle->setUnixTerminalDisabled(); - m_handle->start(program, arguments, writeData); - } + void interrupt() override + { QTC_CHECK(false); } // TODO: send it to process launcher and use there a default impl of QProcessImpl void terminate() override { cancel(); } // TODO: what are differences among terminate, kill and close? void kill() override { cancel(); } // TODO: see above void close() override { cancel(); } // TODO: see above qint64 write(const QByteArray &data) override { return m_handle->write(data); } - void setStandardInputFile(const QString &fileName) override { m_handle->setStandardInputFile(fileName); } - void setProcessChannelMode(QProcess::ProcessChannelMode mode) override { m_handle->setProcessChannelMode(mode); } - - QString program() const override { return m_handle->program(); } QProcess::ProcessError error() const override { return m_handle->error(); } QProcess::ProcessState state() const override { return m_handle->state(); } qint64 processId() const override { return m_handle->processId(); } @@ -517,13 +529,13 @@ public: bool waitForFinished(int msecs) override { return m_handle->waitForFinished(msecs); } private: - typedef void (ProcessLauncherImpl::*PreSignal)(void); + void doDefaultStart(const QString &program, const QStringList &arguments) override + { + m_handle->start(program, arguments); + } void cancel(); - void handleSocketError(const QString &message); - void handleSocketReady(); - quintptr token() const { return m_token; } const uint m_token = 0; @@ -536,14 +548,11 @@ void ProcessLauncherImpl::cancel() m_handle->cancel(); } -static ProcessInterface *newProcessInstance(QObject *parent, QtcProcess::ProcessImpl processImpl, - ProcessMode mode, QtcProcess::TerminalMode terminalMode) +static ProcessImpl defaultProcessImpl() { - if (terminalMode != QtcProcess::TerminalOff) - return new TerminalImpl(parent, processImpl, terminalMode); - if (processImpl == QtcProcess::QProcessImpl) - return new QProcessImpl(parent, mode); - return new ProcessLauncherImpl(parent, mode); + if (qEnvironmentVariableIsSet("QTC_USE_QPROCESS")) + return ProcessImpl::QProcess; + return ProcessImpl::ProcessLauncher; } class QtcProcessPrivate : public QObject @@ -551,29 +560,43 @@ class QtcProcessPrivate : public QObject public: enum StartFailure { NoFailure, - WrongFileNameFailure, + WrongCommandFailure, OtherFailure }; - explicit QtcProcessPrivate(QtcProcess *parent, - QtcProcess::ProcessImpl processImpl, - ProcessMode processMode, - QtcProcess::TerminalMode terminalMode) + explicit QtcProcessPrivate(QtcProcess *parent, const ProcessSetupData::Ptr &setup) : QObject(parent) , q(parent) - , m_process(newProcessInstance(parent, processImpl, processMode, terminalMode)) - , m_processMode(processMode) - , m_terminalMode(terminalMode) + , m_setup(setup) + {} + + ProcessInterface *createProcessInterface() + { + if (m_setup->m_terminalMode != TerminalMode::Off) + return new TerminalImpl(); + + const ProcessImpl impl = m_setup->m_processImpl == ProcessImpl::Default + ? defaultProcessImpl() : m_setup->m_processImpl; + if (impl == ProcessImpl::QProcess) + return new QProcessImpl(); + return new ProcessLauncherImpl(); + } + + void setProcessInterface(ProcessInterface *process) { - connect(m_process, &ProcessInterface::started, - q, &QtcProcess::started); - connect(m_process, &ProcessInterface::finished, + m_process.reset(process); + m_setup->m_errorString.clear(); + m_process->setParent(this); + + connect(m_process.get(), &ProcessInterface::started, + q, &QtcProcess::emitStarted); + connect(m_process.get(), &ProcessInterface::finished, this, &QtcProcessPrivate::slotFinished); - connect(m_process, &ProcessInterface::errorOccurred, - this, [this](QProcess::ProcessError error) { handleError(error, OtherFailure); }); - connect(m_process, &ProcessInterface::readyReadStandardOutput, + connect(m_process.get(), &ProcessInterface::errorOccurred, + this, &QtcProcessPrivate::handleError); + connect(m_process.get(), &ProcessInterface::readyReadStandardOutput, this, &QtcProcessPrivate::handleReadyReadStandardOutput); - connect(m_process, &ProcessInterface::readyReadStandardError, + connect(m_process.get(), &ProcessInterface::readyReadStandardError, this, &QtcProcessPrivate::handleReadyReadStandardError); } @@ -591,93 +614,23 @@ public: emit q->readyReadStandardError(); } - FilePath resolve(const FilePath &workingDir, const FilePath &filePath) const - { - if (filePath.isAbsolutePath()) - return filePath; - - const FilePath fromWorkingDir = workingDir.resolvePath(filePath); - if (fromWorkingDir.exists() && fromWorkingDir.isExecutableFile()) - return fromWorkingDir; - return filePath.searchInPath(); - } - - void defaultStart(const CommandLine &commandLine, const FilePath &workingDirectory, - const Environment &environment) - { - if (processLog().isDebugEnabled()) { - static int n = 0; - qCDebug(processLog) << "STARTING PROCESS: " << ++n << " " << commandLine.toUserOutput(); - } - - m_process->setProcessEnvironment(environment.toProcessEnvironment()); - m_process->setWorkingDirectory(workingDirectory.path()); - - QString commandString; - ProcessArgs arguments; - const bool success = ProcessArgs::prepareCommand(commandLine, &commandString, &arguments, - &environment, &workingDirectory); - - if (commandLine.executable().osType() == OsTypeWindows) { - QString args; - if (m_useCtrlCStub) { - if (m_process->isLowPriority()) - ProcessArgs::addArg(&args, "-nice"); - ProcessArgs::addArg(&args, QDir::toNativeSeparators(commandString)); - commandString = QCoreApplication::applicationDirPath() - + QLatin1String("/qtcreator_ctrlc_stub.exe"); - } else if (m_process->isLowPriority()) { - m_process->setBelowNormalPriority(); - } - ProcessArgs::addArgs(&args, arguments.toWindowsArgs()); -#ifdef Q_OS_WIN - m_process->setNativeArguments(args); -#endif - // Note: Arguments set with setNativeArgs will be appended to the ones - // passed with start() below. - start(commandString, QStringList(), workingDirectory, m_writeData); - } else { - if (!success) { - q->setErrorString(QtcProcess::tr("Error in command line.")); - // Should be FailedToStart, but we cannot set the process error from the outside, - // so it would be inconsistent. - emit q->errorOccurred(QProcess::UnknownError); - return; - } - start(commandString, arguments.toUnixArgs(), workingDirectory, m_writeData); - } - } - - void start(const QString &program, const QStringList &arguments, - const FilePath &workingDirectory, const QByteArray &writeData) - { - const FilePath programFilePath = resolve(workingDirectory, FilePath::fromString(program)); - if (programFilePath.exists() && programFilePath.isExecutableFile()) { - s_start.measureAndRun(&ProcessInterface::start, m_process, program, arguments, writeData); - } else { - m_process->setErrorString(QLatin1String( - "The program \"%1\" does not exist or is not executable.").arg(program)); - handleError(QProcess::FailedToStart, WrongFileNameFailure); - } - } - CommandLine fullCommandLine() const { - if (!m_runAsRoot || HostOsInfo::isWindowsHost()) - return m_commandLine; + if (!m_setup->m_runAsRoot || HostOsInfo::isWindowsHost()) + return m_setup->m_commandLine; CommandLine rootCommand("sudo", {"-A"}); - rootCommand.addCommandLineAsArgs(m_commandLine); + rootCommand.addCommandLineAsArgs(m_setup->m_commandLine); return rootCommand; } Environment fullEnvironment() const { Environment env; - if (m_haveEnv) { - if (m_environment.size() == 0) + if (m_setup->m_haveEnv) { + if (m_setup->m_environment.size() == 0) qWarning("QtcProcess::start: Empty environment set when running '%s'.", - qPrintable(m_commandLine.executable().toString())); - env = m_environment; + qPrintable(m_setup->m_commandLine.executable().toString())); + env = m_setup->m_environment; } else { env = Environment::systemEnvironment(); } @@ -689,27 +642,20 @@ public: } QtcProcess *q; - ProcessInterface *m_process; - const ProcessMode m_processMode; - const QtcProcess::TerminalMode m_terminalMode; - CommandLine m_commandLine; - FilePath m_workingDirectory; - Environment m_environment; - QByteArray m_writeData; - bool m_runAsRoot = false; - bool m_haveEnv = false; - bool m_useCtrlCStub = false; + std::unique_ptr<ProcessInterface> m_process; + ProcessSetupData::Ptr m_setup; void slotTimeout(); - void slotFinished(int exitCode, QProcess::ExitStatus e); - void handleError(QProcess::ProcessError error, StartFailure startFailure); + void slotFinished(); + void handleFinished(int exitCode, QProcess::ExitStatus status); + void handleError(QProcess::ProcessError error); void clearForRun(); - QtcProcess::Result interpretExitCode(int exitCode); + ProcessResult interpretExitCode(int exitCode); QTextCodec *m_codec = QTextCodec::codecForLocale(); QEventLoop *m_eventLoop = nullptr; - QtcProcess::Result m_result = QtcProcess::StartFailed; + ProcessResult m_result = ProcessResult::StartFailed; ChannelBuffer m_stdOut; ChannelBuffer m_stdErr; ExitCodeInterpreter m_exitCodeInterpreter; @@ -728,21 +674,31 @@ void QtcProcessPrivate::clearForRun() m_stdOut.codec = m_codec; m_stdErr.clearForRun(); m_stdErr.codec = m_codec; - m_result = QtcProcess::StartFailed; + m_result = ProcessResult::StartFailed; m_startFailure = NoFailure; } -QtcProcess::Result QtcProcessPrivate::interpretExitCode(int exitCode) +ProcessResult QtcProcessPrivate::interpretExitCode(int exitCode) { if (m_exitCodeInterpreter) return m_exitCodeInterpreter(exitCode); // default: - return exitCode ? QtcProcess::FinishedWithError : QtcProcess::FinishedWithSuccess; + return exitCode ? ProcessResult::FinishedWithError : ProcessResult::FinishedWithSuccess; } } // Internal +void ProcessInterface::kickoffProcess() +{ + QTC_CHECK(false); +} + +qint64 ProcessInterface::applicationMainThreadID() const +{ + QTC_CHECK(false); return -1; +} + /*! \class Utils::QtcProcess @@ -751,90 +707,154 @@ QtcProcess::Result QtcProcessPrivate::interpretExitCode(int exitCode) \sa Utils::ProcessArgs */ -static QtcProcess::ProcessImpl defaultProcessImpl() -{ - if (qEnvironmentVariableIsSet("QTC_USE_QPROCESS")) - return QtcProcess::QProcessImpl; - return QtcProcess::ProcessLauncherImpl; -} - -QtcProcess::QtcProcess(const Setup &setup, QObject *parent) - : QObject(parent), - d(new QtcProcessPrivate(this, - setup.processImpl == DefaultImpl ? defaultProcessImpl() : setup.processImpl, - setup.processMode, setup.terminalMode)) +QtcProcess::QtcProcess(QObject *parent) + : ProcessInterface(parent), + d(new QtcProcessPrivate(this, m_setup)) { static int qProcessExitStatusMeta = qRegisterMetaType<QProcess::ExitStatus>(); static int qProcessProcessErrorMeta = qRegisterMetaType<QProcess::ProcessError>(); Q_UNUSED(qProcessExitStatusMeta) Q_UNUSED(qProcessProcessErrorMeta) -} -QtcProcess::QtcProcess(QObject *parent) - : QtcProcess({}, parent) -{} + if (processLog().isDebugEnabled()) { + connect(this, &QtcProcess::finished, [this] { + if (!d->m_process.get()) + return; + const QVariant n = d->m_process.get()->property(QTC_PROCESS_NUMBER); + if (!n.isValid()) + return; + using namespace std::chrono; + const quint64 msSinceEpoc = + duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); + const quint64 msStarted = + d->m_process.get()->property(QTC_PROCESS_STARTTIME).toULongLong(); + const quint64 msElapsed = msSinceEpoc - msStarted; + + const int number = n.toInt(); + qCDebug(processLog).nospace() << "Process " << number << " finished: " + << "result=" << int(result()) + << ", ex=" << exitCode() + << ", " << stdOut().size() << " bytes stdout" + << ", " << stdErr().size() << " bytes stderr" + << ", " << msElapsed << " ms elapsed"; + if (processStdoutLog().isDebugEnabled() && !stdOut().isEmpty()) + qCDebug(processStdoutLog).nospace() + << "Process " << number << " sdout: " << stdOut(); + if (processStderrLog().isDebugEnabled() && !stdErr().isEmpty()) + qCDebug(processStderrLog).nospace() + << "Process " << number << " stderr: " << stdErr(); + }); + } +} QtcProcess::~QtcProcess() { delete d; } +void QtcProcess::emitStarted() +{ + emit started(); +} + +void QtcProcess::emitFinished() +{ + emit finished(); +} + +void QtcProcess::emitErrorOccurred(QProcess::ProcessError error) +{ + emit errorOccurred(error); +} + +void QtcProcess::setProcessInterface(ProcessInterface *interface) +{ + d->setProcessInterface(interface); + // Make a copy, don't share, until we get rid of fullCommandLine() and fullEnvironment() + *d->m_process->m_setup = *d->m_setup; +} + +void QtcProcess::setProcessImpl(ProcessImpl processImpl) +{ + d->m_setup->m_processImpl = processImpl; +} + ProcessMode QtcProcess::processMode() const { - return d->m_processMode; + return d->m_setup->m_processMode; } -QtcProcess::TerminalMode QtcProcess::terminalMode() const +void QtcProcess::setTerminalMode(TerminalMode mode) { - return d->m_terminalMode; + d->m_setup->m_terminalMode = mode; +} + +TerminalMode QtcProcess::terminalMode() const +{ + return d->m_setup->m_terminalMode; +} + +void QtcProcess::setProcessMode(ProcessMode processMode) +{ + d->m_setup->m_processMode = processMode; } void QtcProcess::setEnvironment(const Environment &env) { - d->m_environment = env; - d->m_haveEnv = true; + d->m_setup->m_environment = env; + d->m_setup->m_haveEnv = true; } void QtcProcess::unsetEnvironment() { - d->m_environment = Environment(); - d->m_haveEnv = false; + d->m_setup->m_environment = Environment(); + d->m_setup->m_haveEnv = false; } const Environment &QtcProcess::environment() const { - return d->m_environment; + return d->m_setup->m_environment; } bool QtcProcess::hasEnvironment() const { - return d->m_haveEnv; + return d->m_setup->m_haveEnv; +} + +void QtcProcess::setRemoteEnvironment(const Environment &environment) +{ + d->m_setup->m_remoteEnvironment = environment; +} + +Environment QtcProcess::remoteEnvironment() const +{ + return d->m_setup->m_remoteEnvironment; } void QtcProcess::setCommand(const CommandLine &cmdLine) { - if (d->m_workingDirectory.needsDevice() && cmdLine.executable().needsDevice()) { - QTC_CHECK(d->m_workingDirectory.host() == cmdLine.executable().host()); + if (d->m_setup->m_workingDirectory.needsDevice() && cmdLine.executable().needsDevice()) { + QTC_CHECK(d->m_setup->m_workingDirectory.host() == cmdLine.executable().host()); } - d->m_commandLine = cmdLine; + d->m_setup->m_commandLine = cmdLine; } const CommandLine &QtcProcess::commandLine() const { - return d->m_commandLine; + return d->m_setup->m_commandLine; } FilePath QtcProcess::workingDirectory() const { - return d->m_workingDirectory; + return d->m_setup->m_workingDirectory; } void QtcProcess::setWorkingDirectory(const FilePath &dir) { - if (dir.needsDevice() && d->m_commandLine.executable().needsDevice()) { - QTC_CHECK(dir.host() == d->m_commandLine.executable().host()); + if (dir.needsDevice() && d->m_setup->m_commandLine.executable().needsDevice()) { + QTC_CHECK(dir.host() == d->m_setup->m_commandLine.executable().host()); } - d->m_workingDirectory = dir; + d->m_setup->m_workingDirectory = dir; } void QtcProcess::setUseCtrlCStub(bool enabled) @@ -843,7 +863,7 @@ void QtcProcess::setUseCtrlCStub(bool enabled) // Qt Creator otherwise, because they share the same Windows console. // See QTCREATORBUG-11995 for details. #ifndef QT_DEBUG - d->m_useCtrlCStub = enabled; + d->m_setup->m_useCtrlCStub = enabled; #else Q_UNUSED(enabled) #endif @@ -851,18 +871,32 @@ void QtcProcess::setUseCtrlCStub(bool enabled) void QtcProcess::start() { - if (d->m_commandLine.executable().needsDevice()) { - QTC_ASSERT(s_deviceHooks.startProcessHook, return); - s_deviceHooks.startProcessHook(*this); - return; +// TODO: Uncomment when we de-virtualize start() +// QTC_ASSERT(state() == QProcess::NotRunning, return); + + ProcessInterface *processImpl = nullptr; + if (d->m_setup->m_commandLine.executable().needsDevice()) { + if (s_deviceHooks.processImplHook) { // TODO: replace "if" with an assert for the hook + processImpl = s_deviceHooks.processImplHook(commandLine().executable()); + } + if (!processImpl) { // TODO: remove this branch when docker is adapted accordingly + QTC_ASSERT(s_deviceHooks.startProcessHook, return); + s_deviceHooks.startProcessHook(*this); + return; + } + } else { + processImpl = d->createProcessInterface(); } + QTC_ASSERT(processImpl, return); + setProcessInterface(processImpl); d->clearForRun(); - const CommandLine cmd = d->fullCommandLine(); - const Environment env = d->fullEnvironment(); - if (d->m_process->isCustomStart()) - d->m_process->customStart(cmd, d->m_workingDirectory, env); - else - d->defaultStart(cmd, d->m_workingDirectory, env); + d->m_process->m_setup->m_commandLine = d->fullCommandLine(); + d->m_process->m_setup->m_environment = d->fullEnvironment(); + if (processLog().isDebugEnabled()) { + // Pass a dynamic property with info about blocking type + d->m_process->setProperty(QTC_PROCESS_BLOCKING_TYPE, property(QTC_PROCESS_BLOCKING_TYPE)); + } + d->m_process->start(); } #ifdef Q_OS_WIN @@ -893,19 +927,23 @@ BOOL CALLBACK sendInterruptMessageToAllWindowsOfProcess_enumWnd(HWND hwnd, LPARA void QtcProcess::terminate() { #ifdef Q_OS_WIN - if (d->m_useCtrlCStub) + if (d->m_setup->m_useCtrlCStub) EnumWindows(sendShutDownMessageToAllWindowsOfProcess_enumWnd, processId()); else #endif - d->m_process->terminate(); + if (d->m_process) + d->m_process->terminate(); } void QtcProcess::interrupt() { #ifdef Q_OS_WIN - QTC_ASSERT(d->m_useCtrlCStub, return); - EnumWindows(sendInterruptMessageToAllWindowsOfProcess_enumWnd, processId()); + if (d->m_setup->m_useCtrlCStub) + EnumWindows(sendInterruptMessageToAllWindowsOfProcess_enumWnd, processId()); + else #endif + if (d->m_process) + d->m_process->interrupt(); } bool QtcProcess::startDetached(const CommandLine &cmd, const FilePath &workingDirectory, qint64 *pid) @@ -918,53 +956,73 @@ bool QtcProcess::startDetached(const CommandLine &cmd, const FilePath &workingDi void QtcProcess::setLowPriority() { - d->m_process->setLowPriority(); + d->m_setup->m_lowPriority = true; } void QtcProcess::setDisableUnixTerminal() { - d->m_process->setUnixTerminalDisabled(); + d->m_setup->m_unixTerminalDisabled = true; } void QtcProcess::setAbortOnMetaChars(bool abort) { - d->m_process->setAbortOnMetaChars(abort); + d->m_setup->m_abortOnMetaChars = abort; } void QtcProcess::setRunAsRoot(bool on) { - d->m_runAsRoot = on; + d->m_setup->m_runAsRoot = on; } bool QtcProcess::isRunAsRoot() const { - return d->m_runAsRoot; + return d->m_setup->m_runAsRoot; } void QtcProcess::setStandardInputFile(const QString &inputFile) { - d->m_process->setStandardInputFile(inputFile); + d->m_setup->m_standardInputFile = inputFile; } QString QtcProcess::toStandaloneCommandLine() const { QStringList parts; parts.append("/usr/bin/env"); - if (!d->m_workingDirectory.isEmpty()) { + if (!d->m_setup->m_workingDirectory.isEmpty()) { parts.append("-C"); - d->m_workingDirectory.path(); + d->m_setup->m_workingDirectory.path(); } parts.append("-i"); - if (d->m_environment.size() > 0) { - const QStringList envVars = d->m_environment.toStringList(); + if (d->m_setup->m_environment.size() > 0) { + const QStringList envVars = d->m_setup->m_environment.toStringList(); std::transform(envVars.cbegin(), envVars.cend(), std::back_inserter(parts), ProcessArgs::quoteArgUnix); } - parts.append(d->m_commandLine.executable().path()); - parts.append(d->m_commandLine.splitArguments()); + parts.append(d->m_setup->m_commandLine.executable().path()); + parts.append(d->m_setup->m_commandLine.splitArguments()); return parts.join(" "); } +void QtcProcess::setExtraData(const QString &key, const QVariant &value) +{ + d->m_setup->m_extraData.insert(key, value); +} + +QVariant QtcProcess::extraData(const QString &key) const +{ + return d->m_setup->m_extraData.value(key); +} + +void QtcProcess::setExtraData(const QVariantHash &extraData) +{ + d->m_setup->m_extraData = extraData; +} + +QVariantHash QtcProcess::extraData() const +{ + return d->m_setup->m_extraData; +} + void QtcProcess::setRemoteProcessHooks(const DeviceProcessHooks &hooks) { s_deviceHooks = hooks; @@ -1047,7 +1105,7 @@ bool QtcProcess::readDataFromProcess(int timeoutS, } // Prompt user, pretend we have data if says 'No'. const bool hang = !hasData && !finished; - hasData = hang && showTimeOutMessageBox && !askToKill(d->m_process->program()); + hasData = hang && showTimeOutMessageBox && !askToKill(d->m_setup->m_commandLine.executable().path()); } while (hasData && !finished); if (syncDebug) qDebug() << "<readDataFromProcess" << finished; @@ -1065,21 +1123,23 @@ QString QtcProcess::normalizeNewlines(const QString &text) return res; } -QtcProcess::Result QtcProcess::result() const +ProcessResult QtcProcess::result() const { return d->m_result; } -void QtcProcess::setResult(Result result) +void QtcProcess::setResult(const ProcessResult &result) { d->m_result = result; } int QtcProcess::exitCode() const { - if (d->m_startFailure == QtcProcessPrivate::WrongFileNameFailure) + if (d->m_startFailure == QtcProcessPrivate::WrongCommandFailure) return 255; // This code is being returned by QProcess when FailedToStart error occurred - return d->m_process->exitCode(); + if (d->m_process) + return d->m_process->exitCode(); + return 0; } @@ -1174,34 +1234,36 @@ Environment QtcProcess::systemEnvironmentForBinary(const FilePath &filePath) void QtcProcess::kickoffProcess() { - d->m_process->kickoffProcess(); -} - -void QtcProcess::interruptProcess() -{ - d->m_process->interruptProcess(); + if (d->m_process) + d->m_process->kickoffProcess(); } qint64 QtcProcess::applicationMainThreadID() const { - return d->m_process->applicationMainThreadID(); + if (d->m_process) + return d->m_process->applicationMainThreadID(); + return -1; } void QtcProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode) { - d->m_process->setProcessChannelMode(mode); + d->m_setup->m_processChannelMode = mode; } QProcess::ProcessError QtcProcess::error() const { - if (d->m_startFailure == QtcProcessPrivate::WrongFileNameFailure) + if (d->m_startFailure == QtcProcessPrivate::WrongCommandFailure) return QProcess::FailedToStart; - return d->m_process->error(); + if (d->m_process) + return d->m_process->error(); + return QProcess::UnknownError; } QProcess::ProcessState QtcProcess::state() const { - return d->m_process->state(); + if (d->m_process) + return d->m_process->state(); + return QProcess::NotRunning; } bool QtcProcess::isRunning() const @@ -1211,31 +1273,41 @@ bool QtcProcess::isRunning() const QString QtcProcess::errorString() const { - return d->m_process->errorString(); + if (d->m_process) + return d->m_process->errorString(); + return d->m_setup->m_errorString; } void QtcProcess::setErrorString(const QString &str) { - d->m_process->setErrorString(str); + if (d->m_process) + d->m_process->setErrorString(str); + else + d->m_setup->m_errorString = str; } qint64 QtcProcess::processId() const { - return d->m_process->processId(); + if (d->m_process) + return d->m_process->processId(); + return 0; } bool QtcProcess::waitForStarted(int msecs) { + QTC_ASSERT(d->m_process, return false); return s_waitForStarted.measureAndRun(&ProcessInterface::waitForStarted, d->m_process, msecs); } bool QtcProcess::waitForReadyRead(int msecs) { + QTC_ASSERT(d->m_process, return false); return d->m_process->waitForReadyRead(msecs); } bool QtcProcess::waitForFinished(int msecs) { + QTC_ASSERT(d->m_process, return false); return d->m_process->waitForFinished(msecs); } @@ -1255,23 +1327,28 @@ QByteArray QtcProcess::readAllStandardError() QProcess::ExitStatus QtcProcess::exitStatus() const { - return d->m_process->exitStatus(); + if (d->m_process) + return d->m_process->exitStatus(); + return QProcess::NormalExit; } void QtcProcess::kill() { - d->m_process->kill(); + if (d->m_process) + d->m_process->kill(); } qint64 QtcProcess::write(const QByteArray &input) { QTC_ASSERT(processMode() == ProcessMode::Writer, return -1); + QTC_ASSERT(d->m_process, return -1); return d->m_process->write(input); } void QtcProcess::close() { - d->m_process->close(); + if (d->m_process) + d->m_process->close(); } void QtcProcess::beginFeed() @@ -1281,7 +1358,7 @@ void QtcProcess::beginFeed() void QtcProcess::endFeed() { - d->slotFinished(0, QProcess::NormalExit); + d->handleFinished(0, QProcess::NormalExit); } void QtcProcess::feedStdOut(const QByteArray &data) @@ -1297,7 +1374,6 @@ QString QtcProcess::locateBinary(const QString &binary) return locateBinary(QString::fromLocal8Bit(path), binary); } - /*! \class Utils::SynchronousProcess @@ -1332,16 +1408,16 @@ QString QtcProcess::exitMessage() { const QString fullCmd = commandLine().toUserOutput(); switch (result()) { - case FinishedWithSuccess: + case ProcessResult::FinishedWithSuccess: return QtcProcess::tr("The command \"%1\" finished successfully.").arg(fullCmd); - case FinishedWithError: + case ProcessResult::FinishedWithError: return QtcProcess::tr("The command \"%1\" terminated with exit code %2.") .arg(fullCmd).arg(exitCode()); - case TerminatedAbnormally: + case ProcessResult::TerminatedAbnormally: return QtcProcess::tr("The command \"%1\" terminated abnormally.").arg(fullCmd); - case StartFailed: + case ProcessResult::StartFailed: return QtcProcess::tr("The command \"%1\" could not be started.").arg(fullCmd); - case Hang: + case ProcessResult::Hang: return QtcProcess::tr("The command \"%1\" did not respond within the timeout limit (%2 s).") .arg(fullCmd).arg(d->m_maxHangTimerCount); } @@ -1405,7 +1481,7 @@ QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const QtcProcess &r) { QDebug nsp = str.nospace(); nsp << "QtcProcess: result=" - << r.d->m_result << " ex=" << r.exitCode() << '\n' + << int(r.d->m_result) << " ex=" << r.exitCode() << '\n' << r.d->m_stdOut.rawData.size() << " bytes stdout, stderr=" << r.d->m_stdErr.rawData << '\n'; return str; } @@ -1506,7 +1582,7 @@ void QtcProcess::setExitCodeInterpreter(const ExitCodeInterpreter &interpreter) void QtcProcess::setWriteData(const QByteArray &writeData) { - d->m_writeData = writeData; + d->m_setup->m_writeData = writeData; } #ifdef QT_GUI_LIB @@ -1516,23 +1592,27 @@ static bool isGuiThread() } #endif -void QtcProcess::runBlocking(QtcProcess::EventLoopMode eventLoopMode) +void QtcProcess::runBlocking(EventLoopMode eventLoopMode) { // FIXME: Implement properly - if (d->m_commandLine.executable().needsDevice()) { + if (d->m_setup->m_commandLine.executable().needsDevice()) { QtcProcess::start(); waitForFinished(); return; }; - qCDebug(processLog).noquote() << "Starting blocking:" << d->m_commandLine.toUserOutput() - << " process user events: " << (eventLoopMode == QtcProcess::WithEventLoop); - ExecuteOnDestruction logResult([this] { qCDebug(processLog) << *this; }); - - if (eventLoopMode == QtcProcess::WithEventLoop) { - QtcProcess::start(); + if (processLog().isDebugEnabled()) { + // Attach a dynamic property with info about blocking type + setProperty(QTC_PROCESS_BLOCKING_TYPE, int(eventLoopMode)); + } + QtcProcess::start(); + if (processLog().isDebugEnabled()) { + // Remove the dynamic property so that it's not reused in subseqent start() + setProperty(QTC_PROCESS_BLOCKING_TYPE, QVariant()); + } + if (eventLoopMode == EventLoopMode::On) { // On Windows, start failure is triggered immediately if the // executable cannot be found in the path. Do not start the // event loop in that case. @@ -1552,6 +1632,8 @@ void QtcProcess::runBlocking(QtcProcess::EventLoopMode eventLoopMode) d->m_eventLoop = nullptr; d->m_stdOut.append(d->m_process->readAllStandardOutput()); d->m_stdErr.append(d->m_process->readAllStandardError()); + d->m_stdOut.handleRest(); + d->m_stdErr.handleRest(); timer.stop(); #ifdef QT_GUI_LIB @@ -1560,25 +1642,22 @@ void QtcProcess::runBlocking(QtcProcess::EventLoopMode eventLoopMode) #endif } } else { - QtcProcess::start(); if (!waitForStarted(d->m_maxHangTimerCount * 1000)) { - d->m_result = QtcProcess::StartFailed; + d->m_result = ProcessResult::StartFailed; return; } if (!waitForFinished(d->m_maxHangTimerCount * 1000)) { - d->m_result = QtcProcess::Hang; + d->m_result = ProcessResult::Hang; terminate(); if (!waitForFinished(1000)) { kill(); waitForFinished(1000); } + d->m_stdOut.append(d->m_process->readAllStandardOutput()); + d->m_stdErr.append(d->m_process->readAllStandardError()); + d->m_stdOut.handleRest(); + d->m_stdErr.handleRest(); } - - if (state() != QProcess::NotRunning) - return; - - d->m_stdOut.append(d->m_process->readAllStandardOutput()); - d->m_stdErr.append(d->m_process->readAllStandardError()); } } @@ -1615,11 +1694,11 @@ void QtcProcessPrivate::slotTimeout() qDebug() << Q_FUNC_INFO << "HANG detected, killing"; m_waitingForUser = true; const bool terminate = !m_timeOutMessageBoxEnabled - || askToKill(m_commandLine.executable().toString()); + || askToKill(m_setup->m_commandLine.executable().toString()); m_waitingForUser = false; if (terminate) { q->stopProcess(); - m_result = QtcProcess::Hang; + m_result = ProcessResult::Hang; } else { m_hangTimerCount = 0; } @@ -1629,7 +1708,13 @@ void QtcProcessPrivate::slotTimeout() } } -void QtcProcessPrivate::slotFinished(int exitCode, QProcess::ExitStatus status) +void QtcProcessPrivate::slotFinished() +{ + handleFinished(m_process->exitCode(), m_process->exitStatus()); + q->emitFinished(); +} + +void QtcProcessPrivate::handleFinished(int exitCode, QProcess::ExitStatus status) { if (debug) qDebug() << Q_FUNC_INFO << exitCode << status; @@ -1641,8 +1726,8 @@ void QtcProcessPrivate::slotFinished(int exitCode, QProcess::ExitStatus status) break; case QProcess::CrashExit: // Was hang detected before and killed? - if (m_result != QtcProcess::Hang) - m_result = QtcProcess::TerminatedAbnormally; + if (m_result != ProcessResult::Hang) + m_result = ProcessResult::TerminatedAbnormally; break; } if (m_eventLoop) @@ -1650,23 +1735,21 @@ void QtcProcessPrivate::slotFinished(int exitCode, QProcess::ExitStatus status) m_stdOut.handleRest(); m_stdErr.handleRest(); - - emit q->finished(); } -void QtcProcessPrivate::handleError(QProcess::ProcessError error, StartFailure startFailure) +void QtcProcessPrivate::handleError(QProcess::ProcessError error) { m_hangTimerCount = 0; if (debug) qDebug() << Q_FUNC_INFO << error; // Was hang detected before and killed? - if (m_result != QtcProcess::Hang) - m_result = QtcProcess::StartFailed; - m_startFailure = startFailure; + if (m_result != ProcessResult::Hang) + m_result = ProcessResult::StartFailed; + m_startFailure = (error == QProcess::FailedToStart) ? WrongCommandFailure : OtherFailure; if (m_eventLoop) m_eventLoop->quit(); - emit q->errorOccurred(error); + q->emitErrorOccurred(error); } } // namespace Utils diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index fa844d18955..e2c773448ca 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -29,12 +29,12 @@ #include "environment.h" #include "commandline.h" -#include "processutils.h" +#include "processenums.h" +#include "processinterface.h" +#include "qtcassert.h" #include <QProcess> -#include <functional> - QT_FORWARD_DECLARE_CLASS(QDebug) QT_FORWARD_DECLARE_CLASS(QTextCodec) @@ -42,133 +42,136 @@ class tst_QtcProcess; namespace Utils { -class CommandLine; -class Environment; -class QtcProcess; - namespace Internal { class QtcProcessPrivate; } -class DeviceProcessHooks -{ -public: - std::function<void(QtcProcess &)> startProcessHook; - std::function<Environment(const FilePath &)> systemEnvironmentForBinary; -}; +class DeviceProcessHooks; -class QTCREATOR_UTILS_EXPORT QtcProcess : public QObject +class QTCREATOR_UTILS_EXPORT QtcProcess : public ProcessInterface { Q_OBJECT public: - enum ProcessImpl { - QProcessImpl, - ProcessLauncherImpl, - DefaultImpl, - }; - - enum TerminalMode { - TerminalOff, - TerminalRun, - TerminalDebug, - TerminalSuspend, - TerminalOn = TerminalRun // default mode for ON - }; - - struct Setup { - Setup() {} - Setup(ProcessImpl processImpl) : processImpl(processImpl) {} - Setup(ProcessMode processMode) : processMode(processMode) {} - Setup(TerminalMode terminalMode) : terminalMode(terminalMode) {} - - ProcessImpl processImpl = DefaultImpl; - ProcessMode processMode = ProcessMode::Reader; - TerminalMode terminalMode = TerminalOff; - }; - - QtcProcess(const Setup &setup = {}, QObject *parent = nullptr); - QtcProcess(QObject *parent); + QtcProcess(QObject *parent = nullptr); ~QtcProcess(); - ProcessMode processMode() const; + // ProcessInterface related + + void start() override; + void interrupt() override; + void terminate() override; + void kill() override; + void close() final; + + QByteArray readAllStandardOutput() override; + QByteArray readAllStandardError() override; + qint64 write(const QByteArray &input) override; + + qint64 processId() const override; + QProcess::ProcessState state() const override; + int exitCode() const override; + QProcess::ExitStatus exitStatus() const override; + + QProcess::ProcessError error() const final; + QString errorString() const override; + void setErrorString(const QString &str) final; + + bool waitForStarted(int msecs = 30000) final; + bool waitForReadyRead(int msecs = 30000) final; + bool waitForFinished(int msecs = 30000) final; + + void kickoffProcess() final; + qint64 applicationMainThreadID() const final; + + // ProcessSetupData related + + void setProcessImpl(ProcessImpl processImpl); + + void setTerminalMode(TerminalMode mode); TerminalMode terminalMode() const; + bool usesTerminal() const { return terminalMode() != TerminalMode::Off; } - enum Result { - // Finished successfully. Unless an ExitCodeInterpreter is set - // this corresponds to a return code 0. - FinishedWithSuccess, - Finished = FinishedWithSuccess, // FIXME: Kept to ease downstream transition - // Finished unsuccessfully. Unless an ExitCodeInterpreter is set - // this corresponds to a return code different from 0. - FinishedWithError, - FinishedError = FinishedWithError, // FIXME: Kept to ease downstream transition - // Process terminated abnormally (kill) - TerminatedAbnormally, - // Executable could not be started - StartFailed, - // Hang, no output after time out - Hang - }; + void setProcessMode(ProcessMode processMode); + ProcessMode processMode() const; void setEnvironment(const Environment &env); void unsetEnvironment(); const Environment &environment() const; bool hasEnvironment() const; + void setRemoteEnvironment(const Environment &env); + Environment remoteEnvironment() const; + void setCommand(const CommandLine &cmdLine); const CommandLine &commandLine() const; - FilePath workingDirectory() const; void setWorkingDirectory(const FilePath &dir); + FilePath workingDirectory() const; - void setUseCtrlCStub(bool enabled); + void setWriteData(const QByteArray &writeData); + + void setUseCtrlCStub(bool enabled); // debug only void setLowPriority(); void setDisableUnixTerminal(); void setRunAsRoot(bool on); bool isRunAsRoot() const; - void setAbortOnMetaChars(bool abort); - void start(); - virtual void terminate(); - virtual void interrupt(); + void setProcessChannelMode(QProcess::ProcessChannelMode mode); + void setStandardInputFile(const QString &inputFile); + + void setExtraData(const QString &key, const QVariant &value); + QVariant extraData(const QString &key) const; + + void setExtraData(const QVariantHash &extraData); + QVariantHash extraData() const; + + static void setRemoteProcessHooks(const DeviceProcessHooks &hooks); + + // TODO: Some usages of this method assume that Starting phase is also a running state + // i.e. if isRunning() returns false, they assume NotRunning state, what may be an error. + bool isRunning() const; // Short for state() == QProcess::Running. + + // Other enhancements. + // These (or some of them) may be potentially moved outside of the class. + // For some we may aggregate in another public utils class (or subclass of QtcProcess)? + + // TODO: How below 3 methods relate to QtcProcess? Action: move them somewhere else. + // Helpers to find binaries. Do not use it for other path variables + // and file types. + static QString locateBinary(const QString &binary); + static QString locateBinary(const QString &path, const QString &binary); + static QString normalizeNewlines(const QString &text); + + // TODO: Unused currently? Should it serve as a compartment for contrary of remoteEnvironment? + static Environment systemEnvironmentForBinary(const FilePath &filePath); static bool startDetached(const CommandLine &cmd, const FilePath &workingDirectory = {}, qint64 *pid = nullptr); - enum EventLoopMode { - NoEventLoop, - WithEventLoop // Avoid - }; - // Starts the command and waits for finish. // User input processing is enabled when WithEventLoop was passed. - void runBlocking(EventLoopMode eventLoopMode = NoEventLoop); + void runBlocking(EventLoopMode eventLoopMode = EventLoopMode::Off); /* Timeout for hanging processes (triggers after no more output * occurs on stderr/stdout). */ void setTimeoutS(int timeoutS); + // TODO: We should specify the purpose of the codec, e.g. setCodecForStandardChannel() void setCodec(QTextCodec *c); void setTimeOutMessageBoxEnabled(bool); - void setExitCodeInterpreter(const std::function<QtcProcess::Result(int)> &interpreter); - - void setWriteData(const QByteArray &writeData); + void setExitCodeInterpreter(const ExitCodeInterpreter &interpreter); void setStdOutCallback(const std::function<void(const QString &)> &callback); void setStdOutLineCallback(const std::function<void(const QString &)> &callback); void setStdErrCallback(const std::function<void(const QString &)> &callback); void setStdErrLineCallback(const std::function<void(const QString &)> &callback); - static void setRemoteProcessHooks(const DeviceProcessHooks &hooks); - bool stopProcess(); bool readDataFromProcess(int timeoutS, QByteArray *stdOut, QByteArray *stdErr, bool showTimeOutMessageBox); - static QString normalizeNewlines(const QString &text); - - Result result() const; - void setResult(Result result); + ProcessResult result() const; + void setResult(const ProcessResult &result); QByteArray allRawOutput() const; QString allOutput() const; @@ -178,62 +181,22 @@ public: QByteArray rawStdOut() const; - virtual int exitCode() const; - QString exitMessage(); - // Helpers to find binaries. Do not use it for other path variables - // and file types. - static QString locateBinary(const QString &binary); - static QString locateBinary(const QString &path, const QString &binary); - - static Environment systemEnvironmentForBinary(const FilePath &filePath); - - void kickoffProcess(); - void interruptProcess(); - qint64 applicationMainThreadID() const; - - // FIXME: Cut down the following bits inherited from QProcess and QIODevice. - - void setProcessChannelMode(QProcess::ProcessChannelMode mode); - - QProcess::ProcessError error() const; - virtual QProcess::ProcessState state() const; - bool isRunning() const; // Short for state() == QProcess::Running. - - virtual QString errorString() const; - void setErrorString(const QString &str); - - qint64 processId() const; - - bool waitForStarted(int msecs = 30000); - bool waitForReadyRead(int msecs = 30000); - bool waitForFinished(int msecs = 30000); - - virtual QByteArray readAllStandardOutput(); - virtual QByteArray readAllStandardError(); - - virtual QProcess::ExitStatus exitStatus() const; - - virtual void kill(); - - virtual qint64 write(const QByteArray &input); - void close(); - - void setStandardInputFile(const QString &inputFile); - QString toStandaloneCommandLine() const; -signals: - void started(); - void finished(); - void errorOccurred(QProcess::ProcessError error); - void readyReadStandardOutput(); - void readyReadStandardError(); +protected: + // TODO: remove these methods on QtcProcess de-virtualization + virtual void emitStarted(); + virtual void emitFinished(); + virtual void emitErrorOccurred(QProcess::ProcessError error); private: + void setProcessInterface(ProcessInterface *interface); + friend QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug str, const QtcProcess &r); + friend class Internal::QtcProcessPrivate; Internal::QtcProcessPrivate *d = nullptr; friend tst_QtcProcess; @@ -242,6 +205,13 @@ private: void endFeed(); }; -using ExitCodeInterpreter = std::function<QtcProcess::Result(int /*exitCode*/)>; +class DeviceProcessHooks +{ +public: + std::function<ProcessInterface *(const FilePath &)> processImplHook; + // TODO: remove this hook + std::function<void(QtcProcess &)> startProcessHook; + std::function<Environment(const FilePath &)> systemEnvironmentForBinary; +}; } // namespace Utils diff --git a/src/libs/utils/shellcommand.cpp b/src/libs/utils/shellcommand.cpp index e5527c127b4..abef79ea5f4 100644 --- a/src/libs/utils/shellcommand.cpp +++ b/src/libs/utils/shellcommand.cpp @@ -28,13 +28,13 @@ #include "environment.h" #include "fileutils.h" #include "qtcassert.h" +#include "qtcprocess.h" #include "runextensions.h" #include <QFileInfo> #include <QFuture> #include <QFutureWatcher> #include <QMutex> -#include <QProcess> #include <QProcessEnvironment> #include <QScopedPointer> #include <QSharedPointer> @@ -280,7 +280,7 @@ void ShellCommand::run(QFutureInterface<void> &future) stdOut += proc.stdOut(); stdErr += proc.stdErr(); d->m_lastExecExitCode = proc.exitCode(); - d->m_lastExecSuccess = proc.result() == QtcProcess::FinishedWithSuccess; + d->m_lastExecSuccess = proc.result() == ProcessResult::FinishedWithSuccess; if (!d->m_lastExecSuccess) break; } @@ -314,7 +314,7 @@ void ShellCommand::runCommand(QtcProcess &proc, const FilePath dir = workDirectory(workingDirectory); if (command.executable().isEmpty()) { - proc.setResult(QtcProcess::StartFailed); + proc.setResult(ProcessResult::StartFailed); return; } @@ -332,7 +332,7 @@ void ShellCommand::runCommand(QtcProcess &proc, if (!d->m_aborted) { // Success/Fail message in appropriate window? - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { if (d->m_flags & ShowSuccessMessage) emit appendMessage(proc.exitMessage()); } else if (!(d->m_flags & SuppressFailMessage)) { @@ -416,7 +416,7 @@ void ShellCommand::runSynchronous(QtcProcess &process, const FilePath &workingDi if (d->m_codec) process.setCodec(d->m_codec); - process.runBlocking(QtcProcess::WithEventLoop); + process.runBlocking(EventLoopMode::On); } const QVariant &ShellCommand::cookie() const diff --git a/src/libs/utils/shellcommand.h b/src/libs/utils/shellcommand.h index dcdd0795f1a..3f694528bce 100644 --- a/src/libs/utils/shellcommand.h +++ b/src/libs/utils/shellcommand.h @@ -27,7 +27,10 @@ #include "utils_global.h" -#include "qtcprocess.h" +#include "filepath.h" +#include "processenums.h" + +#include <QObject> QT_BEGIN_NAMESPACE class QMutex; @@ -36,10 +39,15 @@ template <typename T> class QFutureInterface; template <typename T> class QFuture; +class QTextCodec; QT_END_NAMESPACE namespace Utils { +class CommandLine; +class Environment; +class QtcProcess; + namespace Internal { class ShellCommandPrivate; } class QTCREATOR_UTILS_EXPORT ProgressParser diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index ba664b1b700..7fa3937da3c 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -400,9 +400,6 @@ QString formatElapsedTime(qint64 elapsed) */ QString wildcardToRegularExpression(const QString &original) { -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - using qsizetype = int; -#endif const qsizetype wclen = original.size(); QString rx; rx.reserve(wclen + wclen / 16); @@ -459,11 +456,7 @@ QString wildcardToRegularExpression(const QString &original) } } -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) return QRegularExpression::anchoredPattern(rx); -#else - return "\\A" + rx + "\\z"; -#endif } QTCREATOR_UTILS_EXPORT QString languageNameFromLanguageCode(const QString &languageCode) diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index 9a8cf8f964c..70cf6738950 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -27,8 +27,6 @@ #include "utils_global.h" -#include "porting.h" - #include <QList> #include <QString> diff --git a/src/libs/utils/terminalprocess.cpp b/src/libs/utils/terminalprocess.cpp index f24b6aaf952..88d4b902513 100644 --- a/src/libs/utils/terminalprocess.cpp +++ b/src/libs/utils/terminalprocess.cpp @@ -63,16 +63,16 @@ namespace Utils { namespace Internal { -static QString modeOption(QtcProcess::TerminalMode m) +static QString modeOption(TerminalMode m) { switch (m) { - case QtcProcess::TerminalRun: + case TerminalMode::Run: return QLatin1String("run"); - case QtcProcess::TerminalDebug: + case TerminalMode::Debug: return QLatin1String("debug"); - case QtcProcess::TerminalSuspend: + case TerminalMode::Suspend: return QLatin1String("suspend"); - case QtcProcess::TerminalOff: + case TerminalMode::Off: QTC_CHECK(false); break; } @@ -123,13 +123,10 @@ static QString msgCannotExecute(const QString & p, const QString &why) class TerminalProcessPrivate { public: - TerminalProcessPrivate(QObject *parent, QtcProcess::ProcessImpl processImpl, - QtcProcess::TerminalMode terminalMode) - : m_terminalMode(terminalMode) - , m_process(processImpl, parent) - {} + TerminalProcessPrivate(QObject *parent) + : m_process(parent) {} - const QtcProcess::TerminalMode m_terminalMode; + TerminalMode m_terminalMode = TerminalMode::On; FilePath m_workingDir; Environment m_environment; qint64 m_processId = 0; @@ -159,9 +156,8 @@ public: #endif }; -TerminalProcess::TerminalProcess(QObject *parent, QtcProcess::ProcessImpl processImpl, - QtcProcess::TerminalMode terminalMode) : - QObject(parent), d(new TerminalProcessPrivate(this, processImpl, terminalMode)) +TerminalProcess::TerminalProcess(QObject *parent) + : QObject(parent), d(new TerminalProcessPrivate(this)) { connect(&d->m_stubServer, &QLocalServer::newConnection, this, &TerminalProcess::stubConnectionAvailable); @@ -175,6 +171,17 @@ TerminalProcess::~TerminalProcess() delete d; } +void TerminalProcess::setProcessImpl(ProcessImpl processImpl) +{ + d->m_process.setProcessImpl(processImpl); +} + +void TerminalProcess::setTerminalMode(TerminalMode mode) +{ + QTC_ASSERT(mode != TerminalMode::Off, return); + d->m_terminalMode = mode; +} + void TerminalProcess::setCommand(const CommandLine &command) { d->m_commandLine = command; @@ -209,7 +216,7 @@ void TerminalProcess::start() QString pcmd; QString pargs; - if (d->m_terminalMode != QtcProcess::TerminalRun) { // The debugger engines already pre-process the arguments. + if (d->m_terminalMode != TerminalMode::Run) { // The debugger engines already pre-process the arguments. pcmd = d->m_commandLine.executable().toString(); pargs = d->m_commandLine.arguments(); } else { @@ -374,7 +381,7 @@ void TerminalProcess::start() emitError(QProcess::FailedToStart, tr("Quoting error in command.")); return; } - if (d->m_terminalMode == QtcProcess::TerminalDebug) { + if (d->m_terminalMode == TerminalMode::Debug) { // FIXME: QTCREATORBUG-2809 emitError(QProcess::FailedToStart, tr("Debugging complex shell commands in a terminal" " is currently not supported.")); @@ -474,7 +481,7 @@ void TerminalProcess::finish(int exitCode, QProcess::ExitStatus exitStatus) d->m_processId = 0; d->m_exitCode = exitCode; d->m_appStatus = exitStatus; - emit finished(exitCode, exitStatus); + emit finished(); } void TerminalProcess::kickoffProcess() @@ -489,7 +496,7 @@ void TerminalProcess::kickoffProcess() #endif } -void TerminalProcess::interruptProcess() +void TerminalProcess::interrupt() { #ifdef Q_OS_WIN // Not used. diff --git a/src/libs/utils/terminalprocess_p.h b/src/libs/utils/terminalprocess_p.h index 0fb2006049e..bce07403a68 100644 --- a/src/libs/utils/terminalprocess_p.h +++ b/src/libs/utils/terminalprocess_p.h @@ -25,7 +25,9 @@ #pragma once -#include "qtcprocess.h" +#include "processenums.h" + +#include <QProcess> namespace Utils { @@ -39,10 +41,12 @@ class TerminalProcess : public QObject { Q_OBJECT public: - explicit TerminalProcess(QObject *parent, QtcProcess::ProcessImpl processImpl, - QtcProcess::TerminalMode terminalMode); + explicit TerminalProcess(QObject *parent); ~TerminalProcess() override; + void setProcessImpl(ProcessImpl processImpl); + void setTerminalMode(TerminalMode mode); + void setCommand(const CommandLine &command); const CommandLine &commandLine() const; @@ -69,12 +73,12 @@ public: void setAbortOnMetaChars(bool abort); // used only in sshDeviceProcess void kickoffProcess(); // only debugger terminal, only non-windows - void interruptProcess(); // only debugger terminal, only non-windows + void interrupt(); // only debugger terminal, only non-windows qint64 applicationMainThreadID() const; // only debugger terminal, only windows (-1 otherwise) signals: void started(); - void finished(int exitCode, QProcess::ExitStatus status); + void finished(); void errorOccurred(QProcess::ProcessError error); private: diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 318e7bcbef1..196cbf90db4 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -5,7 +5,7 @@ Project { name: "Utils" QtcLibrary { - + cpp.includePaths: base.concat("mimetypes", ".") cpp.defines: base.concat([ "UTILS_LIBRARY" ]) @@ -179,6 +179,7 @@ Project { "macroexpander.cpp", "macroexpander.h", "mapreduce.h", + "mimeutils.h", "multitextcursor.cpp", "multitextcursor.h", "namevaluedictionary.cpp", @@ -220,8 +221,12 @@ Project { "porting.h", "portlist.cpp", "portlist.h", + "processenums.h", "processhandle.cpp", "processhandle.h", + "processinfo.cpp", + "processinfo.h", + "processinterface.h", "processreaper.cpp", "processreaper.h", "processutils.cpp", @@ -402,6 +407,7 @@ Project { "mimetype_p.h", "mimetypeparser.cpp", "mimetypeparser_p.h", + "mimeutils.cpp" ] } @@ -428,6 +434,7 @@ Project { Export { Depends { name: "Qt"; submodules: ["concurrent", "widgets" ] } + cpp.includePaths: base.concat("mimetypes") } } } diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 800cd3b775d..8534fcab7f9 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -78,7 +78,6 @@ add_subdirectory(qmlpreview) add_subdirectory(qmlprofiler) add_subdirectory(remotelinux) add_subdirectory(valgrind) -add_subdirectory(winrt) add_subdirectory(perfprofiler) add_subdirectory(qbsprojectmanager) add_subdirectory(ctfvisualizer) diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 3944e30ed6b..0393b1d0a3a 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -66,13 +66,13 @@ const int avdCreateTimeoutMs = 30000; bool AndroidAvdManager::avdManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output) { CommandLine cmd(config.avdManagerToolPath(), args); - Utils::QtcProcess proc; + QtcProcess proc; Environment env = AndroidConfigurations::toolsEnvironment(config); proc.setEnvironment(env); qCDebug(avdManagerLog) << "Running AVD Manager command:" << cmd.toUserOutput(); proc.setCommand(cmd); proc.runBlocking(); - if (proc.result() == Utils::QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { if (output) *output = proc.allOutput(); return true; @@ -118,7 +118,8 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA const FilePath avdManagerTool = config.avdManagerToolPath(); qCDebug(avdManagerLog) << "Running AVD Manager command:" << CommandLine(avdManagerTool, arguments).toUserOutput(); - QtcProcess proc(ProcessMode::Writer); + QtcProcess proc; + proc.setProcessMode(ProcessMode::Writer); proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config)); proc.setCommand({avdManagerTool, arguments}); proc.start(); @@ -195,7 +196,7 @@ AndroidAvdManager::~AndroidAvdManager() = default; QFuture<CreateAvdInfo> AndroidAvdManager::createAvd(CreateAvdInfo info) const { - return Utils::runAsync(&createAvdCommand, m_config, info); + return runAsync(&createAvdCommand, m_config, info); } bool AndroidAvdManager::removeAvd(const QString &name) const @@ -207,12 +208,12 @@ bool AndroidAvdManager::removeAvd(const QString &name) const proc.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config)); proc.setCommand(command); proc.runBlocking(); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } static void avdConfigEditManufacturerTag(const QString &avdPathStr, bool recoverMode = false) { - const Utils::FilePath avdPath = Utils::FilePath::fromString(avdPathStr); + const FilePath avdPath = FilePath::fromString(avdPathStr); if (avdPath.exists()) { const QString configFilePath = avdPath.pathAppended("config.ini").toString(); QFile configFile(configFilePath); @@ -272,7 +273,7 @@ static AndroidDeviceInfoList listVirtualDevices(const AndroidConfig &config) QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const { - return Utils::runAsync(listVirtualDevices, m_config); + return runAsync(listVirtualDevices, m_config); } QString AndroidAvdManager::startAvd(const QString &name) const @@ -321,7 +322,7 @@ QString AndroidAvdManager::findAvd(const QString &avdName) const foreach (AndroidDeviceInfo device, devices) { if (device.type != ProjectExplorer::IDevice::Emulator) continue; - if (device.avdname == avdName) + if (device.avdName == avdName) return device.serialNumber; } return QString(); @@ -355,7 +356,7 @@ bool AndroidAvdManager::isAvdBooted(const QString &device) const adbProc.setTimeoutS(10); adbProc.setCommand(command); adbProc.runBlocking(); - if (adbProc.result() != QtcProcess::FinishedWithSuccess) + if (adbProc.result() != ProcessResult::FinishedWithSuccess) return false; QString value = adbProc.allOutput().trimmed(); return value == "stopped"; diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index c97a337c231..b53cf3b6f7a 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -535,7 +535,7 @@ bool AndroidBuildApkStep::init() const QVersionNumber sdkToolsVersion = AndroidConfigurations::currentConfig().sdkToolsVersion(); if (sdkToolsVersion >= QVersionNumber(25, 3, 0) - || AndroidConfigurations::currentConfig().isCmdlineSdkToolsInstalled()) { + && AndroidConfigurations::currentConfig().preCmdlineSdkToolsInstalled()) { if (!version->sourcePath().pathAppended("src/3rdparty/gradle").exists()) { const QString error = tr("The installed SDK tools version (%1) does not include Gradle " @@ -1061,8 +1061,8 @@ QAbstractItemModel *AndroidBuildApkStep::keystoreCertificates() QtcProcess keytoolProc; keytoolProc.setTimeoutS(30); keytoolProc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), params}); - keytoolProc.runBlocking(QtcProcess::WithEventLoop); - if (keytoolProc.result() > QtcProcess::FinishedWithError) + keytoolProc.runBlocking(EventLoopMode::On); + if (keytoolProc.result() > ProcessResult::FinishedWithError) QMessageBox::critical(nullptr, tr("Error"), tr("Failed to run keytool.")); else model = new CertificatesModel(keytoolProc.stdOut(), this); diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 7400020bbb9..c3702fa2763 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -165,7 +165,7 @@ namespace { proc.setTimeoutS(30); proc.setCommand({executable, {shell}}); proc.runBlocking(); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return true; return !proc.allOutput().contains("x86-64"); } @@ -474,9 +474,12 @@ QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform) QString("android-%1").arg(platform->apiLevel()) : ""; } -bool AndroidConfig::isCmdlineSdkToolsInstalled() const +// This is checking for the SDK tools [*] that were deprecated in favor of +// the command-line tools. +// See https://developer.android.com/studio/releases/sdk-tools +bool AndroidConfig::preCmdlineSdkToolsInstalled() const { - QString toolPath("cmdline-tools/latest/bin/sdkmanager"); + QString toolPath("tools/bin/sdkmanager"); if (HostOsInfo::isWindowsHost()) toolPath += ANDROID_BAT_SUFFIX; @@ -491,38 +494,51 @@ FilePath AndroidConfig::adbToolPath() const FilePath AndroidConfig::emulatorToolPath() const { QString relativePath = "emulator/emulator"; - if (sdkToolsVersion() < QVersionNumber(25, 3, 0) && !isCmdlineSdkToolsInstalled()) + if (sdkToolsVersion() < QVersionNumber(25, 3, 0) && preCmdlineSdkToolsInstalled()) relativePath = "tools/emulator"; return m_sdkLocation / (relativePath + QTC_HOST_EXE_SUFFIX); } FilePath AndroidConfig::sdkManagerToolPath() const { - QStringList sdkmanagerPaths = {"cmdline-tools/latest/bin/sdkmanager", - "tools/bin/sdkmanager"}; + const QStringList sdkmanagerPaths = { + QString(Constants::cmdlineToolsName).append("/latest/bin/sdkmanager"), + "tools/bin/sdkmanager"}; - for (QString &toolPath : sdkmanagerPaths) { + for (const QString &toolPath : sdkmanagerPaths) { + QString toolPathWithSuffix = toolPath; if (HostOsInfo::isWindowsHost()) - toolPath += ANDROID_BAT_SUFFIX; - - const FilePath sdkmanagerPath = m_sdkLocation / toolPath; + toolPathWithSuffix += ANDROID_BAT_SUFFIX; + const FilePath sdkmanagerPath = m_sdkLocation / toolPathWithSuffix; if (sdkmanagerPath.exists()) return sdkmanagerPath; } + // If it's a first time install use the path of Constants::cmdlineToolsName temporary download + const FilePath tmpSdkPath = m_temporarySdkToolsPath; + if (!tmpSdkPath.isEmpty()) { + QString suffix = "bin/sdkmanager"; + if (HostOsInfo::isWindowsHost()) + suffix += ANDROID_BAT_SUFFIX; + const FilePath tmpsdkManagerPath = tmpSdkPath.pathAppended(suffix); + if (tmpsdkManagerPath.exists()) + return tmpsdkManagerPath; + } + return FilePath(); } FilePath AndroidConfig::avdManagerToolPath() const { - QStringList sdkmanagerPaths = {"cmdline-tools/latest/bin/avdmanager", - "tools/bin/avdmanager"}; + const QStringList sdkmanagerPaths = { + QString(Constants::cmdlineToolsName).append("/latest/bin/avdmanager"), + "tools/bin/avdmanager"}; - for (QString &toolPath : sdkmanagerPaths) { + for (const QString &toolPath : sdkmanagerPaths) { + QString toolPathWithSuffix = toolPath; if (HostOsInfo::isWindowsHost()) - toolPath += ANDROID_BAT_SUFFIX; - - const FilePath sdkmanagerPath = m_sdkLocation / toolPath; + toolPathWithSuffix += ANDROID_BAT_SUFFIX; + const FilePath sdkmanagerPath = m_sdkLocation / toolPathWithSuffix; if (sdkmanagerPath.exists()) return sdkmanagerPath; } @@ -530,6 +546,34 @@ FilePath AndroidConfig::avdManagerToolPath() const return FilePath(); } +void AndroidConfig::setTemporarySdkToolsPath(const Utils::FilePath &path) +{ + m_temporarySdkToolsPath = path; +} + +FilePath AndroidConfig::sdkToolsVersionPath() const +{ + const QStringList sdkVersionPaths = { + QString(Constants::cmdlineToolsName).append("/latest/source.properties"), + "tools/source.properties"}; + + for (const QString &versionPath : sdkVersionPaths) { + const FilePath sdkVersionPath = m_sdkLocation / versionPath; + if (sdkVersionPath.exists()) + return sdkVersionPath; + } + + // If it's a first time install use the path of Constants::cmdlineToolsName temporary download + const FilePath tmpSdkPath = m_temporarySdkToolsPath; + if (!tmpSdkPath.isEmpty()) { + const FilePath sdkVersionPath = tmpSdkPath.pathAppended("source.properties"); + if (sdkVersionPath.exists()) + return sdkVersionPath; + } + + return FilePath(); +} + FilePath AndroidConfig::toolchainPathFromNdk(const FilePath &ndkLocation, OsType hostOs) { const FilePath tcPath = ndkLocation / "toolchains/"; @@ -635,7 +679,7 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const CommandLine cmd{adbToolPath(), {"devices"}}; adbProc.setCommand(cmd); adbProc.runBlocking(); - if (adbProc.result() != QtcProcess::FinishedWithSuccess) { + if (adbProc.result() != ProcessResult::FinishedWithSuccess) { if (error) *error = QApplication::translate("AndroidConfiguration", "Could not run: %1") .arg(cmd.toUserOutput()); @@ -669,9 +713,9 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(QString *error) const dev.state = IDevice::DeviceReadyToUse; if (dev.type == IDevice::Emulator) { - dev.avdname = getAvdName(dev.serialNumber); - if (dev.avdname.isEmpty()) - dev.avdname = serialNo; + dev.avdName = getAvdName(dev.serialNumber); + if (dev.avdName.isEmpty()) + dev.avdName = serialNo; } devices.push_back(dev); @@ -706,7 +750,7 @@ QString AndroidConfig::getDeviceProperty(const QString &device, const QString &p adbProc.setTimeoutS(10); adbProc.setCommand(cmd); adbProc.runBlocking(); - if (adbProc.result() != QtcProcess::FinishedWithSuccess) + if (adbProc.result() != ProcessResult::FinishedWithSuccess) return QString(); return adbProc.allOutput(); @@ -753,28 +797,6 @@ QString AndroidConfig::getAvdName(const QString &serialnumber) return QString::fromLatin1(name).trimmed(); } -AndroidConfig::OpenGl AndroidConfig::getOpenGLEnabled(const QString &emulator) const -{ - QDir dir = QDir::home(); - if (!dir.cd(QLatin1String(".android"))) - return OpenGl::Unknown; - if (!dir.cd(QLatin1String("avd"))) - return OpenGl::Unknown; - if (!dir.cd(emulator + QLatin1String(".avd"))) - return OpenGl::Unknown; - QFile file(dir.filePath(QLatin1String("config.ini"))); - if (!file.exists()) - return OpenGl::Unknown; - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) - return OpenGl::Unknown; - while (!file.atEnd()) { - QByteArray line = file.readLine(); - if (line.contains("hw.gpu.enabled") && line.contains("yes")) - return OpenGl::Enabled; - } - return OpenGl::Disabled; -} - //! //! \brief AndroidConfigurations::getProductModel //! \param device serial number @@ -805,7 +827,7 @@ QStringList AndroidConfig::getAbis(const QString &device) adbProc.setTimeoutS(10); adbProc.setCommand({adbTool, arguments}); adbProc.runBlocking(); - if (adbProc.result() != QtcProcess::FinishedWithSuccess) + if (adbProc.result() != ProcessResult::FinishedWithSuccess) return result; QString output = adbProc.allOutput().trimmed(); @@ -828,7 +850,7 @@ QStringList AndroidConfig::getAbis(const QString &device) abiProc.setTimeoutS(10); abiProc.setCommand({adbTool, arguments}); abiProc.runBlocking(); - if (abiProc.result() != QtcProcess::FinishedWithSuccess) + if (abiProc.result() != ProcessResult::FinishedWithSuccess) return result; QString abi = abiProc.allOutput().trimmed(); @@ -883,18 +905,12 @@ void AndroidConfig::setSdkLocation(const FilePath &sdkLocation) QVersionNumber AndroidConfig::sdkToolsVersion() const { - QVersionNumber version; - if (m_sdkLocation.exists()) { - FilePath sdkToolsPropertiesPath; - if (isCmdlineSdkToolsInstalled()) - sdkToolsPropertiesPath = m_sdkLocation / "cmdline-tools/latest/source.properties"; - else - sdkToolsPropertiesPath = m_sdkLocation / "tools/source.properties"; - QSettings settings(sdkToolsPropertiesPath.toString(), QSettings::IniFormat); - auto versionStr = settings.value(sdkToolsVersionKey).toString(); - version = QVersionNumber::fromString(versionStr); - } - return version; + if (!m_sdkLocation.exists()) + return {}; + + const FilePath sdkToolsPropertiesPath = sdkToolsVersionPath(); + const QSettings settings(sdkToolsPropertiesPath.toString(), QSettings::IniFormat); + return QVersionNumber::fromString(settings.value(sdkToolsVersionKey).toString()); } QVersionNumber AndroidConfig::buildToolsVersion() const diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 5338845d0d2..c9726370eef 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -96,6 +96,7 @@ public: Utils::FilePath sdkLocation() const; void setSdkLocation(const Utils::FilePath &sdkLocation); QVersionNumber sdkToolsVersion() const; + Utils::FilePath sdkToolsVersionPath() const; QVersionNumber buildToolsVersion() const; QStringList sdkManagerToolArgs() const; void setSdkManagerToolArgs(const QStringList &args); @@ -134,6 +135,8 @@ public: Utils::FilePath sdkManagerToolPath() const; Utils::FilePath avdManagerToolPath() const; + void setTemporarySdkToolsPath(const Utils::FilePath &path); + Utils::FilePath toolchainPath(const QtSupport::QtVersion *qtVersion) const; static Utils::FilePath toolchainPathFromNdk(const Utils::FilePath &ndkLocation, Utils::OsType hostOs = Utils::HostOsInfo::hostOs()); @@ -156,11 +159,9 @@ public: static QLatin1String displayName(const ProjectExplorer::Abi &abi); QString getProductModel(const QString &device) const; - enum class OpenGl { Enabled, Disabled, Unknown }; - OpenGl getOpenGLEnabled(const QString &emulator) const; bool isConnected(const QString &serialNumber) const; - bool isCmdlineSdkToolsInstalled() const; + bool preCmdlineSdkToolsInstalled() const; bool sdkFullyConfigured() const { return m_sdkFullyConfigured; } void setSdkFullyConfigured(bool allEssentialsInstalled) { m_sdkFullyConfigured = allEssentialsInstalled; } @@ -190,6 +191,7 @@ private: QList<int> availableNdkPlatforms(const QtSupport::QtVersion *qtVersion) const; Utils::FilePath m_sdkLocation; + Utils::FilePath m_temporarySdkToolsPath; QStringList m_sdkManagerToolArgs; Utils::FilePath m_openJDKLocation; Utils::FilePath m_keystoreLocation; diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h index 02bb50b348e..83012d8073e 100644 --- a/src/plugins/android/androidconstants.h +++ b/src/plugins/android/androidconstants.h @@ -99,11 +99,11 @@ const char SdkLocation[] = "SdkLocation"; // FileName const Utils::Id AndroidSerialNumber = "AndroidSerialNumber"; const Utils::Id AndroidAvdName = "AndroidAvdName"; const Utils::Id AndroidCpuAbi = "AndroidCpuAbi"; -const Utils::Id AndroidAvdTarget = "AndroidAvdTarget"; -const Utils::Id AndroidAvdDevice = "AndroidAvdDevice"; -const Utils::Id AndroidAvdSkin = "AndroidAvdSkin"; -const Utils::Id AndroidAvdSdcard = "AndroidAvdSdcard"; const Utils::Id AndroidSdk = "AndroidSdk"; +const Utils::Id AndroidAvdPath = "AndroidAvdPath"; + +// SDK Tools +const char cmdlineToolsName[] = "cmdline-tools"; } // namespace Constants; } // namespace Android diff --git a/src/plugins/android/androidcreatekeystorecertificate.cpp b/src/plugins/android/androidcreatekeystorecertificate.cpp index 48c91c6bae8..b17ac11993b 100644 --- a/src/plugins/android/androidcreatekeystorecertificate.cpp +++ b/src/plugins/android/androidcreatekeystorecertificate.cpp @@ -208,9 +208,9 @@ void AndroidCreateKeystoreCertificate::buttonBoxAccepted() QtcProcess genKeyCertProc; genKeyCertProc.setTimeoutS(15); genKeyCertProc.setCommand(command); - genKeyCertProc.runBlocking(QtcProcess::WithEventLoop); + genKeyCertProc.runBlocking(EventLoopMode::On); - if (genKeyCertProc.result() != QtcProcess::FinishedWithSuccess) { + if (genKeyCertProc.result() != ProcessResult::FinishedWithSuccess) { QMessageBox::critical(this, tr("Error"), genKeyCertProc.exitMessage() + '\n' + genKeyCertProc.allOutput()); return; diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 89ea6de806a..6cfe8d9f806 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -209,7 +209,7 @@ bool AndroidDeployQtStep::init() .arg(info.cpuAbi.first()))); } - m_avdName = info.avdname; + m_avdName = info.avdName; m_serialNumber = info.serialNumber; qCDebug(deployStepLog) << "Selected device info:" << info; @@ -528,8 +528,8 @@ void AndroidDeployQtStep::runCommand(const CommandLine &command) OutputFormat::NormalMessage); buildProc.setCommand(command); - buildProc.runBlocking(QtcProcess::WithEventLoop); - if (buildProc.result() != QtcProcess::FinishedWithSuccess) + buildProc.runBlocking(EventLoopMode::On); + if (buildProc.result() != ProcessResult::FinishedWithSuccess) reportWarningOrError(buildProc.exitMessage(), Task::Error); } diff --git a/src/plugins/android/androiddeployqtstep.h b/src/plugins/android/androiddeployqtstep.h index 0e0f1c3054e..db9648df808 100644 --- a/src/plugins/android/androiddeployqtstep.h +++ b/src/plugins/android/androiddeployqtstep.h @@ -32,8 +32,10 @@ #include <projectexplorer/abstractprocessstep.h> #include <qtsupport/baseqtversion.h> +#include <utils/commandline.h> #include <utils/environment.h> -#include <utils/qtcprocess.h> + +namespace Utils { class QtcProcess; } namespace Android { namespace Internal { diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index dac30fd1446..d0b782df442 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -60,6 +60,7 @@ #include <utils/qtcprocess.h> using namespace ProjectExplorer; +using namespace Utils; namespace { static Q_LOGGING_CATEGORY(androidDeviceLog, "qtc.android.androiddevice", QtWarningMsg) @@ -118,7 +119,7 @@ AndroidDeviceWidget::AndroidDeviceWidget(const IDevice::Ptr &device) formLayout->addRow(AndroidDevice::tr("Android target flavor:"), new QLabel(targetName)); formLayout->addRow(AndroidDevice::tr("SD card size:"), new QLabel(dev->sdcardSize())); formLayout->addRow(AndroidDevice::tr("Skin type:"), new QLabel(dev->skinName())); - const QString openGlStatus = dev->openGlStatusString(); + const QString openGlStatus = dev->openGLStatus(); formLayout->addRow(AndroidDevice::tr("OpenGL status:"), new QLabel(openGlStatus)); } } @@ -162,8 +163,8 @@ AndroidDevice::AndroidDevice() setDefaultDisplayName(tr("Run on Android")); setDisplayType(tr("Android")); setMachineType(IDevice::Hardware); - setOsType(Utils::OsTypeOtherUnix); - setDeviceState(DeviceConnected); + setOsType(OsType::OsTypeOtherUnix); + setDeviceState(DeviceDisconnected); addDeviceAction({tr("Refresh"), [](const IDevice::Ptr &device, QWidget *parent) { Q_UNUSED(parent) @@ -217,6 +218,7 @@ void AndroidDevice::addEmulatorActionsIfNotFound() void AndroidDevice::fromMap(const QVariantMap &map) { IDevice::fromMap(map); + initAvdSettings(); // Add Actions for Emulator is not added already. // This is needed because actions for Emulators and physical devices are not the same. addEmulatorActionsIfNotFound(); @@ -231,47 +233,33 @@ AndroidDeviceInfo AndroidDevice::androidDeviceInfoFromIDevice(const IDevice *dev { AndroidDeviceInfo info; info.state = dev->deviceState(); - info.avdname = dev->extraData(Constants::AndroidAvdName).toString(); + info.avdName = dev->extraData(Constants::AndroidAvdName).toString(); info.serialNumber = dev->extraData(Constants::AndroidSerialNumber).toString(); info.cpuAbi = dev->extraData(Constants::AndroidCpuAbi).toStringList(); - info.avdTarget = dev->extraData(Constants::AndroidAvdTarget).toString(); - info.avdDevice = dev->extraData(Constants::AndroidAvdDevice).toString(); - info.avdSkin = dev->extraData(Constants::AndroidAvdSkin).toString(); - info.avdSdcardSize = dev->extraData(Constants::AndroidAvdSdcard).toString(); + const QString avdPath = dev->extraData(Constants::AndroidAvdPath).toString(); + info.avdPath = FilePath::fromUserInput(avdPath); info.sdk = dev->extraData(Constants::AndroidSdk).toInt(); info.type = dev->machineType(); return info; } -void AndroidDevice::setAndroidDeviceInfoExtras(IDevice *dev, const AndroidDeviceInfo &info) -{ - dev->setExtraData(Constants::AndroidAvdName, info.avdname); - dev->setExtraData(Constants::AndroidSerialNumber, info.serialNumber); - dev->setExtraData(Constants::AndroidCpuAbi, info.cpuAbi); - dev->setExtraData(Constants::AndroidAvdTarget, info.avdTarget); - dev->setExtraData(Constants::AndroidAvdDevice, info.avdDevice); - dev->setExtraData(Constants::AndroidAvdSkin, info.avdSkin); - dev->setExtraData(Constants::AndroidAvdSdcard, info.avdSdcardSize); - dev->setExtraData(Constants::AndroidSdk, info.sdk); -} - QString AndroidDevice::displayNameFromInfo(const AndroidDeviceInfo &info) { return info.type == IDevice::Hardware ? AndroidConfigurations::currentConfig().getProductModel(info.serialNumber) - : info.avdname; + : info.avdName; } -Utils::Id AndroidDevice::idFromDeviceInfo(const AndroidDeviceInfo &info) +Id AndroidDevice::idFromDeviceInfo(const AndroidDeviceInfo &info) { - const QString id = (info.type == IDevice::Hardware ? info.serialNumber : info.avdname); - return Utils::Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + id); + const QString id = (info.type == IDevice::Hardware ? info.serialNumber : info.avdName); + return Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + id); } -Utils::Id AndroidDevice::idFromAvdInfo(const CreateAvdInfo &info) +Id AndroidDevice::idFromAvdInfo(const CreateAvdInfo &info) { - return Utils::Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + info.name); + return Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + info.name); } QStringList AndroidDevice::supportedAbis() const @@ -344,6 +332,17 @@ int AndroidDevice::sdkLevel() const return extraData(Constants::AndroidSdk).toInt(); } +FilePath AndroidDevice::avdPath() const +{ + return FilePath::fromUserInput(extraData(Constants::AndroidAvdPath).toString()); +} + +void AndroidDevice::setAvdPath(const FilePath &path) +{ + setExtraData(Constants::AndroidAvdPath, path.toUserOutput()); + initAvdSettings(); +} + QString AndroidDevice::androidVersion() const { return AndroidManager::androidNameForApiLevel(sdkLevel()); @@ -352,46 +351,32 @@ QString AndroidDevice::androidVersion() const QString AndroidDevice::deviceTypeName() const { if (machineType() == Emulator) - return tr("Emulator for ") + extraData(Constants::AndroidAvdDevice).toString(); + return tr("Emulator for \"%1\"").arg(avdSettings()->value("hw.device.name").toString()); return tr("Physical device"); } QString AndroidDevice::skinName() const { - const QString skin = extraData(Constants::AndroidAvdSkin).toString(); + const QString skin = avdSettings()->value("skin.name").toString(); return skin.isEmpty() ? tr("None") : skin; } QString AndroidDevice::androidTargetName() const { - const QString target = extraData(Constants::AndroidAvdTarget).toString(); + const QString target = avdSettings()->value("tag.display").toString(); return target.isEmpty() ? tr("Unknown") : target; } QString AndroidDevice::sdcardSize() const { - const QString size = extraData(Constants::AndroidAvdSdcard).toString(); + const QString size = avdSettings()->value("sdcard.size").toString(); return size.isEmpty() ? tr("Unknown") : size; } -AndroidConfig::OpenGl AndroidDevice::openGlStatus() const +QString AndroidDevice::openGLStatus() const { - return AndroidConfigurations::currentConfig().getOpenGLEnabled(displayName()); -} - -QString AndroidDevice::openGlStatusString() const -{ - const AndroidConfig::OpenGl glStatus = AndroidConfigurations::currentConfig() - .getOpenGLEnabled(displayName()); - switch (glStatus) { - case (AndroidConfig::OpenGl::Enabled): - return tr("Enabled"); - case (AndroidConfig::OpenGl::Disabled): - return tr("Disabled"); - case (AndroidConfig::OpenGl::Unknown): - return tr("Unknown"); - } - return tr("Unknown"); + const QString openGL = avdSettings()->value("hw.gpu.enabled").toString(); + return openGL.isEmpty() ? tr("Unknown") : openGL; } IDevice::DeviceInfo AndroidDevice::deviceInformation() const @@ -422,6 +407,17 @@ QUrl AndroidDevice::toolControlChannel(const ControlChannelHint &) const return url; } +QSettings *AndroidDevice::avdSettings() const +{ + return m_avdSettings.get(); +} + +void AndroidDevice::initAvdSettings() +{ + const FilePath configPath = avdPath().resolvePath(QStringLiteral("config.ini")); + m_avdSettings.reset(new QSettings(configPath.toUserOutput(), QSettings::IniFormat)); +} + void AndroidDeviceManager::updateAvdsList() { if (!m_avdsFutureWatcher.isRunning() && m_androidConfig.adbToolPath().exists()) @@ -447,12 +443,10 @@ void AndroidDeviceManager::updateDeviceState(const ProjectExplorer::IDevice::Con const QString serial = dev->serialNumber(); DeviceManager *const devMgr = DeviceManager::instance(); const Utils::Id id = dev->id(); - if (serial.isEmpty() && dev->machineType() == IDevice::Emulator) { + if (!serial.isEmpty()) + devMgr->setDeviceState(id, getDeviceState(serial, dev->machineType())); + else if (dev->machineType() == IDevice::Emulator) devMgr->setDeviceState(id, IDevice::DeviceConnected); - return; - } - - devMgr->setDeviceState(id, getDeviceState(serial, dev->machineType())); } void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent) @@ -647,38 +641,41 @@ void AndroidDeviceManager::HandleAvdsListChange() { DeviceManager *const devMgr = DeviceManager::instance(); - QVector<IDevice::ConstPtr> existingAvds; + QVector<Id> existingAvds; for (int i = 0; i < devMgr->deviceCount(); ++i) { const IDevice::ConstPtr dev = devMgr->deviceAt(i); const bool isEmulator = dev->machineType() == IDevice::Emulator; if (isEmulator && dev->type() == Constants::ANDROID_DEVICE_TYPE) - existingAvds.append(dev); + existingAvds.append(dev->id()); } - QVector<IDevice::ConstPtr> connectedDevs; + QVector<Id> connectedDevs; for (const AndroidDeviceInfo &item : m_avdsFutureWatcher.result()) { const Utils::Id deviceId = AndroidDevice::idFromDeviceInfo(item); const QString displayName = AndroidDevice::displayNameFromInfo(item); IDevice::ConstPtr dev = devMgr->find(deviceId); if (!dev.isNull()) { const auto androidDev = static_cast<const AndroidDevice *>(dev.data()); - // DeviceManager doens't seem to hav a way to directly update the name, if the name + // DeviceManager doens't seem to have a way to directly update the name, if the name // of the device has changed, remove it and register it again with the new name. // Also account for the case of an AVD registered through old QC which might have - // invalid data by checking the sdcard size value. - if (dev->displayName() != displayName - || androidDev->sdcardSize() == AndroidDevice::tr("Unknown")) { + // invalid data by checking if the avdPath is not empty. + if (dev->displayName() != displayName || androidDev->avdPath().toString().isEmpty()) { devMgr->removeDevice(dev->id()); } else { // Find the state of the AVD retrieved from the AVD watcher - const QString serial = getRunningAvdsSerialNumber(item.avdname); - const IDevice::DeviceState state = getDeviceState(serial, IDevice::Emulator); - if (dev->deviceState() != state) { - devMgr->setDeviceState(dev->id(), state); - qCDebug(androidDeviceLog, "Device id \"%s\" changed its state.", - dev->id().toString().toUtf8().data()); + const QString serial = getRunningAvdsSerialNumber(item.avdName); + if (!serial.isEmpty()) { + const IDevice::DeviceState state = getDeviceState(serial, IDevice::Emulator); + if (dev->deviceState() != state) { + devMgr->setDeviceState(dev->id(), state); + qCDebug(androidDeviceLog, "Device id \"%s\" changed its state.", + dev->id().toString().toUtf8().data()); + } + } else { + devMgr->setDeviceState(dev->id(), IDevice::DeviceConnected); } - connectedDevs.append(dev); + connectedDevs.append(dev->id()); continue; } } @@ -688,20 +685,26 @@ void AndroidDeviceManager::HandleAvdsListChange() newDev->setDisplayName(displayName); newDev->setMachineType(item.type); newDev->setDeviceState(item.state); - AndroidDevice::setAndroidDeviceInfoExtras(newDev, item); + + newDev->setExtraData(Constants::AndroidAvdName, item.avdName); + newDev->setExtraData(Constants::AndroidSerialNumber, item.serialNumber); + newDev->setExtraData(Constants::AndroidCpuAbi, item.cpuAbi); + newDev->setExtraData(Constants::AndroidSdk, item.sdk); + newDev->setAvdPath(item.avdPath); + qCDebug(androidDeviceLog, "Registering new Android device id \"%s\".", newDev->id().toString().toUtf8().data()); const IDevice::ConstPtr constNewDev = IDevice::ConstPtr(newDev); devMgr->addDevice(IDevice::ConstPtr(constNewDev)); - connectedDevs.append(constNewDev); + connectedDevs.append(constNewDev->id()); + } - // Set devices no longer connected to disconnected state. - for (const IDevice::ConstPtr &dev : existingAvds) { - if (!connectedDevs.contains(dev)) { + // Set devices no longer connected to disconnected state. + for (const Id &id : existingAvds) { + if (!connectedDevs.contains(id)) { qCDebug(androidDeviceLog, "Removing AVD id \"%s\" because it no longer exists.", - dev->id().toString().toUtf8().data()); - devMgr->removeDevice(dev->id()); - } + id.toString().toUtf8().data()); + devMgr->removeDevice(id); } } } diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index 5ecf8101e07..574e2756f1a 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -35,10 +35,11 @@ #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/devicesupport/idevicefactory.h> -#include <utils/qtcprocess.h> - #include <QFutureWatcher> #include <QFileSystemWatcher> +#include <QSettings> + +namespace Utils { class QtcProcess; } namespace Android { namespace Internal { @@ -52,7 +53,6 @@ public: static IDevice::Ptr create(); static AndroidDeviceInfo androidDeviceInfoFromIDevice(const IDevice *dev); - static void setAndroidDeviceInfoExtras(IDevice *dev, const AndroidDeviceInfo &info); static QString displayNameFromInfo(const AndroidDeviceInfo &info); static Utils::Id idFromDeviceInfo(const AndroidDeviceInfo &info); @@ -68,14 +68,17 @@ public: QString avdName() const; int sdkLevel() const; + Utils::FilePath avdPath() const; + void setAvdPath(const Utils::FilePath &path); + QString deviceTypeName() const; QString androidVersion() const; + + // AVD specific QString skinName() const; QString androidTargetName() const; QString sdcardSize() const; - QString openGlStatusString() const; - // TODO: remove not used - AndroidConfig::OpenGl openGlStatus() const; + QString openGLStatus() const; protected: void fromMap(const QVariantMap &map) final; @@ -87,6 +90,11 @@ private: bool canAutoDetectPorts() const override; ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override; QUrl toolControlChannel(const ControlChannelHint &) const override; + + QSettings *avdSettings() const; + void initAvdSettings(); + + std::unique_ptr<QSettings> m_avdSettings; }; class AndroidDeviceFactory final : public ProjectExplorer::IDeviceFactory diff --git a/src/plugins/android/androiddeviceinfo.cpp b/src/plugins/android/androiddeviceinfo.cpp index 752a710c14d..30ac8c2f0f1 100644 --- a/src/plugins/android/androiddeviceinfo.cpp +++ b/src/plugins/android/androiddeviceinfo.cpp @@ -46,25 +46,24 @@ bool AndroidDeviceInfo::operator<(const AndroidDeviceInfo &other) const return type == ProjectExplorer::IDevice::Hardware; if (sdk != other.sdk) return sdk < other.sdk; - if (avdname != other.avdname) - return avdname < other.avdname; + if (avdName != other.avdName) + return avdName < other.avdName; return serialNumber < other.serialNumber; } bool AndroidDeviceInfo::operator==(const AndroidDeviceInfo &other) const { - return serialNumber == other.serialNumber && avdname == other.avdname && cpuAbi == other.cpuAbi - && avdTarget == other.avdTarget && avdDevice == other.avdDevice - && avdSkin == other.avdSkin && avdSdcardSize == other.avdSdcardSize && sdk == other.sdk - && state == other.state && type == other.type; + return serialNumber == other.serialNumber && avdName == other.avdName + && avdPath == other.avdPath && cpuAbi == other.cpuAbi + && sdk == other.sdk && state == other.state && type == other.type; } QDebug &operator<<(QDebug &stream, const AndroidDeviceInfo &device) { stream << "Type:" << (device.type == ProjectExplorer::IDevice::Emulator ? "Emulator" : "Device") << ", ABI:" << device.cpuAbi << ", Serial:" << device.serialNumber - << ", Name:" << device.avdname << ", API:" << device.sdk + << ", Name:" << device.avdName << ", API:" << device.sdk << ", Authorised:" << (device.state == IDevice::DeviceReadyToUse); return stream; } diff --git a/src/plugins/android/androiddeviceinfo.h b/src/plugins/android/androiddeviceinfo.h index b43cbc63426..8b6e4c0e921 100644 --- a/src/plugins/android/androiddeviceinfo.h +++ b/src/plugins/android/androiddeviceinfo.h @@ -40,19 +40,16 @@ class AndroidDeviceInfo { public: QString serialNumber; - QString avdname; + QString avdName; QStringList cpuAbi; - QString avdTarget; - QString avdDevice; - QString avdSkin; - QString avdSdcardSize; int sdk = -1; IDevice::DeviceState state = IDevice::DeviceDisconnected; IDevice::MachineType type = IDevice::Emulator; + Utils::FilePath avdPath; static QStringList adbSelector(const QString &serialNumber); - bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); } + bool isValid() const { return !serialNumber.isEmpty() || !avdName.isEmpty(); } bool operator<(const AndroidDeviceInfo &other) const; bool operator==(const AndroidDeviceInfo &other) const; // should be = default with C++20 bool operator!=(const AndroidDeviceInfo &other) const { return !(*this == other); } diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 3fadbf6f9be..ffa3d307916 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -573,7 +573,7 @@ void AndroidManager::installQASIPackage(Target *target, const FilePath &packageP QString deviceSerialNumber = info.serialNumber; if (info.type == IDevice::Emulator) { - deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname); + deviceSerialNumber = AndroidAvdManager().startAvd(info.avdName); if (deviceSerialNumber.isEmpty()) Core::MessageManager::writeDisrupting(tr("Starting Android virtual device failed.")); } @@ -595,8 +595,8 @@ bool AndroidManager::checkKeystorePassword(const QString &keystorePath, const QS QtcProcess proc; proc.setTimeoutS(10); proc.setCommand(cmd); - proc.runBlocking(QtcProcess::WithEventLoop); - return proc.result() == QtcProcess::FinishedWithSuccess; + proc.runBlocking(EventLoopMode::On); + return proc.result() == ProcessResult::FinishedWithSuccess; } bool AndroidManager::checkCertificatePassword(const QString &keystorePath, const QString &keystorePasswd, const QString &alias, const QString &certificatePasswd) @@ -612,8 +612,8 @@ bool AndroidManager::checkCertificatePassword(const QString &keystorePath, const QtcProcess proc; proc.setTimeoutS(10); proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments}); - proc.runBlocking(QtcProcess::WithEventLoop); - return proc.result() == QtcProcess::FinishedWithSuccess; + proc.runBlocking(EventLoopMode::On); + return proc.result() == ProcessResult::FinishedWithSuccess; } bool AndroidManager::checkCertificateExists(const QString &keystorePath, @@ -626,8 +626,8 @@ bool AndroidManager::checkCertificateExists(const QString &keystorePath, QtcProcess proc; proc.setTimeoutS(10); proc.setCommand({AndroidConfigurations::currentConfig().keytoolPath(), arguments}); - proc.runBlocking(QtcProcess::WithEventLoop); - return proc.result() == QtcProcess::FinishedWithSuccess; + proc.runBlocking(EventLoopMode::On); + return proc.result() == ProcessResult::FinishedWithSuccess; } using GradleProperties = QMap<QByteArray, QByteArray>; @@ -781,10 +781,10 @@ SdkToolResult AndroidManager::runCommand(const CommandLine &command, cmdProc.setWriteData(writeData); qCDebug(androidManagerLog) << "Running command (sync):" << command.toUserOutput(); cmdProc.setCommand(command); - cmdProc.runBlocking(QtcProcess::WithEventLoop); + cmdProc.runBlocking(EventLoopMode::On); cmdResult.m_stdOut = cmdProc.stdOut().trimmed(); cmdResult.m_stdErr = cmdProc.stdErr().trimmed(); - cmdResult.m_success = cmdProc.result() == QtcProcess::FinishedWithSuccess; + cmdResult.m_success = cmdProc.result() == ProcessResult::FinishedWithSuccess; qCDebug(androidManagerLog) << "Command finshed (sync):" << command.toUserOutput() << "Success:" << cmdResult.m_success << "Output:" << cmdProc.allRawOutput(); diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp index cb809454015..f8dd04bd42d 100644 --- a/src/plugins/android/androidqmlpreviewworker.cpp +++ b/src/plugins/android/androidqmlpreviewworker.cpp @@ -250,7 +250,7 @@ bool AndroidQmlPreviewWorker::ensureAvdIsRunning() if (devInfoLocal.isValid()) { if (dev->machineType() == IDevice::Emulator) { appendMessage(tr("Launching AVD."), NormalMessageFormat); - devInfoLocal.serialNumber = avdMananager.startAvd(devInfoLocal.avdname); + devInfoLocal.serialNumber = avdMananager.startAvd(devInfoLocal.avdName); } if (devInfoLocal.serialNumber.isEmpty()) { appendMessage(tr("Could not start AVD."), ErrorMessageFormat); diff --git a/src/plugins/android/androidqmlpreviewworker.h b/src/plugins/android/androidqmlpreviewworker.h index 68e541a8ec8..9b6bd5cce97 100644 --- a/src/plugins/android/androidqmlpreviewworker.h +++ b/src/plugins/android/androidqmlpreviewworker.h @@ -27,7 +27,8 @@ #include "androidconfigurations.h" #include <projectexplorer/runcontrol.h> -#include <utils/environment.h> + +#include <utils/qtcprocess.h> #include <QFutureWatcher> diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index b8be01ae808..3156ecda5c3 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -193,9 +193,9 @@ void AndroidRunner::launchAVD() emit androidDeviceInfoChanged(info); if (info.isValid()) { AndroidAvdManager avdManager; - if (!info.avdname.isEmpty() && avdManager.findAvd(info.avdname).isEmpty()) { - bool launched = avdManager.startAvdAsync(info.avdname); - m_launchedAVDName = launched ? info.avdname:""; + if (!info.avdName.isEmpty() && avdManager.findAvd(info.avdName).isEmpty()) { + bool launched = avdManager.startAvdAsync(info.avdName); + m_launchedAVDName = launched ? info.avdName:""; } else { m_launchedAVDName.clear(); } diff --git a/src/plugins/android/androidsdkdownloader.cpp b/src/plugins/android/androidsdkdownloader.cpp index c9512b464a9..91319cb8a96 100644 --- a/src/plugins/android/androidsdkdownloader.cpp +++ b/src/plugins/android/androidsdkdownloader.cpp @@ -25,6 +25,8 @@ #include "androidsdkdownloader.h" +#include "androidconstants.h" + #include <utils/archive.h> #include <utils/filepath.h> @@ -61,7 +63,7 @@ void AndroidSdkDownloader::sslErrors(const QList<QSslError> &sslErrors) } #endif -void AndroidSdkDownloader::downloadAndExtractSdk(const FilePath &sdkExtractPath) +void AndroidSdkDownloader::downloadAndExtractSdk() { if (m_androidConfig.sdkToolsUrl().isEmpty()) { logError(tr("The SDK Tools download URL is empty.")); @@ -88,11 +90,17 @@ void AndroidSdkDownloader::downloadAndExtractSdk(const FilePath &sdkExtractPath) connect(m_progressDialog, &QProgressDialog::canceled, this, &AndroidSdkDownloader::cancel); - connect(this, &AndroidSdkDownloader::sdkPackageWriteFinished, this, [this, sdkExtractPath]() { - if (Archive *archive = Archive::unarchive(m_sdkFilename, sdkExtractPath)) { - connect(archive, &Archive::finished, [this, sdkExtractPath](bool success){ - if (success) + connect(this, &AndroidSdkDownloader::sdkPackageWriteFinished, this, [this]() { + const FilePath extractDir = m_sdkFilename.parentDir(); + if (Archive *archive = Archive::unarchive(m_sdkFilename, extractDir)) { + connect(archive, &Archive::finished, [this, extractDir](bool success) { + if (success) { + // Save the extraction path temporarily which can be used by sdkmanager + // to install essential packages at firt time setup. + m_androidConfig.setTemporarySdkToolsPath( + extractDir.pathAppended(Constants::cmdlineToolsName)); emit sdkExtracted(); + } }); } }); @@ -153,7 +161,7 @@ FilePath AndroidSdkDownloader::getSaveFilename(const QUrl &url) basename += QString::number(i); } - return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) + return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::TempLocation)) / basename; } diff --git a/src/plugins/android/androidsdkdownloader.h b/src/plugins/android/androidsdkdownloader.h index 76375132397..1cfe5b950dd 100644 --- a/src/plugins/android/androidsdkdownloader.h +++ b/src/plugins/android/androidsdkdownloader.h @@ -43,7 +43,7 @@ class AndroidSdkDownloader : public QObject public: AndroidSdkDownloader(); - void downloadAndExtractSdk(const Utils::FilePath &sdkExtractPath); + void downloadAndExtractSdk(); static QString dialogTitle(); void cancel(); @@ -71,7 +71,7 @@ private: QNetworkReply *m_reply = nullptr; Utils::FilePath m_sdkFilename; QProgressDialog *m_progressDialog = nullptr; - const AndroidConfig &m_androidConfig; + AndroidConfig &m_androidConfig; }; } // Internal diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 15636de0789..41dab000895 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -138,10 +138,10 @@ static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &ar proc.setTimeoutS(timeout); proc.setTimeOutMessageBoxEnabled(true); proc.setCommand({config.sdkManagerToolPath(), newArgs}); - proc.runBlocking(QtcProcess::WithEventLoop); + proc.runBlocking(EventLoopMode::On); if (output) *output = proc.allOutput(); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } /*! @@ -179,7 +179,7 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar &proc, &QtcProcess::stopProcess); } proc.setCommand({config.sdkManagerToolPath(), newArgs}); - proc.runBlocking(QtcProcess::WithEventLoop); + proc.runBlocking(EventLoopMode::On); if (assertionFound) { output.success = false; output.stdOutput = proc.stdOut(); @@ -187,7 +187,7 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar "The operation requires user interaction. " "Use the \"sdkmanager\" command-line tool."); } else { - output.success = proc.result() == QtcProcess::FinishedWithSuccess; + output.success = proc.result() == ProcessResult::FinishedWithSuccess; } } @@ -303,18 +303,18 @@ private: using MarkerTagsType = std::map<SdkManagerOutputParser::MarkerTag, const char *>; Q_GLOBAL_STATIC_WITH_ARGS(MarkerTagsType, markerTags, ({ - {SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"}, - {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"}, - {SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"}, - {SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"}, - {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"}, - {SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"}, - {SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"}, - {SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, "cmdline-tools"}, - {SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"}, - {SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"}, - {SdkManagerOutputParser::MarkerTag::NdkMarker, "ndk"}, - {SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"} + {SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"}, + {SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"}, + {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"}, + {SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"}, + {SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"}, + {SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, Constants::cmdlineToolsName}, + {SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"}, + {SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"}, + {SdkManagerOutputParser::MarkerTag::NdkMarker, "ndk"}, + {SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"} })); AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config): @@ -1017,9 +1017,12 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi) { fi.setProgressRange(0, 100); fi.setProgressValue(0); + AndroidSdkManager::OperationOutput result; result.type = AndroidSdkManager::LicenseWorkflow; - QtcProcess licenseCommand(ProcessMode::Writer); + + QtcProcess licenseCommand; + licenseCommand.setProcessMode(ProcessMode::Writer); licenseCommand.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config)); bool reviewingLicenses = false; licenseCommand.setCommand(CommandLine(m_config.sdkManagerToolPath(), {"--licenses", sdkRootArg(m_config)})); diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index e928023937a..00e597b39fb 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -452,6 +452,13 @@ AndroidSettingsWidget::AndroidSettingsWidget() QMessageBox::warning(this, AndroidSdkDownloader::dialogTitle(), error); }); connect(&m_sdkDownloader, &AndroidSdkDownloader::sdkExtracted, this, [this] { + // Make sure the sdk path is created before installing packages + const FilePath sdkPath = m_androidConfig.sdkLocation(); + if (!sdkPath.createDir()) { + QMessageBox::warning(this, AndroidSdkDownloader::dialogTitle(), + tr("Failed to create the SDK Tools path %1.") + .arg("\n\"" + sdkPath.toUserOutput() + "\"")); + } m_sdkManager.reloadPackages(true); updateUI(); apply(); @@ -636,7 +643,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent) m_ui.openSslPathChooser->triggerChanged(); // After cloning, the path exists if (!openSslProgressDialog->wasCanceled() - || gitCloner->result() == QtcProcess::FinishedWithError) { + || gitCloner->result() == ProcessResult::FinishedWithError) { failDialog(); } }); @@ -708,7 +715,7 @@ void AndroidSettingsWidget::downloadSdk() auto userInput = QMessageBox::information(this, AndroidSdkDownloader::dialogTitle(), message, QMessageBox::Yes | QMessageBox::No); if (userInput == QMessageBox::Yes) - m_sdkDownloader.downloadAndExtractSdk(m_ui.SDKLocationPathChooser->filePath().cleanPath()); + m_sdkDownloader.downloadAndExtractSdk(); } // AndroidSettingsPage diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index b44af13720b..b92f4cedf84 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -145,10 +145,6 @@ ProjectExplorer::IDevice::Ptr AvdDialog::device() const dev->setExtraData(Constants::AndroidAvdName, m_createdAvdInfo.name); dev->setExtraData(Constants::AndroidCpuAbi, {m_createdAvdInfo.abi}); dev->setExtraData(Constants::AndroidSdk, m_createdAvdInfo.systemImage->apiLevel()); - dev->setExtraData(Constants::AndroidAvdSdcard, QString("%1 MB") - .arg(m_createdAvdInfo.sdcardSize)); - dev->setExtraData(Constants::AndroidAvdDevice, m_createdAvdInfo.deviceDefinition); - return IDevice::Ptr(dev); } diff --git a/src/plugins/android/avdmanageroutputparser.cpp b/src/plugins/android/avdmanageroutputparser.cpp index 892405c33e4..49652143348 100644 --- a/src/plugins/android/avdmanageroutputparser.cpp +++ b/src/plugins/android/avdmanageroutputparser.cpp @@ -46,10 +46,6 @@ const char avdInfoPathKey[] = "Path:"; const char avdInfoAbiKey[] = "abi.type"; const char avdInfoTargetKey[] = "target"; const char avdInfoErrorKey[] = "Error:"; -const char avdInfoSdcardKey[] = "Sdcard"; -const char avdInfoTargetTypeKey[] = "Target"; -const char avdInfoDeviceKey[] = "Device"; -const char avdInfoSkinKey[] = "Skin"; namespace Android { namespace Internal { @@ -78,9 +74,10 @@ static Utils::optional<AndroidDeviceInfo> parseAvd(const QStringList &deviceInfo qCDebug(avdOutputParserLog) << "Avd Parsing: Skip avd device. Error key found:" << line; return {}; } else if (valueForKey(avdInfoNameKey, line, &value)) { - avd.avdname = value; + avd.avdName = value; } else if (valueForKey(avdInfoPathKey, line, &value)) { const Utils::FilePath avdPath = Utils::FilePath::fromUserInput(value); + avd.avdPath = avdPath; if (avdPath.exists()) { // Get ABI. const Utils::FilePath configFile = avdPath.pathAppended("config.ini"); @@ -92,7 +89,7 @@ static Utils::optional<AndroidDeviceInfo> parseAvd(const QStringList &deviceInfo qCDebug(avdOutputParserLog) << "Avd Parsing: Cannot find ABI:" << configFile; // Get Target - const QString avdInfoFileName = avd.avdname + ".ini"; + const QString avdInfoFileName = avd.avdName + ".ini"; const Utils::FilePath avdInfoFile = avdPath.parentDir().pathAppended( avdInfoFileName); QSettings avdInfo(avdInfoFile.toString(), QSettings::IniFormat); @@ -103,14 +100,6 @@ static Utils::optional<AndroidDeviceInfo> parseAvd(const QStringList &deviceInfo qCDebug(avdOutputParserLog) << "Avd Parsing: Cannot find sdk API:" << avdInfoFile.toString(); } - } else if (valueForKey(avdInfoDeviceKey, line, &value)) { - avd.avdDevice = value.remove(0, 2); - } else if (valueForKey(avdInfoTargetTypeKey, line, &value)) { - avd.avdTarget = value.remove(0, 2); - } else if (valueForKey(avdInfoSkinKey, line, &value)) { - avd.avdSkin = value.remove(0, 2); - } else if (valueForKey(avdInfoSdcardKey, line, &value)) { - avd.avdSdcardSize = value.remove(0, 2); } } if (avd != AndroidDeviceInfo()) diff --git a/src/plugins/autotest/testsettingspage.ui b/src/plugins/autotest/testsettingspage.ui index 0ea7404370d..326a02966d7 100644 --- a/src/plugins/autotest/testsettingspage.ui +++ b/src/plugins/autotest/testsettingspage.ui @@ -106,20 +106,20 @@ <item> <widget class="QCheckBox" name="openResultsOnStartCB"> <property name="toolTip"> - <string>Opens the test results pane automatically when tests are started.</string> + <string>Displays test results automatically when tests are started.</string> </property> <property name="text"> - <string>Open results pane when tests start</string> + <string>Open results when tests start</string> </property> </widget> </item> <item> <widget class="QCheckBox" name="openResultsOnFinishCB"> <property name="toolTip"> - <string>Opens the test result pane automatically when tests are finished.</string> + <string>Displays test results automatically when tests are finished.</string> </property> <property name="text"> - <string>Open results pane when tests finish</string> + <string>Open results when tests finish</string> </property> <property name="checked"> <bool>true</bool> @@ -147,7 +147,7 @@ <item> <widget class="QCheckBox" name="openResultsOnFailCB"> <property name="toolTip"> - <string>Opens the test result pane only if the test run contains failed, fatal or unexpectedly passed tests.</string> + <string>Displays test results only if the test run contains failed, fatal or unexpectedly passed tests.</string> </property> <property name="text"> <string>Only for unsuccessful test runs</string> diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp index 90b3dbd2b93..db1722043c3 100644 --- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp @@ -348,7 +348,9 @@ GdbServerProviderRunner::GdbServerProviderRunner(ProjectExplorer::RunControl *ru { setId("BareMetalGdbServer"); // Baremetal's GDB servers are launched on the host, not on the target. - setStarter([this, runnable] { doStart(runnable, {}); }); + Runnable devicelessRunnable = runnable; + devicelessRunnable.device.reset(); + setStarter([this, devicelessRunnable] { doStart(devicelessRunnable); }); } } // namespace Internal diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index a79a6380081..bc6d2757dfa 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -113,7 +113,7 @@ static Macros dumpPredefinedMacros(const FilePath &compiler, const QStringList & cpp.setCommand(cmd); cpp.runBlocking(); - if (cpp.result() != QtcProcess::FinishedWithSuccess) { + if (cpp.result() != ProcessResult::FinishedWithSuccess) { qWarning() << cpp.exitMessage(); return {}; } diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp index 39cac8a2ca9..8b1d9683262 100644 --- a/src/plugins/baremetal/keiltoolchain.cpp +++ b/src/plugins/baremetal/keiltoolchain.cpp @@ -287,7 +287,7 @@ static Macros dumpArmPredefinedMacros(const FilePath &compiler, const QStringLis cpp.setCommand({compiler, args}); cpp.runBlocking(); - if (cpp.result() != QtcProcess::FinishedWithSuccess) { + if (cpp.result() != ProcessResult::FinishedWithSuccess) { qWarning() << cpp.exitMessage(); return {}; } diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index f793d93de6e..bccd5a4c19d 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -93,7 +93,7 @@ static Macros dumpPredefinedMacros(const FilePath &compiler, const Environment & cpp.setCommand({compiler, {compilerTargetFlag(abi), "-dM", "-E", fakeIn.fileName()}}); cpp.runBlocking(); - if (cpp.result() != QtcProcess::FinishedWithSuccess) { + if (cpp.result() != ProcessResult::FinishedWithSuccess) { qWarning() << cpp.exitMessage(); return {}; } @@ -114,7 +114,7 @@ static HeaderPaths dumpHeaderPaths(const FilePath &compiler, const Environment & cpp.setCommand({compiler, {compilerTargetFlag(abi), "--print-search-dirs"}}); cpp.runBlocking(); - if (cpp.result() != QtcProcess::FinishedWithSuccess) { + if (cpp.result() != ProcessResult::FinishedWithSuccess) { qWarning() << cpp.exitMessage(); return {}; } diff --git a/src/plugins/bazaar/bazaarclient.cpp b/src/plugins/bazaar/bazaarclient.cpp index c0804930b7e..3265905531f 100644 --- a/src/plugins/bazaar/bazaarclient.cpp +++ b/src/plugins/bazaar/bazaarclient.cpp @@ -31,6 +31,7 @@ #include <vcsbase/vcsbaseeditorconfig.h> #include <utils/hostosinfo.h> +#include <utils/qtcprocess.h> #include <QDir> #include <QFileInfo> @@ -153,7 +154,7 @@ bool BazaarClient::synchronousUncommit(const FilePath &workingDir, QtcProcess proc; vcsFullySynchronousExec(proc, workingDir, args); VcsOutputWindow::append(proc.stdOut()); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } void BazaarClient::commit(const FilePath &repositoryRoot, const QStringList &files, @@ -191,7 +192,7 @@ bool BazaarClient::managesFile(const FilePath &workingDirectory, const QString & QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, args); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return false; return proc.rawStdOut().startsWith("unknown"); } @@ -231,8 +232,8 @@ ExitCodeInterpreter BazaarClient::exitCodeInterpreter(VcsCommandTag cmd) const { if (cmd == DiffCommand) { return [](int code) { - return (code < 0 || code > 2) ? QtcProcess::FinishedWithError - : QtcProcess::FinishedWithSuccess; + return (code < 0 || code > 2) ? ProcessResult::FinishedWithError + : ProcessResult::FinishedWithSuccess; }; } return {}; diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp index 74202d90413..185f0e7191a 100644 --- a/src/plugins/bazaar/bazaarplugin.cpp +++ b/src/plugins/bazaar/bazaarplugin.cpp @@ -50,6 +50,8 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/locator/commandlocator.h> +#include <utils/commandline.h> +#include <utils/environment.h> #include <utils/parameteraction.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> diff --git a/src/plugins/beautifier/abstractsettings.cpp b/src/plugins/beautifier/abstractsettings.cpp index 28d8ac6b049..d691984d62f 100644 --- a/src/plugins/beautifier/abstractsettings.cpp +++ b/src/plugins/beautifier/abstractsettings.cpp @@ -33,7 +33,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/genericconstants.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QFile> #include <QFileInfo> diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp index 429fc80ada2..ed6d0b91590 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp @@ -41,6 +41,8 @@ #include <QRegularExpression> #include <QXmlStreamWriter> +using namespace Utils; + namespace Beautifier { namespace Internal { @@ -81,12 +83,12 @@ static int parseVersion(const QString &text) return 0; } -static int updateVersionHelper(const Utils::FilePath &command) +static int updateVersionHelper(const FilePath &command) { - Utils::QtcProcess process; + QtcProcess process; process.setCommand({command, {"--version"}}); process.runBlocking(); - if (process.result() != Utils::QtcProcess::FinishedWithSuccess) + if (process.result() != ProcessResult::FinishedWithSuccess) return 0; // Astyle prints the version on stdout or stderr, depending on platform @@ -101,7 +103,7 @@ void ArtisticStyleSettings::updateVersion() if (m_versionFuture.isRunning()) m_versionFuture.cancel(); - m_versionFuture = Utils::runAsync(updateVersionHelper, command()); + m_versionFuture = runAsync(updateVersionHelper, command()); m_versionWatcher.setFuture(m_versionFuture); } @@ -184,7 +186,7 @@ void ArtisticStyleSettings::createDocumentationFile() const process.setTimeoutS(2); process.setCommand({command(), {"-h"}}); process.runBlocking(); - if (process.result() != Utils::QtcProcess::FinishedWithSuccess) + if (process.result() != ProcessResult::FinishedWithSuccess) return; QFile file(documentationFilePath()); diff --git a/src/plugins/beautifier/beautifierplugin.cpp b/src/plugins/beautifier/beautifierplugin.cpp index aa2f8e4532e..c1abcdd7bcf 100644 --- a/src/plugins/beautifier/beautifierplugin.cpp +++ b/src/plugins/beautifier/beautifierplugin.cpp @@ -51,7 +51,7 @@ #include <texteditor/texteditorconstants.h> #include <utils/algorithm.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/runextensions.h> diff --git a/src/plugins/beautifier/generalsettings.cpp b/src/plugins/beautifier/generalsettings.cpp index df16001aa0a..b90ab0a1337 100644 --- a/src/plugins/beautifier/generalsettings.cpp +++ b/src/plugins/beautifier/generalsettings.cpp @@ -30,7 +30,7 @@ #include <coreplugin/icore.h> #include <utils/algorithm.h> #include <utils/genericconstants.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> namespace Beautifier { namespace Internal { diff --git a/src/plugins/beautifier/generalsettings.h b/src/plugins/beautifier/generalsettings.h index 254a13c7c2a..ae3d49734d2 100644 --- a/src/plugins/beautifier/generalsettings.h +++ b/src/plugins/beautifier/generalsettings.h @@ -25,7 +25,7 @@ #pragma once -#include <utils/mimetypes/mimetype.h> +#include <utils/mimeutils.h> #include <QList> diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp index bb5b234bfbb..78b0529fa4e 100644 --- a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp @@ -39,6 +39,8 @@ #include <QRegularExpression> #include <QXmlStreamWriter> +using namespace Utils; + namespace Beautifier { namespace Internal { @@ -54,7 +56,7 @@ const char SETTINGS_NAME[] = "uncrustify"; UncrustifySettings::UncrustifySettings() : AbstractSettings(SETTINGS_NAME, ".cfg") { - connect(&m_versionProcess, &Utils::QtcProcess::finished, + connect(&m_versionProcess, &QtcProcess::finished, this, &UncrustifySettings::parseVersionProcessResult); setCommand("uncrustify"); @@ -88,12 +90,12 @@ void UncrustifySettings::setUseHomeFile(bool useHomeFile) m_settings.insert(USE_HOME_FILE, QVariant(useHomeFile)); } -Utils::FilePath UncrustifySettings::specificConfigFile() const +FilePath UncrustifySettings::specificConfigFile() const { - return Utils::FilePath::fromString(m_settings.value(SPECIFIC_CONFIG_FILE_PATH).toString()); + return FilePath::fromString(m_settings.value(SPECIFIC_CONFIG_FILE_PATH).toString()); } -void UncrustifySettings::setSpecificConfigFile(const Utils::FilePath &filePath) +void UncrustifySettings::setSpecificConfigFile(const FilePath &filePath) { m_settings.insert(SPECIFIC_CONFIG_FILE_PATH, QVariant(filePath.toString())); } @@ -148,11 +150,11 @@ QString UncrustifySettings::documentationFilePath() const void UncrustifySettings::createDocumentationFile() const { - Utils::QtcProcess process; + QtcProcess process; process.setTimeoutS(2); process.setCommand({command(), {"--show-config"}}); process.runBlocking(); - if (process.result() != Utils::QtcProcess::FinishedWithSuccess) + if (process.result() != ProcessResult::FinishedWithSuccess) return; QFile file(documentationFilePath()); diff --git a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp index 3d54227a514..7a782e09472 100644 --- a/src/plugins/boot2qt/device-detection/qdbwatcher.cpp +++ b/src/plugins/boot2qt/device-detection/qdbwatcher.cpp @@ -62,17 +62,10 @@ void QdbWatcher::start(RequestType requestType) void QdbWatcher::startPrivate() { - void (QLocalSocket::*LocalSocketErrorFunction)(QLocalSocket::LocalSocketError) -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - = &QLocalSocket::error; -#else - = &QLocalSocket::errorOccurred; -#endif - m_socket = std::unique_ptr<QLocalSocket>(new QLocalSocket()); connect(m_socket.get(), &QLocalSocket::connected, this, &QdbWatcher::handleWatchConnection); - connect(m_socket.get(), LocalSocketErrorFunction, + connect(m_socket.get(), &QLocalSocket::errorOccurred, this, &QdbWatcher::handleWatchError); m_socket->connectToServer(qdbSocketName); } diff --git a/src/plugins/boot2qt/qdbdevice.cpp b/src/plugins/boot2qt/qdbdevice.cpp index 7b858747e50..9c55aa35176 100644 --- a/src/plugins/boot2qt/qdbdevice.cpp +++ b/src/plugins/boot2qt/qdbdevice.cpp @@ -66,8 +66,11 @@ public: { ProjectExplorer::Runnable r; r.command = {Constants::AppcontrollerFilepath, {"--stop"}}; + r.device = device(); - (new ApplicationLauncher(this))->start(r, device()); + auto launcher = new ApplicationLauncher(this); + launcher->setRunnable(r); + launcher->start(); } }; @@ -79,9 +82,9 @@ public: { connect(&m_appRunner, &ApplicationLauncher::appendMessage, this, &DeviceApplicationObserver::handleAppendMessage); - connect(&m_appRunner, &ApplicationLauncher::error, this, + connect(&m_appRunner, &ApplicationLauncher::errorOccurred, this, [this] { m_error = m_appRunner.errorString(); }); - connect(&m_appRunner, &ApplicationLauncher::processExited, this, + connect(&m_appRunner, &ApplicationLauncher::finished, this, &DeviceApplicationObserver::handleFinished); QTC_ASSERT(device, return); @@ -89,7 +92,9 @@ public: Runnable r; r.command = command; - m_appRunner.start(r, device); + r.device = device; + m_appRunner.setRunnable(r); + m_appRunner.start(); showMessage(QdbDevice::tr("Starting command \"%1\" on device \"%2\".") .arg(command.toUserOutput(), m_deviceName)); } @@ -103,13 +108,15 @@ private: m_stderr += data; } - void handleFinished(int exitCode, QProcess::ExitStatus exitStatus) + void handleFinished() { - Q_UNUSED(exitCode) // FIXME: Needed in a post-adb world? // adb does not forward exit codes and all stderr goes to stdout. - const bool failure = exitStatus == QProcess::CrashExit || m_stdout.contains("fail") - || m_stdout.contains("error") || m_stdout.contains("not found"); + const bool failure = m_appRunner.exitStatus() == QProcess::CrashExit + || m_stdout.contains("fail") + || m_stdout.contains("error") + || m_stdout.contains("not found"); + if (failure) { QString errorString; if (!m_error.isEmpty()) { @@ -160,7 +167,7 @@ ProjectExplorer::IDeviceWidget *QdbDevice::createWidget() return w; } -ProjectExplorer::DeviceProcess *QdbDevice::createProcess(QObject *parent) const +QtcProcess *QdbDevice::createProcess(QObject *parent) const { return new QdbDeviceProcess(sharedFromThis(), parent); } diff --git a/src/plugins/boot2qt/qdbdevice.h b/src/plugins/boot2qt/qdbdevice.h index 191a410633d..3148cf6fe8a 100644 --- a/src/plugins/boot2qt/qdbdevice.h +++ b/src/plugins/boot2qt/qdbdevice.h @@ -44,7 +44,7 @@ public: ProjectExplorer::IDeviceWidget *createWidget() final; - ProjectExplorer::DeviceProcess *createProcess(QObject *parent) const final; + Utils::QtcProcess *createProcess(QObject *parent) const final; void setSerialNumber(const QString &serial); QString serialNumber() const; diff --git a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp index 153c21be453..39c16d6723f 100644 --- a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp +++ b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp @@ -55,9 +55,9 @@ public: { setId("QdbDebuggeeRunner"); - connect(&m_launcher, &ApplicationLauncher::processStarted, + connect(&m_launcher, &ApplicationLauncher::started, this, &RunWorker::reportStarted); - connect(&m_launcher, &ApplicationLauncher::processExited, + connect(&m_launcher, &ApplicationLauncher::finished, this, &RunWorker::reportStopped); connect(&m_launcher, &ApplicationLauncher::appendMessage, this, &RunWorker::appendMessage); @@ -117,8 +117,10 @@ public: r.command.setArguments(args); r.command.setExecutable(FilePath::fromString(Constants::AppcontrollerFilepath)); + r.device = device(); - m_launcher.start(r, device()); + m_launcher.setRunnable(r); + m_launcher.start(); } void stop() override { m_launcher.stop(); } diff --git a/src/plugins/boot2qt/qdbmakedefaultappservice.cpp b/src/plugins/boot2qt/qdbmakedefaultappservice.cpp index 20192d6aad6..2e3c71197d8 100644 --- a/src/plugins/boot2qt/qdbmakedefaultappservice.cpp +++ b/src/plugins/boot2qt/qdbmakedefaultappservice.cpp @@ -66,8 +66,9 @@ void QdbMakeDefaultAppService::handleStdErr() emit stdErrData(QString::fromUtf8(d->processRunner->readAllStandardError())); } -void QdbMakeDefaultAppService::handleProcessFinished(const QString &error) +void QdbMakeDefaultAppService::handleProcessFinished() { + const QString error = d->processRunner->errorString(); if (!error.isEmpty()) { emit errorMessage(tr("Remote process failed: %1").arg(error)); stopDeployment(); @@ -88,7 +89,7 @@ void QdbMakeDefaultAppService::handleProcessFinished(const QString &error) void QdbMakeDefaultAppService::doDeploy() { d->processRunner = new QSsh::SshRemoteProcessRunner; - connect(d->processRunner, &QSsh::SshRemoteProcessRunner::processClosed, + connect(d->processRunner, &QSsh::SshRemoteProcessRunner::finished, this, &QdbMakeDefaultAppService::handleProcessFinished); connect(d->processRunner, &QSsh::SshRemoteProcessRunner::readyReadStandardError, this, &QdbMakeDefaultAppService::handleStdErr); diff --git a/src/plugins/boot2qt/qdbmakedefaultappservice.h b/src/plugins/boot2qt/qdbmakedefaultappservice.h index cab4b4cb331..f66ee0fc808 100644 --- a/src/plugins/boot2qt/qdbmakedefaultappservice.h +++ b/src/plugins/boot2qt/qdbmakedefaultappservice.h @@ -42,7 +42,7 @@ public: private: void handleStdErr(); - void handleProcessFinished(const QString &error); + void handleProcessFinished(); bool isDeploymentNecessary() const final { return true; } diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp index b9b8aa237d7..bb2902dc68c 100644 --- a/src/plugins/boot2qt/qdbplugin.cpp +++ b/src/plugins/boot2qt/qdbplugin.cpp @@ -147,7 +147,8 @@ public: // FIXME: Spaces! r.command.setArguments(r.command.executable().toString() + ' ' + r.command.arguments()); r.command.setExecutable(FilePath::fromString(Constants::AppcontrollerFilepath)); - doStart(r, runControl->device()); + r.device = runControl->device(); + doStart(r); }); } }; diff --git a/src/plugins/boot2qt/qdbstopapplicationservice.cpp b/src/plugins/boot2qt/qdbstopapplicationservice.cpp index 018e6bdbb70..ce9a22308cc 100644 --- a/src/plugins/boot2qt/qdbstopapplicationservice.cpp +++ b/src/plugins/boot2qt/qdbstopapplicationservice.cpp @@ -57,11 +57,10 @@ QdbStopApplicationService::~QdbStopApplicationService() delete d; } -void QdbStopApplicationService::handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) +void QdbStopApplicationService::handleProcessFinished() { - Q_UNUSED(exitCode) - const auto failureMessage = tr("Could not check and possibly stop running application."); - if (exitStatus == QProcess::CrashExit) { + const QString failureMessage = tr("Could not check and possibly stop running application."); + if (d->applicationLauncher.exitStatus() == QProcess::CrashExit) { emit errorMessage(failureMessage); stopDeployment(); return; @@ -89,9 +88,9 @@ void QdbStopApplicationService::handleAppendMessage(const QString &message, Util void QdbStopApplicationService::doDeploy() { - connect(&d->applicationLauncher, &ProjectExplorer::ApplicationLauncher::error, + connect(&d->applicationLauncher, &ProjectExplorer::ApplicationLauncher::errorOccurred, this, [this] { emit stdErrData(d->applicationLauncher.errorString()); }); - connect(&d->applicationLauncher, &ProjectExplorer::ApplicationLauncher::processExited, + connect(&d->applicationLauncher, &ProjectExplorer::ApplicationLauncher::finished, this, &QdbStopApplicationService::handleProcessFinished); connect(&d->applicationLauncher, &ProjectExplorer::ApplicationLauncher::appendMessage, this, &QdbStopApplicationService::handleAppendMessage); @@ -99,9 +98,10 @@ void QdbStopApplicationService::doDeploy() ProjectExplorer::Runnable runnable; runnable.command = {Constants::AppcontrollerFilepath, {"--stop"}}; runnable.workingDirectory = "/usr/bin"; + runnable.device = ProjectExplorer::DeviceKitAspect::device(target()->kit()); - d->applicationLauncher.start(runnable, - ProjectExplorer::DeviceKitAspect::device(target()->kit())); + d->applicationLauncher.setRunnable(runnable); + d->applicationLauncher.start(); } void QdbStopApplicationService::stopDeployment() diff --git a/src/plugins/boot2qt/qdbstopapplicationservice.h b/src/plugins/boot2qt/qdbstopapplicationservice.h index f9b35681823..3db114a3188 100644 --- a/src/plugins/boot2qt/qdbstopapplicationservice.h +++ b/src/plugins/boot2qt/qdbstopapplicationservice.h @@ -28,8 +28,6 @@ #include <remotelinux/abstractremotelinuxdeployservice.h> #include <utils/outputformat.h> -#include <QProcess> - namespace Qdb { namespace Internal { @@ -43,7 +41,7 @@ public: ~QdbStopApplicationService(); private: - void handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); + void handleProcessFinished(); void handleAppendMessage(const QString &message, Utils::OutputFormat format); bool isDeploymentNecessary() const final { return true; } diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index ac8f686910a..db70275eade 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -50,7 +50,7 @@ #include <clangsupport/filecontainer.h> #include <utils/algorithm.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/optional.h> #include <utils/porting.h> #include <utils/qtcassert.h> diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 894f22aaa92..7525fbf160d 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -51,6 +51,7 @@ #include <cppeditor/cppeditorwidget.h> #include <cppeditor/cppfindreferences.h> #include <cppeditor/cppmodelmanager.h> +#include <cppeditor/cpprefactoringchanges.h> #include <cppeditor/cpptoolsreuse.h> #include <cppeditor/cppvirtualfunctionassistprovider.h> #include <cppeditor/cppvirtualfunctionproposalitem.h> @@ -69,6 +70,7 @@ #include <texteditor/texteditorsettings.h> #include <texteditor/texteditor.h> #include <utils/algorithm.h> +#include <utils/fileutils.h> #include <utils/itemviews.h> #include <utils/runextensions.h> #include <utils/treemodel.h> @@ -85,6 +87,7 @@ #include <QPair> #include <QPointer> #include <QRegularExpression> +#include <QStandardPaths> #include <QVBoxLayout> #include <QWidget> #include <QtConcurrent> @@ -166,7 +169,7 @@ public: bool detailIs(const QString &s) const { - return detail() && detail().value() == s; + return detail() && *detail() == s; } bool isMemberFunctionCall() const @@ -597,6 +600,24 @@ public: : Request("textDocument/symbolInfo", params) {} }; +void setupClangdConfigFile() +{ + const Utils::FilePath baseDir = Utils::FilePath::fromString( + QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)) / "clangd"; + baseDir.ensureWritableDir(); + const Utils::FilePath targetConfigFile = baseDir / "config.yaml"; + Utils::FileReader configReader; + const QByteArray firstLine = "# This file was generated by Qt Creator and will be overwritten " + "unless you remove this line."; + if (!configReader.fetch(targetConfigFile) || configReader.data().startsWith(firstLine)) { + Utils::FileSaver saver(targetConfigFile); + saver.write(firstLine + '\n'); + saver.write("Hover:\n"); + saver.write(" ShowAKA: Yes\n"); + QTC_CHECK(saver.finalize()); + } +} + static BaseClientInterface *clientInterface(Project *project, const Utils::FilePath &jsonDbDir) { QString indexingOption = "--background-index"; @@ -1260,6 +1281,79 @@ private: ClangdClient * const m_client; }; +class ClangdQuickFixProcessor : public LanguageClientQuickFixAssistProcessor +{ +public: + ClangdQuickFixProcessor(LanguageClient::Client *client) + : LanguageClientQuickFixAssistProcessor(client) + { + } + +private: + IAssistProposal *perform(const AssistInterface *interface) override + { + m_interface = interface; + + // Step 1: Collect clangd code actions asynchronously + LanguageClientQuickFixAssistProcessor::perform(interface); + + // Step 2: Collect built-in quickfixes synchronously + m_builtinOps = CppEditor::quickFixOperations(interface); + + return nullptr; + } + + void handleProposalReady(const QuickFixOperations &ops) override + { + // Step 3: Merge the results upon callback from clangd. + for (const auto &op : ops) + op->setDescription("clangd: " + op->description()); + setAsyncProposalAvailable(GenericProposal::createProposal(m_interface, ops + m_builtinOps)); + } + + QuickFixOperations m_builtinOps; + const AssistInterface *m_interface = nullptr; +}; + +class ClangdQuickFixProvider : public LanguageClientQuickFixProvider +{ +public: + ClangdQuickFixProvider(ClangdClient *client) : LanguageClientQuickFixProvider(client) {}; + +private: + IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override + { + return new ClangdQuickFixProcessor(client()); + } +}; + +static void addToCompilationDb(QJsonObject &cdb, + const CppEditor::ClangDiagnosticConfig &projectWarnings, + const QStringList &projectOptions, + const CppEditor::ProjectPart::ConstPtr &projectPart, + const Utils::FilePath &workingDir, + const Utils::FilePath &sourceFile) +{ + // TODO: Do we really need to re-calculate the project part options per source file? + QStringList args = createClangOptions(*projectPart, sourceFile.toString(), + projectWarnings, projectOptions); + + // TODO: clangd seems to apply some heuristics depending on what we put here. + // Should we make use of them or keep using our own? + args.prepend("clang"); + + args.append(sourceFile.toUserOutput()); + QJsonObject value; + value.insert("workingDirectory", workingDir.toString()); + value.insert("compilationCommand", QJsonArray::fromStringList(args)); + cdb.insert(sourceFile.toUserOutput(), value); +} + +static void addCompilationDb(QJsonObject &parentObject, const QJsonObject &cdb) +{ + parentObject.insert("compilationDatabaseChanges", cdb); +} + ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir) : Client(clientInterface(project, jsonDbDir)), d(new Private(this, project)) { @@ -1271,6 +1365,7 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir) setActivateDocumentAutomatically(true); setLogTarget(LogTarget::Console); setCompletionAssistProvider(new ClangdCompletionAssistProvider(this)); + setQuickFixAssistProvider(new ClangdQuickFixProvider(this)); if (!project) { QJsonObject initOptions; const QStringList clangOptions = createClangOptions( @@ -1457,8 +1552,18 @@ void ClangdClient::handleDiagnostics(const PublishDiagnosticsParams ¶ms) return; for (const Diagnostic &diagnostic : params.diagnostics()) { const ClangdDiagnostic clangdDiagnostic(diagnostic); - for (const CodeAction &action : clangdDiagnostic.codeActions().value_or(QList<CodeAction>{})) - LanguageClient::updateCodeActionRefactoringMarker(this, action, uri); + const auto codeActions = clangdDiagnostic.codeActions(); + if (codeActions && !codeActions->isEmpty()) { + for (const CodeAction &action : *codeActions) + LanguageClient::updateCodeActionRefactoringMarker(this, action, uri); + } else { + // We know that there's only one kind of diagnostic for which clangd has + // a quickfix tweak, so let's not be wasteful. + const Diagnostic::Code code = diagnostic.code().value_or(Diagnostic::Code()); + const QString * const codeString = Utils::get_if<QString>(&code); + if (codeString && *codeString == "-Wswitch") + requestCodeActions(uri, diagnostic); + } } } @@ -1491,6 +1596,12 @@ const LanguageClient::Client::CustomInspectorTabs ClangdClient::createCustomInsp return {std::make_pair(new MemoryUsageWidget(this), tr("Memory Usage"))}; } +RefactoringChangesData *ClangdClient::createRefactoringChangesBackend() const +{ + return new CppEditor::CppRefactoringChangesData( + CppEditor::CppModelManager::instance()->snapshot()); +} + QVersionNumber ClangdClient::versionNumber() const { if (d->versionNumber) @@ -1651,18 +1762,11 @@ void ClangdClient::updateParserConfig(const Utils::FilePath &filePath, ->projectPartForId(config.preferredProjectPartId); if (!projectPart) return; - const CppEditor::ClangDiagnosticConfig projectWarnings = warningsConfigForProject(project()); - const QStringList projectOptions = optionsForProject(project()); QJsonObject cdbChanges; - QStringList args = createClangOptions(*projectPart, filePath.toString(), projectWarnings, - projectOptions); - args.prepend("clang"); - args.append(filePath.toString()); - QJsonObject value; - value.insert("workingDirectory", filePath.parentDir().toString()); - value.insert("compilationCommand", QJsonArray::fromStringList(args)); - cdbChanges.insert(filePath.toUserOutput(), value); - const QJsonObject settings({qMakePair(QString("compilationDatabaseChanges"), cdbChanges)}); + addToCompilationDb(cdbChanges, warningsConfigForProject(project()), + optionsForProject(project()), projectPart, filePath.parentDir(), filePath); + QJsonObject settings; + addCompilationDb(settings, cdbChanges); DidChangeConfigurationParams configChangeParams; configChangeParams.setSettings(settings); sendContent(DidChangeConfigurationNotification(configChangeParams)); diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index cc4b6aa71e9..7c9135a0c57 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -43,6 +43,8 @@ namespace TextEditor { class BaseTextEditor; } namespace ClangCodeModel { namespace Internal { +void setupClangdConfigFile(); + class ClangdClient : public LanguageClient::Client { Q_OBJECT @@ -109,6 +111,7 @@ private: QTextCursor adjustedCursorForHighlighting(const QTextCursor &cursor, TextEditor::TextDocument *doc) override; const CustomInspectorTabs createCustomInspectorTabs() override; + TextEditor::RefactoringChangesData *createRefactoringChangesBackend() const override; class Private; class FollowSymbolData; diff --git a/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp b/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp index 5ba64e95773..db7e279af81 100644 --- a/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp +++ b/src/plugins/clangcodemodel/clangdiagnosticmanager.cpp @@ -392,7 +392,6 @@ void ClangDiagnosticManager::invalidateDiagnostics() for (ClangTextMark *textMark : m_clangTextMarks) { textMark->setColor(::Utils::Theme::Color::IconsDisabledColor); textMark->updateIcon(/*valid=*/ false); - textMark->updateMarker(); } } diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 94902075e91..687d13c4476 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -114,6 +114,7 @@ ClangModelManagerSupport::ClangModelManagerSupport() watchForExternalChanges(); watchForInternalChanges(); + setupClangdConfigFile(); cppModelManager()->setCurrentDocumentFilter(std::make_unique<ClangdCurrentDocumentFilter>()); cppModelManager()->setLocatorFilter(std::make_unique<ClangGlobalSymbolFilter>()); cppModelManager()->setClassesFilter(std::make_unique<ClangClassesFilter>()); diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index 9a207a82d48..68ca3b9a8db 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -60,14 +60,14 @@ static QString finishedWithBadExitCode(const QString &name, int exitCode) } ClangToolRunner::ClangToolRunner(QObject *parent) - : QObject(parent), m_process(new Utils::QtcProcess) + : QObject(parent), m_process(new QtcProcess) {} ClangToolRunner::~ClangToolRunner() { if (m_process->state() != QProcess::NotRunning) { // asking politly to terminate costs ~300 ms on windows so skip the courtasy and direct kill the process - if (Utils::HostOsInfo::isWindowsHost()) { + if (HostOsInfo::isWindowsHost()) { m_process->kill(); m_process->waitForFinished(100); } else { @@ -147,10 +147,10 @@ bool ClangToolRunner::run(const QString &fileToAnalyze, const QStringList &compi void ClangToolRunner::onProcessFinished() { - if (m_process->result() == QtcProcess::FinishedWithSuccess) { + if (m_process->result() == ProcessResult::FinishedWithSuccess) { qCDebug(LOG).noquote() << "Output:\n" << m_process->stdOut(); emit finishedWithSuccess(m_fileToAnalyze); - } else if (m_process->result() == QtcProcess::FinishedWithError) { + } else if (m_process->result() == ProcessResult::FinishedWithError) { emit finishedWithFailure(finishedWithBadExitCode(m_name, m_process->exitCode()), commandlineAndOutput()); } else { // == QProcess::CrashExit diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h index 45ed11387f4..38bac4378e5 100644 --- a/src/plugins/clangtools/clangtoolrunner.h +++ b/src/plugins/clangtools/clangtoolrunner.h @@ -27,10 +27,14 @@ #include "clangtoolslogfilereader.h" -#include <utils/qtcprocess.h> +#include <utils/commandline.h> + +#include <QProcess> #include <memory> +namespace Utils { class QtcProcess; } + namespace ClangTools { namespace Internal { diff --git a/src/plugins/clangtools/clangtoolsplugin.cpp b/src/plugins/clangtools/clangtoolsplugin.cpp index 82806284e70..310d64a6baa 100644 --- a/src/plugins/clangtools/clangtoolsplugin.cpp +++ b/src/plugins/clangtools/clangtoolsplugin.cpp @@ -38,8 +38,7 @@ #include "clangtoolsunittests.h" #endif -#include <utils/mimetypes/mimedatabase.h> -#include <utils/mimetypes/mimetype.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <coreplugin/actionmanager/actioncontainer.h> diff --git a/src/plugins/clangtools/diagnosticmark.cpp b/src/plugins/clangtools/diagnosticmark.cpp index 0e29a1c7313..632781c7127 100644 --- a/src/plugins/clangtools/diagnosticmark.cpp +++ b/src/plugins/clangtools/diagnosticmark.cpp @@ -89,7 +89,6 @@ void DiagnosticMark::disable() else setIcon(Utils::Icons::CODEMODEL_DISABLED_WARNING.icon()); setColor(Utils::Theme::Color::IconsDisabledColor); - updateMarker(); } bool DiagnosticMark::enabled() const diff --git a/src/plugins/clangtools/executableinfo.cpp b/src/plugins/clangtools/executableinfo.cpp index 45b2ea8e2cc..9277e54df9f 100644 --- a/src/plugins/clangtools/executableinfo.cpp +++ b/src/plugins/clangtools/executableinfo.cpp @@ -55,9 +55,9 @@ static QString runExecutable(const Utils::CommandLine &commandLine, QueryFailMod cpp.setCommand(commandLine); cpp.runBlocking(); - if (cpp.result() != QtcProcess::FinishedWithSuccess + if (cpp.result() != ProcessResult::FinishedWithSuccess && (queryFailMode == QueryFailMode::Noisy - || cpp.result() != QtcProcess::FinishedWithError)) { + || cpp.result() != ProcessResult::FinishedWithError)) { Core::MessageManager::writeFlashing(cpp.exitMessage()); Core::MessageManager::writeFlashing(QString::fromUtf8(cpp.allRawOutput())); return {}; diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index 9f8afc89aba..bd6786ed49c 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -1673,7 +1673,7 @@ ClearCasePluginPrivate::runCleartool(const FilePath &workingDir, command.setCodec(outputCodec); command.runCommand(proc, {FilePath::fromString(executable), arguments}); - response.error = proc.result() != QtcProcess::FinishedWithSuccess; + response.error = proc.result() != ProcessResult::FinishedWithSuccess; if (response.error) response.message = proc.exitMessage(); response.stdErr = proc.stdErr(); @@ -2354,8 +2354,8 @@ QString ClearCasePluginPrivate::runExtDiff(const FilePath &workingDir, const QSt process.setWorkingDirectory(workingDir); process.setCodec(outputCodec ? outputCodec : QTextCodec::codecForName("UTF-8")); process.setCommand(diff); - process.runBlocking(QtcProcess::WithEventLoop); - if (process.result() != QtcProcess::FinishedWithSuccess) + process.runBlocking(EventLoopMode::On); + if (process.result() != ProcessResult::FinishedWithSuccess) return QString(); return process.allOutput(); } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index fe416e01bca..817098b8f65 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -64,6 +64,7 @@ #include <utils/algorithm.h> #include <utils/categorysortfiltermodel.h> #include <utils/checkablemessagebox.h> +#include <utils/commandline.h> #include <utils/detailswidget.h> #include <utils/headerviewstretcher.h> #include <utils/infolabel.h> diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index c4d2127573b..944f3116ea8 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -59,7 +59,7 @@ #include <utils/checkablemessagebox.h> #include <utils/fileutils.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimetype.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/runextensions.h> @@ -517,8 +517,8 @@ void CMakeBuildSystem::combineScanAndParse(bool restoredFromBackup) CMakeProject::IssueType::Error, tr("<b>CMake configuration failed<b>" "<p>The backup of the previous configuration has been restored.</p>" - "<p>Have a look at the Issues pane or in the \"Projects > Build\" settings " - "for more information about the failure.</p")); + "<p>Issues and \"Projects > Build\" settings " + "show more information about the failure.</p")); m_reader.resetData(); @@ -534,8 +534,8 @@ void CMakeBuildSystem::combineScanAndParse(bool restoredFromBackup) project()->addIssue( CMakeProject::IssueType::Error, tr("<b>Failed to load project<b>" - "<p>Have a look at the Issues pane or in the \"Projects > Build\" settings " - "for more information about the failure.</p")); + "<p>Issues and \"Projects > Build\" settings " + "show more information about the failure.</p")); } } } diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp index 90699b86213..9da43128dce 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp @@ -32,6 +32,7 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/taskhub.h> +#include <utils/qtcprocess.h> #include <utils/stringutils.h> using namespace Utils; diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.h b/src/plugins/cmakeprojectmanager/cmakeprocess.h index 946687a4748..49e6596cf4c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.h +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.h @@ -28,16 +28,18 @@ #include "builddirparameters.h" #include <utils/outputformatter.h> -#include <utils/qtcprocess.h> #include <QElapsedTimer> #include <QFutureInterface> #include <QObject> +#include <QProcess> #include <QStringList> #include <QTimer> #include <memory> +namespace Utils { class QtcProcess; } + namespace CMakeProjectManager { namespace Internal { diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index add008f756d..12939e94dad 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -42,6 +42,7 @@ #include <utils/algorithm.h> #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <utils/stringutils.h> #include <QDir> diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp index e3526737ea4..6e3f73358cb 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp @@ -275,19 +275,19 @@ TextEditor::Keywords CMakeTool::keywords() if (m_introspection->m_functions.isEmpty() && m_introspection->m_didRun) { QtcProcess proc; runCMake(proc, {"--help-command-list"}, 5); - if (proc.result() == QtcProcess::FinishedWithSuccess) + if (proc.result() == ProcessResult::FinishedWithSuccess) m_introspection->m_functions = proc.stdOut().split('\n'); runCMake(proc, {"--help-commands"}, 5); - if (proc.result() == QtcProcess::FinishedWithSuccess) + if (proc.result() == ProcessResult::FinishedWithSuccess) parseFunctionDetailsOutput(proc.stdOut()); runCMake(proc, {"--help-property-list"}, 5); - if (proc.result() == QtcProcess::FinishedWithSuccess) + if (proc.result() == ProcessResult::FinishedWithSuccess) m_introspection->m_variables = parseVariableOutput(proc.stdOut()); runCMake(proc, {"--help-variable-list"}, 5); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { m_introspection->m_variables.append(parseVariableOutput(proc.stdOut())); m_introspection->m_variables = Utils::filteredUnique(m_introspection->m_variables); Utils::sort(m_introspection->m_variables); @@ -517,7 +517,7 @@ void CMakeTool::fetchFromCapabilities() const QtcProcess cmake; runCMake(cmake, {"-E", "capabilities"}); - if (cmake.result() == QtcProcess::FinishedWithSuccess) { + if (cmake.result() == ProcessResult::FinishedWithSuccess) { m_introspection->m_didRun = true; parseFromCapabilities(cmake.stdOut()); } else { diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index d5836605f48..a381fecd69b 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -31,7 +31,7 @@ #include <cppeditor/cppeditorconstants.h> #include <utils/algorithm.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/utilsicons.h> diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp index 37216a48878..caeeddded9d 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp @@ -28,7 +28,7 @@ #include <coreplugin/progressmanager/progressmanager.h> #include <projectexplorer/task.h> #include <projectexplorer/treescanner.h> -#include <utils/mimetypes/mimetype.h> +#include <utils/mimeutils.h> #include <utils/runextensions.h> #include <QCryptographicHash> diff --git a/src/plugins/coreplugin/basefilewizardfactory.cpp b/src/plugins/coreplugin/basefilewizardfactory.cpp index 83d9721bce8..e272acd92a6 100644 --- a/src/plugins/coreplugin/basefilewizardfactory.cpp +++ b/src/plugins/coreplugin/basefilewizardfactory.cpp @@ -26,14 +26,14 @@ #include "basefilewizardfactory.h" #include "basefilewizard.h" +#include "dialogs/promptoverwritedialog.h" +#include "editormanager/editormanager.h" #include "icontext.h" #include "icore.h" #include "ifilewizardextension.h" -#include "editormanager/editormanager.h" -#include "dialogs/promptoverwritedialog.h" #include <extensionsystem/pluginmanager.h> #include <utils/filewizardpage.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> #include <utils/wizard.h> diff --git a/src/plugins/coreplugin/corejsextensions.cpp b/src/plugins/coreplugin/corejsextensions.cpp index 6b6a380108f..b73534b25b5 100644 --- a/src/plugins/coreplugin/corejsextensions.cpp +++ b/src/plugins/coreplugin/corejsextensions.cpp @@ -28,7 +28,7 @@ #include <app/app_version.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <QDir> diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 483c8c3c2b9..a6cccc3d09a 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -53,7 +53,7 @@ #include <utils/commandline.h> #include <utils/infobar.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/pathchooser.h> #include <utils/savefile.h> #include <utils/stringutils.h> diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.ui b/src/plugins/coreplugin/dialogs/externaltoolconfig.ui index a49ae83b892..57fe84c348e 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.ui +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.ui @@ -192,7 +192,7 @@ <property name="toolTip"> <string><html><head/><body> <p>What to do with the executable's standard output. -<ul><li>Ignore: Do nothing with it.</li><li>Show in pane: Show it in the general output pane.</li><li>Replace selection: Replace the current selection in the current document with it.</li></ul></p></body></html> +<ul><li>Ignore: Do nothing with it.</li><li>Show in General Messages.</li><li>Replace selection: Replace the current selection in the current document with it.</li></ul></p></body></html> </string> </property> <property name="text"> @@ -209,7 +209,7 @@ </item> <item> <property name="text"> - <string>Show in Pane</string> + <string>Show in General Messages</string> </property> </item> <item> @@ -225,7 +225,7 @@ <string><html><head><body> <p >What to do with the executable's standard error output.</p> <ul><li>Ignore: Do nothing with it.</li> -<li>Show in pane: Show it in the general output pane.</li> +<li>Show in General Messages.</li> <li>Replace selection: Replace the current selection in the current document with it.</li> </ul></body></html></string> </property> @@ -243,7 +243,7 @@ </item> <item> <property name="text"> - <string>Show in Pane</string> + <string>Show in General Messages</string> </property> </item> <item> diff --git a/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp b/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp index 8901ee109e0..18e13f35311 100644 --- a/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp +++ b/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp @@ -29,7 +29,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditorfactory.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QDateTime> #include <QDebug> diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp index 43ddad0ceef..640b2dffa94 100644 --- a/src/plugins/coreplugin/documentmanager.cpp +++ b/src/plugins/coreplugin/documentmanager.cpp @@ -50,7 +50,7 @@ #include <utils/fileutils.h> #include <utils/globalfilechangeblocker.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/optional.h> #include <utils/pathchooser.h> #include <utils/qtcassert.h> @@ -781,6 +781,17 @@ QString DocumentManager::fileDialogFilter(QString *selectedFilter) return allDocumentFactoryFiltersString(selectedFilter); } +#ifdef Q_OS_WIN +static struct {const char *source; const char *comment; } ALL_FILES_FILTER = QT_TRANSLATE_NOOP3("Core", "All Files (*.*)", "On Windows"); +#else +static struct {const char *source; const char *comment; } ALL_FILES_FILTER = QT_TRANSLATE_NOOP3("Core", "All Files (*)", "On Linux/macOS"); +#endif + +QString DocumentManager::allFilesFilterString() +{ + return QCoreApplication::translate("Core", ALL_FILES_FILTER.source, ALL_FILES_FILTER.comment); +} + QString DocumentManager::allDocumentFactoryFiltersString(QString *allFilesFilter = nullptr) { QSet<QString> uniqueFilters; @@ -803,7 +814,7 @@ QString DocumentManager::allDocumentFactoryFiltersString(QString *allFilesFilter QStringList filters = Utils::toList(uniqueFilters); filters.sort(); - const QString allFiles = Utils::allFilesFilterString(); + const QString allFiles = allFilesFilterString(); if (allFilesFilter) *allFilesFilter = allFiles; filters.prepend(allFiles); @@ -825,7 +836,7 @@ FilePath DocumentManager::getSaveFileName(const QString &title, const FilePath & // specified. Otherwise the suffix must be one available in the selected filter. If // the name already ends with such suffix nothing needs to be done. But if not, the // first one from the filter is appended. - if (selectedFilter && *selectedFilter != Utils::allFilesFilterString()) { + if (selectedFilter && *selectedFilter != allFilesFilterString()) { // Mime database creates filter strings like this: Anything here (*.foo *.bar) const QRegularExpression regExp(QLatin1String(".*\\s+\\((.*)\\)$")); QRegularExpressionMatchIterator matchIt = regExp.globalMatch(*selectedFilter); diff --git a/src/plugins/coreplugin/documentmanager.h b/src/plugins/coreplugin/documentmanager.h index 46667cf8ded..3f0e2bebe2f 100644 --- a/src/plugins/coreplugin/documentmanager.h +++ b/src/plugins/coreplugin/documentmanager.h @@ -145,6 +145,7 @@ public: static void setFileDialogFilter(const QString &filter); static QString fileDialogFilter(QString *selectedFilter = nullptr); + static QString allFilesFilterString(); signals: /* Used to notify e.g. the code model to update the given files. Does *not* diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 150d5d957b2..d40c3b049a6 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -72,8 +72,7 @@ #include <utils/infobar.h> #include <utils/link.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> -#include <utils/mimetypes/mimetype.h> +#include <utils/mimeutils.h> #include <utils/overridecursor.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory.cpp b/src/plugins/coreplugin/editormanager/ieditorfactory.cpp index a4e323b8d2f..af7627a0c20 100644 --- a/src/plugins/coreplugin/editormanager/ieditorfactory.cpp +++ b/src/plugins/coreplugin/editormanager/ieditorfactory.cpp @@ -28,7 +28,7 @@ #include "editormanager.h" #include <utils/algorithm.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <QFileInfo> diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory.h b/src/plugins/coreplugin/editormanager/ieditorfactory.h index 20787c29155..dfefb53c185 100644 --- a/src/plugins/coreplugin/editormanager/ieditorfactory.h +++ b/src/plugins/coreplugin/editormanager/ieditorfactory.h @@ -28,14 +28,16 @@ #include <coreplugin/core_global.h> #include <utils/id.h> -#include <utils/mimetypes/mimetype.h> #include <QObject> #include <QStringList> #include <functional> -namespace Utils { class FilePath; } +namespace Utils { +class FilePath; +class MimeType; +} namespace Core { diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory_p.h b/src/plugins/coreplugin/editormanager/ieditorfactory_p.h index 842772c2e58..9a05d2fdf6b 100644 --- a/src/plugins/coreplugin/editormanager/ieditorfactory_p.h +++ b/src/plugins/coreplugin/editormanager/ieditorfactory_p.h @@ -25,8 +25,7 @@ #pragma once -#include <utils/mimetypes/mimetype.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QHash> #include <QSet> diff --git a/src/plugins/coreplugin/editormanager/iexternaleditor.h b/src/plugins/coreplugin/editormanager/iexternaleditor.h index d37fc9fd12d..a3ed21c64a1 100644 --- a/src/plugins/coreplugin/editormanager/iexternaleditor.h +++ b/src/plugins/coreplugin/editormanager/iexternaleditor.h @@ -30,11 +30,13 @@ #include <coreplugin/core_global.h> #include <utils/id.h> -#include <utils/mimetypes/mimetype.h> #include <QObject> -namespace Utils { class FilePath; } +namespace Utils { +class FilePath; +class MimeType; +} namespace Core { diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp index 04ff8c9e0c5..6dffaa0310b 100644 --- a/src/plugins/coreplugin/externaltool.cpp +++ b/src/plugins/coreplugin/externaltool.cpp @@ -670,7 +670,7 @@ void ExternalToolRunner::run() void ExternalToolRunner::finished() { - if (m_process->result() == QtcProcess::FinishedWithSuccess + if (m_process->result() == ProcessResult::FinishedWithSuccess && (m_tool->outputHandling() == ExternalTool::ReplaceSelection || m_tool->errorHandling() == ExternalTool::ReplaceSelection)) { ExternalToolManager::emitReplaceSelectionRequested(m_processOutput); diff --git a/src/plugins/coreplugin/fileiconprovider.cpp b/src/plugins/coreplugin/fileiconprovider.cpp index e2149ce35a4..bf4109953cc 100644 --- a/src/plugins/coreplugin/fileiconprovider.cpp +++ b/src/plugins/coreplugin/fileiconprovider.cpp @@ -27,7 +27,7 @@ #include <utils/fileutils.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/optional.h> #include <utils/qtcassert.h> #include <utils/variant.h> diff --git a/src/plugins/coreplugin/iwelcomepage.h b/src/plugins/coreplugin/iwelcomepage.h index b95947ecb4b..4487e985588 100644 --- a/src/plugins/coreplugin/iwelcomepage.h +++ b/src/plugins/coreplugin/iwelcomepage.h @@ -28,7 +28,6 @@ #include "core_global.h" #include <utils/id.h> -#include <utils/porting.h> #include <QWidget> #include <QObject> diff --git a/src/plugins/coreplugin/locator/executefilter.cpp b/src/plugins/coreplugin/locator/executefilter.cpp index 327731f1e26..61df5e88553 100644 --- a/src/plugins/coreplugin/locator/executefilter.cpp +++ b/src/plugins/coreplugin/locator/executefilter.cpp @@ -29,6 +29,7 @@ #include <coreplugin/messagemanager.h> #include <utils/macroexpander.h> #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <QMessageBox> @@ -128,7 +129,7 @@ void ExecuteFilter::finished() QTC_ASSERT(m_process, return); const QString commandName = headCommand(); QString message; - if (m_process->result() == QtcProcess::FinishedWithSuccess) + if (m_process->result() == ProcessResult::FinishedWithSuccess) message = tr("Command \"%1\" finished.").arg(commandName); else message = tr("Command \"%1\" failed.").arg(commandName); diff --git a/src/plugins/coreplugin/locator/executefilter.h b/src/plugins/coreplugin/locator/executefilter.h index 85c84bbb6eb..6b5b84aaf8f 100644 --- a/src/plugins/coreplugin/locator/executefilter.h +++ b/src/plugins/coreplugin/locator/executefilter.h @@ -27,12 +27,14 @@ #include "ilocatorfilter.h" -#include <utils/qtcprocess.h> +#include <utils/commandline.h> #include <QQueue> #include <QStringList> #include <QTextCodec> +namespace Utils { class QtcProcess; } + namespace Core { namespace Internal { diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index 145244f7d85..f032f2cc42e 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -56,13 +56,13 @@ #include <coreplugin/actionmanager/actionmanager_p.h> #include <coreplugin/actionmanager/command.h> #include <coreplugin/dialogs/externaltoolconfig.h> -#include <coreplugin/iwizardfactory.h> #include <coreplugin/dialogs/shortcutsettings.h> #include <coreplugin/editormanager/documentmodel_p.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager_p.h> #include <coreplugin/editormanager/ieditor.h> #include <coreplugin/inavigationwidgetfactory.h> +#include <coreplugin/iwizardfactory.h> #include <coreplugin/progressmanager/progressmanager_p.h> #include <coreplugin/progressmanager/progressview.h> #include <coreplugin/settingsdatabase.h> @@ -70,11 +70,11 @@ #include <utils/algorithm.h> #include <utils/historycompleter.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> +#include <utils/stringutils.h> #include <utils/stylehelper.h> #include <utils/theme/theme.h> -#include <utils/stringutils.h> #include <utils/utilsicons.h> #include <QActionGroup> diff --git a/src/plugins/coreplugin/mimetypemagicdialog.cpp b/src/plugins/coreplugin/mimetypemagicdialog.cpp index 18bd8516732..633977638f1 100644 --- a/src/plugins/coreplugin/mimetypemagicdialog.cpp +++ b/src/plugins/coreplugin/mimetypemagicdialog.cpp @@ -37,11 +37,11 @@ using namespace Core; using namespace Internal; -static Utils::Internal::MimeMagicRule::Type typeValue(int i) +static Utils::MimeMagicRule::Type typeValue(int i) { - QTC_ASSERT(i < Utils::Internal::MimeMagicRule::Byte, - return Utils::Internal::MimeMagicRule::Invalid); - return Utils::Internal::MimeMagicRule::Type(i + 1/*0==invalid*/); + QTC_ASSERT(i < Utils::MimeMagicRule::Byte, + return Utils::MimeMagicRule::Invalid); + return Utils::MimeMagicRule::Type(i + 1/*0==invalid*/); } MimeTypeMagicDialog::MimeTypeMagicDialog(QWidget *parent) : @@ -96,7 +96,7 @@ void MimeTypeMagicDialog::applyRecommended(bool checked) void MimeTypeMagicDialog::validateAccept() { QString errorMessage; - Utils::Internal::MimeMagicRule rule = createRule(&errorMessage); + Utils::MimeMagicRule rule = createRule(&errorMessage); if (rule.isValid()) accept(); else @@ -130,12 +130,12 @@ bool MagicData::operator==(const MagicData &other) const Returns the mask, or an empty string if the mask is the default mask which is set by MimeMagicRule when setting an empty mask for string patterns. */ -QByteArray MagicData::normalizedMask(const Utils::Internal::MimeMagicRule &rule) +QByteArray MagicData::normalizedMask(const Utils::MimeMagicRule &rule) { // convert mask and see if it is the "default" one (which corresponds to "empty" mask) // see MimeMagicRule constructor QByteArray mask = rule.mask(); - if (rule.type() == Utils::Internal::MimeMagicRule::String) { + if (rule.type() == Utils::MimeMagicRule::String) { QByteArray actualMask = QByteArray::fromHex(QByteArray::fromRawData(mask.constData() + 2, mask.size() - 2)); if (actualMask.count(char(-1)) == actualMask.size()) { @@ -146,16 +146,16 @@ QByteArray MagicData::normalizedMask(const Utils::Internal::MimeMagicRule &rule) return mask; } -Utils::Internal::MimeMagicRule MimeTypeMagicDialog::createRule(QString *errorMessage) const +Utils::MimeMagicRule MimeTypeMagicDialog::createRule(QString *errorMessage) const { - Utils::Internal::MimeMagicRule::Type type = typeValue(ui.typeSelector->currentIndex()); - Utils::Internal::MimeMagicRule rule(type, + Utils::MimeMagicRule::Type type = typeValue(ui.typeSelector->currentIndex()); + Utils::MimeMagicRule rule(type, ui.valueLineEdit->text().toUtf8(), ui.startRangeSpinBox->value(), ui.endRangeSpinBox->value(), ui.maskLineEdit->text().toLatin1(), errorMessage); - if (type == Utils::Internal::MimeMagicRule::Invalid) { + if (type == Utils::MimeMagicRule::Invalid) { if (errorMessage) *errorMessage = tr("Internal error: Type is invalid"); } diff --git a/src/plugins/coreplugin/mimetypemagicdialog.h b/src/plugins/coreplugin/mimetypemagicdialog.h index 9480369e2eb..06868ff1d82 100644 --- a/src/plugins/coreplugin/mimetypemagicdialog.h +++ b/src/plugins/coreplugin/mimetypemagicdialog.h @@ -27,7 +27,7 @@ #include "ui_mimetypemagicdialog.h" -#include <utils/mimetypes/mimemagicrule_p.h> +#include <utils/mimeutils.h> namespace Core { namespace Internal { @@ -36,11 +36,11 @@ class MagicData { public: MagicData() - : m_rule(Utils::Internal::MimeMagicRule::String, QByteArray(" "), 0, 0) + : m_rule(Utils::MimeMagicRule::String, QByteArray(" "), 0, 0) { } - MagicData(Utils::Internal::MimeMagicRule rule, int priority) + MagicData(Utils::MimeMagicRule rule, int priority) : m_rule(rule) , m_priority(priority) { @@ -49,9 +49,9 @@ public: bool operator==(const MagicData &other) const; bool operator!=(const MagicData &other) { return !(*this == other); } - static QByteArray normalizedMask(const Utils::Internal::MimeMagicRule &rule); + static QByteArray normalizedMask(const Utils::MimeMagicRule &rule); - Utils::Internal::MimeMagicRule m_rule; + Utils::MimeMagicRule m_rule; int m_priority = 0; }; @@ -68,7 +68,7 @@ private: void setToRecommendedValues(); void applyRecommended(bool checked); void validateAccept(); - Utils::Internal::MimeMagicRule createRule(QString *errorMessage = nullptr) const; + Utils::MimeMagicRule createRule(QString *errorMessage = nullptr) const; Ui::MimeTypeMagicDialog ui; int m_customRangeStart = 0; diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp index d8f3b9ca64d..55fb49cb90e 100644 --- a/src/plugins/coreplugin/mimetypesettings.cpp +++ b/src/plugins/coreplugin/mimetypesettings.cpp @@ -35,7 +35,7 @@ #include <utils/algorithm.h> #include <utils/headerviewstretcher.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> @@ -87,7 +87,7 @@ public: bool isValid() const { return !name.isEmpty(); } QString name; QStringList globPatterns; - QMap<int, QList<Utils::Internal::MimeMagicRule> > rules; + QMap<int, QList<Utils::MimeMagicRule> > rules; }; // MimeTypeSettingsModel @@ -346,12 +346,12 @@ void MimeTypeSettingsPrivate::syncData(const QModelIndex ¤t, modifiedType.isValid() ? modifiedType.globPatterns.join(kSemiColon) : currentMimeType.globPatterns().join(kSemiColon)); - QMap<int, QList<Utils::Internal::MimeMagicRule> > rules = + QMap<int, QList<Utils::MimeMagicRule> > rules = modifiedType.isValid() ? modifiedType.rules : Utils::magicRulesForMimeType(currentMimeType); for (auto it = rules.constBegin(); it != rules.constEnd(); ++it) { int priority = it.key(); - foreach (const Utils::Internal::MimeMagicRule &rule, it.value()) { + foreach (const Utils::MimeMagicRule &rule, it.value()) { addMagicHeaderRow(MagicData(rule, priority)); } } @@ -394,7 +394,7 @@ void MimeTypeSettingsPrivate::editMagicHeaderRowData(const int row, const MagicD { auto item = new QTreeWidgetItem; item->setText(0, QString::fromUtf8(data.m_rule.value())); - item->setText(1, QString::fromLatin1(Utils::Internal::MimeMagicRule::typeName(data.m_rule.type()))); + item->setText(1, QString::fromLatin1(Utils::MimeMagicRule::typeName(data.m_rule.type()))); item->setText(2, QString::fromLatin1("%1:%2").arg(data.m_rule.startPos()).arg(data.m_rule.endPos())); item->setText(3, QString::number(data.m_priority)); item->setData(0, Qt::UserRole, QVariant::fromValue(data)); @@ -522,12 +522,12 @@ void MimeTypeSettingsPrivate::writeUserModifiedMimeTypes() mt.globPatterns.join(kSemiColon)); for (auto prioIt = mt.rules.constBegin(); prioIt != mt.rules.constEnd(); ++prioIt) { const QString priorityString = QString::number(prioIt.key()); - foreach (const Utils::Internal::MimeMagicRule &rule, prioIt.value()) { + foreach (const Utils::MimeMagicRule &rule, prioIt.value()) { writer.writeStartElement(QLatin1String(matchTagC)); writer.writeAttribute(QLatin1String(matchValueAttributeC), QString::fromUtf8(rule.value())); writer.writeAttribute(QLatin1String(matchTypeAttributeC), - QString::fromUtf8(Utils::Internal::MimeMagicRule::typeName(rule.type()))); + QString::fromUtf8(Utils::MimeMagicRule::typeName(rule.type()))); writer.writeAttribute(QLatin1String(matchOffsetAttributeC), QString::fromLatin1("%1:%2").arg(rule.startPos()) .arg(rule.endPos())); @@ -585,7 +585,7 @@ MimeTypeSettingsPrivate::UserMimeTypeHash MimeTypeSettingsPrivate::readUserModif int priority = atts.value(QLatin1String(priorityAttributeC)).toString().toInt(); QByteArray mask = atts.value(QLatin1String(matchMaskAttributeC)).toLatin1(); QString errorMessage; - Utils::Internal::MimeMagicRule rule(Utils::Internal::MimeMagicRule::type(typeName), + Utils::MimeMagicRule rule(Utils::MimeMagicRule::type(typeName), value, range.first, range.second, mask, &errorMessage); if (rule.isValid()) { diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index 6ae58c88b28..8297a3a0ed6 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -303,10 +303,10 @@ void OutputPaneManager::updateMaximizeButton(bool maximized) { if (maximized) { m_instance->m_minMaxAction->setIcon(m_instance->m_minimizeIcon); - m_instance->m_minMaxAction->setText(tr("Minimize Output Pane")); + m_instance->m_minMaxAction->setText(tr("Minimize")); } else { m_instance->m_minMaxAction->setIcon(m_instance->m_maximizeIcon); - m_instance->m_minMaxAction->setText(tr("Maximize Output Pane")); + m_instance->m_minMaxAction->setText(tr("Maximize")); } } @@ -352,7 +352,7 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : m_minMaxAction = new QAction(this); m_minMaxAction->setIcon(m_maximizeIcon); - m_minMaxAction->setText(tr("Maximize Output Pane")); + m_minMaxAction->setText(tr("Maximize")); m_closeButton->setIcon(Icons::CLOSE_SPLIT_BOTTOM.icon()); connect(m_closeButton, &QAbstractButton::clicked, this, &OutputPaneManager::slotHide); @@ -396,7 +396,7 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : // Window->Output Panes ActionContainer *mpanes = ActionManager::createMenu(Constants::M_VIEW_PANES); mview->addMenu(mpanes, Constants::G_VIEW_PANES); - mpanes->menu()->setTitle(tr("Output &Panes")); + mpanes->menu()->setTitle(tr("Out&put")); mpanes->appendGroup("Coreplugin.OutputPane.ActionsGroup"); mpanes->appendGroup("Coreplugin.OutputPane.PanesGroup"); diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp index ffdfc4bcde0..4b12254aa9b 100644 --- a/src/plugins/coreplugin/welcomepagehelper.cpp +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -414,10 +414,13 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti } constexpr float hoverAnimationDuration = 260; animationProgress = m_startTime.elapsed() / hoverAnimationDuration; - static const QEasingCurve animationCurve(QEasingCurve::OutCubic); - offset = animationCurve.valueForProgress(animationProgress) * shiftY; - if (offset < shiftY) + if (animationProgress < 1) { + static const QEasingCurve animationCurve(QEasingCurve::OutCubic); + offset = animationCurve.valueForProgress(animationProgress) * shiftY; QTimer::singleShot(10, this, &ListItemDelegate::goon); + } else { + offset = shiftY; + } } else if (index == m_previousIndex) { m_previousIndex = QModelIndex(); } diff --git a/src/plugins/cpaster/cpasterplugin.cpp b/src/plugins/cpaster/cpasterplugin.cpp index 825cacd4a4e..3cf987e1efe 100644 --- a/src/plugins/cpaster/cpasterplugin.cpp +++ b/src/plugins/cpaster/cpasterplugin.cpp @@ -43,7 +43,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/temporarydirectory.h> diff --git a/src/plugins/cpaster/settings.cpp b/src/plugins/cpaster/settings.cpp index d10af8fb807..a27e9876f95 100644 --- a/src/plugins/cpaster/settings.cpp +++ b/src/plugins/cpaster/settings.cpp @@ -68,7 +68,7 @@ Settings::Settings() registerAspect(&displayOutput); displayOutput.setSettingsKey("DisplayOutput"); displayOutput.setDefaultValue(true); - displayOutput.setLabelText(tr("Display Output pane after sending a post")); + displayOutput.setLabelText(tr("Display General Messages after sending a post")); } // SettingsPage diff --git a/src/plugins/cppcheck/cppchecktextmark.cpp b/src/plugins/cppcheck/cppchecktextmark.cpp index 55d5b43f4ce..d4882e7f053 100644 --- a/src/plugins/cppcheck/cppchecktextmark.cpp +++ b/src/plugins/cppcheck/cppchecktextmark.cpp @@ -29,6 +29,9 @@ #include <utils/utilsicons.h> +#include <QAction> +#include <QApplication> +#include <QClipboard> #include <QMap> namespace Cppcheck { @@ -77,6 +80,19 @@ CppcheckTextMark::CppcheckTextMark (const Diagnostic &diagnostic) setToolTip(toolTipText(diagnostic.severityText)); setLineAnnotation(diagnostic.message); setSettingsPage(Constants::OPTIONS_PAGE_ID); + + // Copy to clipboard action + QAction *action = new QAction(); + action->setIcon(QIcon::fromTheme("edit-copy", Utils::Icons::COPY.icon())); + action->setToolTip(tr("Copy to Clipboard")); + QObject::connect(action, &QAction::triggered, [diagnostic]() { + const QString text = QString("%1:%2: %3") + .arg(diagnostic.fileName.toUserOutput()) + .arg(diagnostic.lineNumber) + .arg(diagnostic.message); + QApplication::clipboard()->setText(text); + }); + setActions({action}); } QString CppcheckTextMark::toolTipText(const QString &severityText) const diff --git a/src/plugins/cppeditor/cppcompletionassist.cpp b/src/plugins/cppeditor/cppcompletionassist.cpp index ee04a159570..ab7524f0faa 100644 --- a/src/plugins/cppeditor/cppcompletionassist.cpp +++ b/src/plugins/cppeditor/cppcompletionassist.cpp @@ -42,9 +42,9 @@ #include <texteditor/texteditorsettings.h> #include <texteditor/completionsettings.h> -#include <utils/textutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> +#include <utils/textutils.h> #include <cplusplus/BackwardsScanner.h> #include <cplusplus/CppRewriter.h> diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index e79d8a17675..29e7d90257d 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -49,7 +49,7 @@ #include <utils/executeondestruction.h> #include <utils/infobar.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/runextensions.h> @@ -162,6 +162,8 @@ CompletionAssistProvider *CppEditorDocument::functionHintAssistProvider() const TextEditor::IAssistProvider *CppEditorDocument::quickFixAssistProvider() const { + if (const auto baseProvider = TextDocument::quickFixAssistProvider()) + return baseProvider; return CppEditorPlugin::instance()->quickFixProvider(); } diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 0bc90d1f1f1..a6baee1fefb 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -107,7 +107,7 @@ #include <utils/fileutils.h> #include <utils/hostosinfo.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/theme/theme.h> diff --git a/src/plugins/cppeditor/cppfilesettingspage.cpp b/src/plugins/cppeditor/cppfilesettingspage.cpp index d6abddffc40..3521211b117 100644 --- a/src/plugins/cppeditor/cppfilesettingspage.cpp +++ b/src/plugins/cppeditor/cppfilesettingspage.cpp @@ -37,7 +37,7 @@ #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/stringutils.h> #include <QCoreApplication> diff --git a/src/plugins/cppeditor/cppprojectfile.cpp b/src/plugins/cppeditor/cppprojectfile.cpp index 58196521ca1..c565df9bb40 100644 --- a/src/plugins/cppeditor/cppprojectfile.cpp +++ b/src/plugins/cppeditor/cppprojectfile.cpp @@ -28,7 +28,7 @@ #include "cppeditorconstants.h" #include <coreplugin/icore.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QDebug> diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index f254abc8df3..9d5d2271a6c 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -53,14 +53,8 @@ class CppQuickFixAssistProcessor : public IAssistProcessor { IAssistProposal *perform(const AssistInterface *interface) override { - QSharedPointer<const AssistInterface> assistInterface(interface); - auto cppInterface = assistInterface.staticCast<const CppQuickFixInterface>(); - - QuickFixOperations quickFixes; - for (CppQuickFixFactory *factory : CppQuickFixFactory::cppQuickFixFactories()) - factory->match(*cppInterface, quickFixes); - - return GenericProposal::createProposal(interface, quickFixes); + QSharedPointer<const AssistInterface> dummy(interface); // FIXME: Surely this cannot be our way of doing memory management??? + return GenericProposal::createProposal(interface, quickFixOperations(interface)); } }; @@ -137,5 +131,15 @@ bool CppQuickFixInterface::isCursorOn(const AST *ast) const return currentFile()->isCursorOn(ast); } +QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface) +{ + const auto cppInterface = dynamic_cast<const CppQuickFixInterface *>(interface); + QTC_ASSERT(cppInterface, return {}); + QuickFixOperations quickFixes; + for (CppQuickFixFactory *factory : CppQuickFixFactory::cppQuickFixFactories()) + factory->match(*cppInterface, quickFixes); + return quickFixes; +} + } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppquickfixassistant.h b/src/plugins/cppeditor/cppquickfixassistant.h index d5e626cb0e0..d6e556c7b19 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.h +++ b/src/plugins/cppeditor/cppquickfixassistant.h @@ -29,6 +29,7 @@ #include <texteditor/codeassist/assistinterface.h> #include <texteditor/codeassist/iassistprovider.h> +#include <texteditor/quickfix.h> #include <cplusplus/LookupContext.h> @@ -73,5 +74,7 @@ public: TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override; }; +TextEditor::QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface); + } // Internal } // CppEditor diff --git a/src/plugins/cppeditor/cpprefactoringchanges.cpp b/src/plugins/cppeditor/cpprefactoringchanges.cpp index 6e48ae1500b..8c16686824c 100644 --- a/src/plugins/cppeditor/cpprefactoringchanges.cpp +++ b/src/plugins/cppeditor/cpprefactoringchanges.cpp @@ -28,8 +28,6 @@ #include "cppqtstyleindenter.h" #include "cppcodeformatter.h" #include "cppeditorconstants.h" -#include "cppmodelmanager.h" -#include "cppworkingcopy.h" #include <projectexplorer/editorconfiguration.h> @@ -45,63 +43,15 @@ using namespace CPlusPlus; namespace CppEditor { -class CppRefactoringChangesData : public TextEditor::RefactoringChangesData +static std::unique_ptr<TextEditor::Indenter> createIndenter(const Utils::FilePath &filePath, + QTextDocument *textDocument) { - static std::unique_ptr<TextEditor::Indenter> createIndenter(const Utils::FilePath &filePath, - QTextDocument *textDocument) - { - TextEditor::ICodeStylePreferencesFactory *factory - = TextEditor::TextEditorSettings::codeStyleFactory(Constants::CPP_SETTINGS_ID); - std::unique_ptr<TextEditor::Indenter> indenter(factory->createIndenter(textDocument)); - indenter->setFileName(filePath); - return indenter; - } - -public: - explicit CppRefactoringChangesData(const Snapshot &snapshot) - : m_snapshot(snapshot) - , m_modelManager(CppModelManager::instance()) - , m_workingCopy(m_modelManager->workingCopy()) - {} - - void indentSelection(const QTextCursor &selection, - const Utils::FilePath &filePath, - const TextEditor::TextDocument *textDocument) const override - { - if (textDocument) { // use the indenter from the textDocument if there is one, can be ClangFormat - textDocument->indenter()->indent(selection, QChar::Null, textDocument->tabSettings()); - } else { - const auto &tabSettings = ProjectExplorer::actualTabSettings(filePath.toString(), - textDocument); - auto indenter = createIndenter(filePath, selection.document()); - indenter->indent(selection, QChar::Null, tabSettings); - } - } - - void reindentSelection(const QTextCursor &selection, - const Utils::FilePath &filePath, - const TextEditor::TextDocument *textDocument) const override - { - if (textDocument) { // use the indenter from the textDocument if there is one, can be ClangFormat - textDocument->indenter()->reindent(selection, textDocument->tabSettings()); - } else { - const auto &tabSettings = ProjectExplorer::actualTabSettings(filePath.toString(), - textDocument); - auto indenter = createIndenter(filePath, selection.document()); - indenter->reindent(selection, tabSettings); - } - } - - void fileChanged(const Utils::FilePath &filePath) override - { - m_modelManager->updateSourceFiles({filePath.toString()}); - } - - Snapshot m_snapshot; - CppModelManager *m_modelManager; - WorkingCopy m_workingCopy; - -}; + TextEditor::ICodeStylePreferencesFactory *factory + = TextEditor::TextEditorSettings::codeStyleFactory(Constants::CPP_SETTINGS_ID); + std::unique_ptr<TextEditor::Indenter> indenter(factory->createIndenter(textDocument)); + indenter->setFileName(filePath); + return indenter; +} CppRefactoringChanges::CppRefactoringChanges(const Snapshot &snapshot) : RefactoringChanges(new CppRefactoringChangesData(snapshot)) @@ -283,4 +233,41 @@ void CppRefactoringFile::fileChanged() RefactoringFile::fileChanged(); } +CppRefactoringChangesData::CppRefactoringChangesData(const Snapshot &snapshot) + : m_snapshot(snapshot) + , m_modelManager(CppModelManager::instance()) + , m_workingCopy(m_modelManager->workingCopy()) +{} + +void CppRefactoringChangesData::indentSelection(const QTextCursor &selection, + const Utils::FilePath &filePath, + const TextEditor::TextDocument *textDocument) const +{ + if (textDocument) { // use the indenter from the textDocument if there is one, can be ClangFormat + textDocument->indenter()->indent(selection, QChar::Null, textDocument->tabSettings()); + } else { + const auto &tabSettings = ProjectExplorer::actualTabSettings(filePath, textDocument); + auto indenter = createIndenter(filePath, selection.document()); + indenter->indent(selection, QChar::Null, tabSettings); + } +} + +void CppRefactoringChangesData::reindentSelection(const QTextCursor &selection, + const Utils::FilePath &filePath, + const TextEditor::TextDocument *textDocument) const +{ + if (textDocument) { // use the indenter from the textDocument if there is one, can be ClangFormat + textDocument->indenter()->reindent(selection, textDocument->tabSettings()); + } else { + const auto &tabSettings = ProjectExplorer::actualTabSettings(filePath, textDocument); + auto indenter = createIndenter(filePath, selection.document()); + indenter->reindent(selection, tabSettings); + } +} + +void CppRefactoringChangesData::fileChanged(const Utils::FilePath &filePath) +{ + m_modelManager->updateSourceFiles({filePath.toString()}); +} + } // namespace CppEditor diff --git a/src/plugins/cppeditor/cpprefactoringchanges.h b/src/plugins/cppeditor/cpprefactoringchanges.h index a50631856a3..2b4d09b9e56 100644 --- a/src/plugins/cppeditor/cpprefactoringchanges.h +++ b/src/plugins/cppeditor/cpprefactoringchanges.h @@ -27,6 +27,9 @@ #include "cppeditor_global.h" +#include "cppmodelmanager.h" +#include "cppworkingcopy.h" + #include <cplusplus/CppDocument.h> #include <texteditor/refactoringchanges.h> @@ -79,6 +82,26 @@ protected: friend class CppRefactoringChanges; // for access to constructor }; +class CPPEDITOR_EXPORT CppRefactoringChangesData : public TextEditor::RefactoringChangesData +{ +public: + explicit CppRefactoringChangesData(const CPlusPlus::Snapshot &snapshot); + + void indentSelection(const QTextCursor &selection, + const Utils::FilePath &filePath, + const TextEditor::TextDocument *textDocument) const override; + + void reindentSelection(const QTextCursor &selection, + const Utils::FilePath &filePath, + const TextEditor::TextDocument *textDocument) const override; + + void fileChanged(const Utils::FilePath &filePath) override; + + CPlusPlus::Snapshot m_snapshot; + CppModelManager *m_modelManager; + WorkingCopy m_workingCopy; +}; + class CPPEDITOR_EXPORT CppRefactoringChanges: public TextEditor::RefactoringChanges { public: diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp index bd5b702360d..7f94a6e05c8 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.cpp +++ b/src/plugins/cppeditor/cpptoolsreuse.cpp @@ -31,6 +31,7 @@ #include "cppeditorplugin.h" #include "cpphighlighter.h" #include "cppqtstyleindenter.h" +#include "cppquickfixassistant.h" #include "cpprefactoringchanges.h" #include "projectinfo.h" @@ -339,6 +340,11 @@ bool isInCommentOrString(const TextEditor::AssistInterface *interface, return true; } +TextEditor::QuickFixOperations quickFixOperations(const TextEditor::AssistInterface *interface) +{ + return Internal::quickFixOperations(interface); +} + CppCodeModelSettings *codeModelSettings() { return Internal::CppEditorPlugin::instance()->codeModelSettings(); diff --git a/src/plugins/cppeditor/cpptoolsreuse.h b/src/plugins/cppeditor/cpptoolsreuse.h index e7023b7be78..59ea0a9994e 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.h +++ b/src/plugins/cppeditor/cpptoolsreuse.h @@ -31,6 +31,7 @@ #include "compileroptionsbuilder.h" #include "projectpart.h" +#include <texteditor/quickfix.h> #include <texteditor/texteditor.h> #include <cplusplus/ASTVisitor.h> @@ -76,6 +77,8 @@ const CPlusPlus::Macro CPPEDITOR_EXPORT *findCanonicalMacro(const QTextCursor &c bool CPPEDITOR_EXPORT isInCommentOrString(const TextEditor::AssistInterface *interface, CPlusPlus::LanguageFeatures features); +TextEditor::QuickFixOperations CPPEDITOR_EXPORT +quickFixOperations(const TextEditor::AssistInterface *interface); enum class CacheUsage { ReadWrite, ReadOnly }; diff --git a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp index 8ebb0339a8c..238464619a9 100644 --- a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp +++ b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp @@ -32,7 +32,7 @@ #include <texteditor/texteditor.h> #include <utils/executeondestruction.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/tooltip/tooltip.h> diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index b619c493f32..b3190d872f0 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -42,11 +42,6 @@ #include <texteditor/textdocument.h> -#include <utils/parameteraction.h> -#include <utils/qtcassert.h> -#include <utils/qtcprocess.h> -#include <utils/stringutils.h> - #include <coreplugin/icore.h> #include <coreplugin/coreconstants.h> #include <coreplugin/documentmanager.h> @@ -58,18 +53,22 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/locator/commandlocator.h> #include <coreplugin/vcsmanager.h> + #include <utils/fileutils.h> +#include <utils/parameteraction.h> +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <utils/stringutils.h> -#include <QDebug> +#include <QAction> #include <QDate> +#include <QDebug> #include <QDir> #include <QFileInfo> -#include <QTextCodec> -#include <QAction> #include <QMainWindow> #include <QMenu> #include <QMessageBox> +#include <QTextCodec> #ifdef WITH_TESTS #include <QTest> @@ -181,7 +180,7 @@ public: QStringList arguments() const override { - QStringList args = m_settings.diffOptions.value().split(' ', SkipEmptyParts); + QStringList args = m_settings.diffOptions.value().split(' ', Qt::SkipEmptyParts); args += VcsBaseEditorConfig::arguments(); return args; } @@ -204,8 +203,8 @@ public: { if (cmd == DiffCommand) { return [](int code) { - return (code < 0 || code > 2) ? QtcProcess::FinishedWithError - : QtcProcess::FinishedWithSuccess; + return (code < 0 || code > 2) ? ProcessResult::FinishedWithError + : ProcessResult::FinishedWithSuccess; }; } return {}; @@ -1452,15 +1451,15 @@ CvsResponse CvsPluginPrivate::runCvs(const FilePath &workingDirectory, response.stdErr = proc.stdErr(); response.stdOut = proc.stdOut(); switch (proc.result()) { - case QtcProcess::FinishedWithSuccess: + case ProcessResult::FinishedWithSuccess: response.result = CvsResponse::Ok; break; - case QtcProcess::FinishedWithError: + case ProcessResult::FinishedWithError: response.result = CvsResponse::NonNullExitCode; break; - case QtcProcess::TerminatedAbnormally: - case QtcProcess::StartFailed: - case QtcProcess::Hang: + case ProcessResult::TerminatedAbnormally: + case ProcessResult::StartFailed: + case ProcessResult::Hang: break; } diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 520a2cb44f1..8c9a95a9abe 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -182,9 +182,10 @@ void addCdbOptionPages(QList<Core::IOptionsPage *> *opts) CdbEngine::CdbEngine() : m_tokenPrefix("<token>"), - m_process(ProcessMode::Writer), m_extensionCommandPrefix("!" QT_CREATOR_CDB_EXT ".") { + m_process.setProcessMode(ProcessMode::Writer); + setObjectName("CdbEngine"); setDebuggerName("CDB"); diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 6488605d09c..811b539c000 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -32,6 +32,8 @@ #include <projectexplorer/devicesupport/idevice.h> +#include <utils/qtcprocess.h> + #include <QElapsedTimer> namespace Debugger { diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 058c0a3f7ce..a8c251789be 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -185,7 +185,6 @@ void LocationMark::updateIcon() if (m_engine && EngineManager::currentEngine() == m_engine) icon = m_engine->isReverseDebugging() ? &Icons::REVERSE_LOCATION : &Icons::LOCATION; setIcon(icon->icon()); - updateMarker(); } bool LocationMark::isDraggable() const diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp index 4a34596e2fe..bdc46cb71e1 100644 --- a/src/plugins/debugger/debuggeritem.cpp +++ b/src/plugins/debugger/debuggeritem.cpp @@ -191,7 +191,7 @@ void DebuggerItem::reinitializeFromFile(const Environment &sysEnv, QString *erro proc.setCommand({m_command, {version}}); proc.runBlocking(); const QString output = proc.allOutput().trimmed(); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { if (error) *error = output; m_engineType = NoEngineType; diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index 8b3a31d2d5b..bd8ab9e3ca1 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -763,7 +763,7 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s proc.setCommand({"xcrun", {"--find", "lldb"}}); proc.runBlocking(); // FIXME: - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { QString lPath = proc.allOutput().trimmed(); if (!lPath.isEmpty()) { const QFileInfo fi(lPath); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 7ec66b5f109..2db6ecc167c 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -89,7 +89,6 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildmanager.h> #include <projectexplorer/devicesupport/deviceprocessesdialog.h> -#include <projectexplorer/devicesupport/deviceprocesslist.h> #include <projectexplorer/itaskhandler.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -114,6 +113,7 @@ #include <utils/checkablemessagebox.h> #include <utils/fancymainwindow.h> #include <utils/hostosinfo.h> +#include <utils/processinfo.h> #include <utils/proxyaction.h> #include <utils/qtcassert.h> #include <utils/statuslabel.h> @@ -606,7 +606,7 @@ public: void extensionsInitialized(); void aboutToShutdown(); - RunControl *attachToRunningProcess(Kit *kit, DeviceProcessItem process, bool contAfterAttach); + RunControl *attachToRunningProcess(Kit *kit, const ProcessInfo &process, bool contAfterAttach); void writeSettings() { @@ -1659,16 +1659,16 @@ void DebuggerPluginPrivate::attachToRunningApplication() IDevice::ConstPtr device = DeviceKitAspect::device(kit); QTC_ASSERT(device, return); - DeviceProcessItem process = dlg->currentProcess(); + const ProcessInfo processInfo = dlg->currentProcess(); if (device->type() == PE::DESKTOP_DEVICE_TYPE) { - attachToRunningProcess(kit, process, false); + attachToRunningProcess(kit, processInfo, false); } else { auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE); runControl->setKit(kit); //: %1: PID - runControl->setDisplayName(tr("Process %1").arg(process.pid)); - auto debugger = new RemoteAttachRunner(runControl, ProcessHandle(process.pid)); + runControl->setDisplayName(tr("Process %1").arg(processInfo.processId)); + auto debugger = new RemoteAttachRunner(runControl, ProcessHandle(processInfo.processId)); debugger->startRunControl(); } } @@ -1693,23 +1693,23 @@ void DebuggerPluginPrivate::attachToUnstartedApplicationDialog() } RunControl *DebuggerPluginPrivate::attachToRunningProcess(Kit *kit, - DeviceProcessItem process, bool contAfterAttach) + const ProcessInfo &processInfo, bool contAfterAttach) { QTC_ASSERT(kit, return nullptr); IDevice::ConstPtr device = DeviceKitAspect::device(kit); QTC_ASSERT(device, return nullptr); - if (process.pid == 0) { + if (processInfo.processId == 0) { AsynchronousMessageBox::warning(tr("Warning"), tr("Cannot attach to process with PID 0")); return nullptr; } const Abi tcAbi = ToolChainKitAspect::targetAbi(kit); const bool isWindows = (tcAbi.os() == Abi::WindowsOS); - if (isWindows && isWinProcessBeingDebugged(process.pid)) { + if (isWindows && isWinProcessBeingDebugged(processInfo.processId)) { AsynchronousMessageBox::warning( tr("Process Already Under Debugger Control"), tr("The process %1 is already under the control of a debugger.\n" - "%2 cannot attach to it.").arg(process.pid) + "%2 cannot attach to it.").arg(processInfo.processId) .arg(Core::Constants::IDE_DISPLAY_NAME)); return nullptr; } @@ -1723,10 +1723,10 @@ RunControl *DebuggerPluginPrivate::attachToRunningProcess(Kit *kit, auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE); runControl->setKit(kit); //: %1: PID - runControl->setDisplayName(tr("Process %1").arg(process.pid)); + runControl->setDisplayName(tr("Process %1").arg(processInfo.processId)); auto debugger = new DebuggerRunTool(runControl); - debugger->setAttachPid(ProcessHandle(process.pid)); - debugger->setInferiorExecutable(FilePath::fromString(process.exe)); + debugger->setAttachPid(ProcessHandle(processInfo.processId)); + debugger->setInferiorExecutable(FilePath::fromString(processInfo.executable)); debugger->setInferiorDevice(device); debugger->setStartMode(AttachToLocalProcess); debugger->setCloseMode(DetachAtClose); diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index ca638e41036..1a61639d71d 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -42,7 +42,6 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/devicesupport/deviceprocessesdialog.h> -#include <projectexplorer/devicesupport/deviceprocesslist.h> #include <projectexplorer/environmentaspect.h> // For the environment #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -1085,8 +1084,8 @@ DebugServerRunner::DebugServerRunner(RunControl *runControl, DebugServerPortsGat } } debugServer.command.setArguments(ProcessArgs::joinArgs(args, OsTypeLinux)); - - doStart(debugServer, runControl->device()); + debugServer.device = runControl->device(); + doStart(debugServer); }); } diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index 659b75d1bc4..c34b01d7c4f 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -34,6 +34,7 @@ #include <utils/layoutbuilder.h> #include <utils/pathchooser.h> #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <utils/variablechooser.h> #include <QFileDialog> diff --git a/src/plugins/debugger/disassembleragent.cpp b/src/plugins/debugger/disassembleragent.cpp index 1b313efaea5..d56bac0c9d8 100644 --- a/src/plugins/debugger/disassembleragent.cpp +++ b/src/plugins/debugger/disassembleragent.cpp @@ -43,7 +43,7 @@ #include <texteditor/texteditor.h> #include <utils/aspects.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <QTextBlock> diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 6a644892fea..d71a87afacd 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -54,7 +54,6 @@ #include <coreplugin/icore.h> #include <coreplugin/messagebox.h> -#include <projectexplorer/devicesupport/deviceprocess.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/taskhub.h> @@ -145,8 +144,9 @@ const char tracepointCapturePropertyName[] = "GDB.TracepointCapture"; /////////////////////////////////////////////////////////////////////// GdbEngine::GdbEngine() - : m_gdbProc(ProcessMode::Writer) { + m_gdbProc.setProcessMode(ProcessMode::Writer); + setObjectName("GdbEngine"); setDebuggerName("GDB"); @@ -4667,7 +4667,7 @@ void GdbEngine::interruptInferior2() } else if (isTermEngine()) { - terminal()->interruptProcess(); + terminal()->interrupt(); } } @@ -5013,7 +5013,7 @@ CoreInfo CoreInfo::readExecutableNameFromCore(const Runnable &debugger, const Fi proc.setCommand({debugger.command.executable(), args}); proc.runBlocking(); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { QString output = proc.stdOut(); // Core was generated by `/data/dev/creator-2.6/bin/qtcreator'. // Program terminated with signal 11, Segmentation fault. diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 287014ab7c8..13bfd56f3af 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -82,8 +82,9 @@ static int ¤tToken() /////////////////////////////////////////////////////////////////////// LldbEngine::LldbEngine() - : m_lldbProc(ProcessMode::Writer) { + m_lldbProc.setProcessMode(ProcessMode::Writer); + setObjectName("LldbEngine"); setDebuggerName("LLDB"); diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp index a040c7f1feb..4f4e8a75d1e 100644 --- a/src/plugins/debugger/moduleshandler.cpp +++ b/src/plugins/debugger/moduleshandler.cpp @@ -33,6 +33,7 @@ #include <utils/basetreeview.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <utils/treemodel.h> #include <QCoreApplication> diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 5eae3b34821..a9ab0f32264 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -63,8 +63,10 @@ using namespace Utils; namespace Debugger { namespace Internal { -PdbEngine::PdbEngine() : m_proc(ProcessMode::Writer) +PdbEngine::PdbEngine() { + m_proc.setProcessMode(ProcessMode::Writer); + setObjectName("PdbEngine"); setDebuggerName("PDB"); } diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index db0df2dd232..11d41d6e6af 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -269,11 +269,11 @@ QmlEngine::QmlEngine() connect(stackHandler(), &StackHandler::currentIndexChanged, this, &QmlEngine::updateCurrentContext); - connect(&d->applicationLauncher, &ApplicationLauncher::processExited, + connect(&d->applicationLauncher, &ApplicationLauncher::finished, this, &QmlEngine::disconnected); connect(&d->applicationLauncher, &ApplicationLauncher::appendMessage, this, &QmlEngine::appMessage); - connect(&d->applicationLauncher, &ApplicationLauncher::processStarted, + connect(&d->applicationLauncher, &ApplicationLauncher::started, this, &QmlEngine::handleLauncherStarted); debuggerConsole()->populateFileFinder(); @@ -504,17 +504,19 @@ void QmlEngine::closeConnection() void QmlEngine::startApplicationLauncher() { if (!d->applicationLauncher.isRunning()) { - const Runnable runnable = runParameters().inferior; + Runnable runnable = runParameters().inferior; + runnable.device.reset(); showMessage(tr("Starting %1").arg(runnable.command.toUserOutput()), NormalMessageFormat); - d->applicationLauncher.start(runnable); + d->applicationLauncher.setRunnable(runnable); + d->applicationLauncher.start(); } } void QmlEngine::stopApplicationLauncher() { if (d->applicationLauncher.isRunning()) { - disconnect(&d->applicationLauncher, &ApplicationLauncher::processExited, + disconnect(&d->applicationLauncher, &ApplicationLauncher::finished, this, &QmlEngine::disconnected); d->applicationLauncher.stop(); } diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp index fc1d228d787..1017695fc2b 100644 --- a/src/plugins/debugger/terminal.cpp +++ b/src/plugins/debugger/terminal.cpp @@ -182,10 +182,10 @@ void TerminalRunner::kickoffProcess() m_stubProc->kickoffProcess(); } -void TerminalRunner::interruptProcess() +void TerminalRunner::interrupt() { if (m_stubProc) - m_stubProc->interruptProcess(); + m_stubProc->interrupt(); } void TerminalRunner::start() @@ -194,9 +194,10 @@ void TerminalRunner::start() QTC_ASSERT(!m_stubProc, reportFailure({}); return); Runnable stub = m_stubRunnable(); - const QtcProcess::TerminalMode terminalMode = HostOsInfo::isWindowsHost() - ? QtcProcess::TerminalSuspend : QtcProcess::TerminalDebug; - m_stubProc = new QtcProcess(terminalMode, this); + m_stubProc = new QtcProcess(this); + m_stubProc->setTerminalMode(HostOsInfo::isWindowsHost() + ? TerminalMode::Suspend : TerminalMode::Debug); + connect(m_stubProc, &QtcProcess::errorOccurred, this, &TerminalRunner::stubError); connect(m_stubProc, &QtcProcess::started, diff --git a/src/plugins/debugger/terminal.h b/src/plugins/debugger/terminal.h index 2227bdbce4b..6534e5983e1 100644 --- a/src/plugins/debugger/terminal.h +++ b/src/plugins/debugger/terminal.h @@ -78,7 +78,7 @@ public: qint64 applicationMainThreadId() const { return m_applicationMainThreadId; } void kickoffProcess(); - void interruptProcess(); + void interrupt(); private: void start() final; diff --git a/src/plugins/debugger/unstartedappwatcherdialog.cpp b/src/plugins/debugger/unstartedappwatcherdialog.cpp index 64e3e04def0..edfc8cff44f 100644 --- a/src/plugins/debugger/unstartedappwatcherdialog.cpp +++ b/src/plugins/debugger/unstartedappwatcherdialog.cpp @@ -51,6 +51,7 @@ #include <QFileDialog> using namespace ProjectExplorer; +using namespace Utils; namespace Debugger { namespace Internal { @@ -224,7 +225,7 @@ void UnstartedAppWatcherDialog::startWatching() } } -void UnstartedAppWatcherDialog::pidFound(const DeviceProcessItem &p) +void UnstartedAppWatcherDialog::pidFound(const ProcessInfo &p) { setWaitingState(FoundState); startStopTimer(false); @@ -256,16 +257,17 @@ void UnstartedAppWatcherDialog::startStopTimer(bool start) void UnstartedAppWatcherDialog::findProcess() { const QString &appName = m_pathChooser->filePath().normalizedPathName().toString(); - DeviceProcessItem fallback; - foreach (const DeviceProcessItem &p, DeviceProcessList::localProcesses()) { - if (Utils::FileUtils::normalizedPathName(p.exe) == appName) { - pidFound(p); + ProcessInfo fallback; + const QList<ProcessInfo> processInfoList = ProcessInfo::processInfoList(); + for (const ProcessInfo &processInfo : processInfoList) { + if (Utils::FileUtils::normalizedPathName(processInfo.executable) == appName) { + pidFound(processInfo); return; } - if (p.cmdLine.startsWith(appName)) - fallback = p; + if (processInfo.commandLine.startsWith(appName)) + fallback = processInfo; } - if (fallback.pid != 0) + if (fallback.processId != 0) pidFound(fallback); } @@ -302,7 +304,7 @@ Kit *UnstartedAppWatcherDialog::currentKit() const return m_kitChooser->currentKit(); } -DeviceProcessItem UnstartedAppWatcherDialog::currentProcess() const +ProcessInfo UnstartedAppWatcherDialog::currentProcess() const { return m_process; } diff --git a/src/plugins/debugger/unstartedappwatcherdialog.h b/src/plugins/debugger/unstartedappwatcherdialog.h index 029266de17f..5186f304b53 100644 --- a/src/plugins/debugger/unstartedappwatcherdialog.h +++ b/src/plugins/debugger/unstartedappwatcherdialog.h @@ -28,7 +28,7 @@ #include <QDialog> #include <QTimer> -#include <projectexplorer/devicesupport/deviceprocesslist.h> +#include <utils/processinfo.h> QT_BEGIN_NAMESPACE class QLabel; @@ -53,7 +53,7 @@ public: explicit UnstartedAppWatcherDialog(QWidget *parent = nullptr); ProjectExplorer::Kit *currentKit() const; - ProjectExplorer::DeviceProcessItem currentProcess() const; + Utils::ProcessInfo currentProcess() const; bool hideOnAttach() const; bool continueOnAttach() const; void startWatching(); @@ -65,7 +65,7 @@ signals: private: void selectExecutable(); - void pidFound(const ProjectExplorer::DeviceProcessItem &p); + void pidFound(const Utils::ProcessInfo &p); void startStopWatching(bool start); void findProcess(); void stopAndCheckExecutable(); @@ -89,7 +89,7 @@ private: QCheckBox *m_hideOnAttachCheckBox; QCheckBox *m_continueOnAttachCheckBox; QPushButton *m_watchingPushButton; - ProjectExplorer::DeviceProcessItem m_process; + Utils::ProcessInfo m_process; QTimer m_timer; }; diff --git a/src/plugins/designer/cpp/formclasswizardpage.cpp b/src/plugins/designer/cpp/formclasswizardpage.cpp index 6f7e244eb59..52bdfe7ccba 100644 --- a/src/plugins/designer/cpp/formclasswizardpage.cpp +++ b/src/plugins/designer/cpp/formclasswizardpage.cpp @@ -31,7 +31,7 @@ #include <coreplugin/icore.h> #include <cppeditor/cppeditorconstants.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QDebug> #include <QMessageBox> diff --git a/src/plugins/designer/formeditorplugin.cpp b/src/plugins/designer/formeditorplugin.cpp index eafddea3e36..ebe2f7cad38 100644 --- a/src/plugins/designer/formeditorplugin.cpp +++ b/src/plugins/designer/formeditorplugin.cpp @@ -39,14 +39,14 @@ #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/designmode.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> #include <coreplugin/idocument.h> -#include <coreplugin/coreconstants.h> -#include <coreplugin/designmode.h> #include <cppeditor/cppeditorconstants.h> #include <projectexplorer/jsonwizard/jsonwizardfactory.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QAction> #include <QCoreApplication> diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp index 4559c1df996..00df6f19b84 100644 --- a/src/plugins/designer/qtcreatorintegration.cpp +++ b/src/plugins/designer/qtcreatorintegration.cpp @@ -46,7 +46,7 @@ #include <projectexplorer/projecttree.h> #include <projectexplorer/session.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index a89eca89c3c..8b01467e27a 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -88,8 +88,6 @@ #include <sys/types.h> #endif -//#define ALLOW_LOCAL_ACCESS 1 - using namespace Core; using namespace ProjectExplorer; using namespace QtSupport; @@ -101,50 +99,50 @@ namespace Internal { static Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg); #define LOG(x) qCDebug(dockerDeviceLog) << this << x << '\n' -class DockerDeviceProcess : public ProjectExplorer::DeviceProcess +class DockerDeviceProcess : public Utils::QtcProcess { public: DockerDeviceProcess(const QSharedPointer<const IDevice> &device, QObject *parent = nullptr); ~DockerDeviceProcess() {} - void start(const Runnable &runnable) override; + void start() override; void interrupt() override; + + const QSharedPointer<const IDevice> m_device; }; DockerDeviceProcess::DockerDeviceProcess(const QSharedPointer<const IDevice> &device, - QObject *parent) - : DeviceProcess(device, ProcessMode::Writer, parent) + QObject *parent) + : QtcProcess(parent), m_device(device) { + setProcessMode(ProcessMode::Writer); } -void DockerDeviceProcess::start(const Runnable &runnable) +void DockerDeviceProcess::start() { QTC_ASSERT(state() == QProcess::NotRunning, return); - DockerDevice::ConstPtr dockerDevice = qSharedPointerCast<const DockerDevice>(device()); + DockerDevice::ConstPtr dockerDevice = qSharedPointerCast<const DockerDevice>(m_device); QTC_ASSERT(dockerDevice, return); - connect(this, &DeviceProcess::readyReadStandardOutput, this, [this] { + connect(this, &QtcProcess::readyReadStandardOutput, this, [this] { MessageManager::writeSilently(QString::fromLocal8Bit(readAllStandardError())); }); - connect(this, &DeviceProcess::readyReadStandardError, this, [this] { + connect(this, &QtcProcess::readyReadStandardError, this, [this] { MessageManager::writeDisrupting(QString::fromLocal8Bit(readAllStandardError())); }); - CommandLine command = runnable.command; + CommandLine command = commandLine(); command.setExecutable( command.executable().withNewPath(dockerDevice->mapToDevicePath(command.executable()))); setCommand(command); - setEnvironment(runnable.environment); - setWorkingDirectory(runnable.workingDirectory); - LOG("Running process:" << command.toUserOutput() - << "in" << runnable.workingDirectory.toUserOutput()); - dockerDevice->runProcess(*this); + LOG("Running process:" << command.toUserOutput() << "in" << workingDirectory().toUserOutput()); + QtcProcess::start(); } void DockerDeviceProcess::interrupt() { - device()->signalOperation()->interruptProcess(processId()); + m_device->signalOperation()->interruptProcess(processId()); } class DockerPortsGatheringMethod : public PortsGatheringMethod @@ -249,18 +247,7 @@ class DockerDevicePrivate : public QObject public: DockerDevicePrivate(DockerDevice *parent) : q(parent) - { -#ifdef ALLOW_LOCAL_ACCESS - connect(&m_mergedDirWatcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &path) { - Q_UNUSED(path) - LOG("Container watcher change, file: " << path); - }); - connect(&m_mergedDirWatcher, &QFileSystemWatcher::directoryChanged, this, [this](const QString &path) { - Q_UNUSED(path) - LOG("Container watcher change, directory: " << path); - }); -#endif - } + {} ~DockerDevicePrivate() { stopCurrentContainer(); } @@ -269,7 +256,6 @@ public: QByteArray outputForRunInShell(const CommandLine &cmd) const; void updateContainerAccess(); - void updateFileSystemAccess(); void startContainer(); void stopCurrentContainer(); @@ -283,11 +269,6 @@ public: mutable QMutex m_shellMutex; QString m_container; -#ifdef ALLOW_LOCAL_ACCESS - QString m_mergedDir; - QFileSystemWatcher m_mergedDirWatcher; -#endif - Environment m_cachedEnviroment; bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback @@ -344,24 +325,6 @@ public: data.useLocalUidGid = on; }); -#ifdef ALLOW_LOCAL_ACCESS - // This tries to find the directory in the host file system that corresponds to the - // docker container root file system, which is a merge of the layers from the - // container image and the volumes mapped using -v on container startup. - // - // Accessing files there is much faster than using 'docker exec', but conceptually - // only works on Linux, and is restricted there by proper matching of user - // permissions between host and container. - m_usePathMapping = new QCheckBox(tr("Use local file path mapping")); - m_usePathMapping->setToolTip(tr("Maps docker filesystem to a local directory.")); - m_usePathMapping->setChecked(data.useFilePathMapping); - m_usePathMapping->setEnabled(HostOsInfo::isLinuxHost()); - connect(m_usePathMapping, &QCheckBox::toggled, this, [&, dockerDevice](bool on) { - data.useFilePathMapping = on; - dockerDevice->updateContainerAccess(); - }); -#endif - m_pathsListLabel = new InfoLabel(tr("Paths to mount:")); // FIXME: 8.0: use //m_pathsListLabel->setToolTip(tr("Source directory list should not be empty")); @@ -449,9 +412,6 @@ public: idLabel, m_idLineEdit, Break(), daemonStateLabel, m_daemonReset, m_daemonState, Break(), m_runAsOutsideUser, Break(), -#ifdef ALLOW_LOCAL_ACCESS - m_usePathMapping, Break(), -#endif Column { m_pathsListLabel, m_pathsListEdit, @@ -493,9 +453,6 @@ private: QToolButton *m_daemonReset; QLabel *m_daemonState; QCheckBox *m_runAsOutsideUser; -#ifdef ALLOW_LOCAL_ACCESS - QCheckBox *m_usePathMapping; -#endif InfoLabel *m_pathsListLabel; PathListEditor *m_pathsListEdit; @@ -554,10 +511,12 @@ DockerDevice::DockerDevice(const DockerDeviceData &data) return; } - QtcProcess *proc = new QtcProcess(QtcProcess::TerminalOn); + QtcProcess *proc = new QtcProcess; + proc->setTerminalMode(TerminalMode::On); + QObject::connect(proc, &QtcProcess::finished, proc, &QObject::deleteLater); - QObject::connect(proc, &DeviceProcess::errorOccurred, [proc] { + QObject::connect(proc, &QtcProcess::errorOccurred, [proc] { MessageManager::writeDisrupting(tr("Error starting remote shell.")); proc->deleteLater(); }); @@ -836,9 +795,6 @@ void DockerDevicePrivate::stopCurrentContainer() if (m_shell->state() == QProcess::NotRunning) { LOG("Clean exit via shell"); m_container.clear(); -#ifdef ALLOW_LOCAL_ACCESS - m_mergedDir.clear(); -#endif delete m_shell; m_shell = nullptr; return; @@ -849,9 +805,6 @@ void DockerDevicePrivate::stopCurrentContainer() proc.setCommand({"docker", {"container", "stop", m_container}}); m_container.clear(); -#ifdef ALLOW_LOCAL_ACCESS - m_mergedDir.clear(); -#endif proc.runBlocking(); } @@ -904,7 +857,7 @@ void DockerDevicePrivate::startContainer() createProcess.setCommand(dockerCreate); createProcess.runBlocking(); - if (createProcess.result() != QtcProcess::FinishedWithSuccess) + if (createProcess.result() != ProcessResult::FinishedWithSuccess) return; m_container = createProcess.stdOut().trimmed(); @@ -914,12 +867,13 @@ void DockerDevicePrivate::startContainer() CommandLine dockerRun{"docker", {"container" , "start", "-i", "-a", m_container}}; LOG("RUNNING: " << dockerRun.toUserOutput()); - QPointer<QtcProcess> shell = new QtcProcess(ProcessMode::Writer); + QPointer<QtcProcess> shell = new QtcProcess; + shell->setProcessMode(ProcessMode::Writer); connect(shell, &QtcProcess::finished, this, [this, shell] { LOG("\nSHELL FINISHED\n"); QTC_ASSERT(shell, return); const int exitCode = shell->exitCode(); - LOG("RES: " << shell->result() + LOG("RES: " << int(shell->result()) << " EXIT CODE: " << exitCode << " STDOUT: " << shell->readAllStandardOutput() << " STDERR: " << shell->readAllStandardError()); @@ -958,69 +912,10 @@ void DockerDevicePrivate::updateContainerAccess() if (DockerPlugin::isDaemonRunning().value_or(true) == false) return; - if (!m_shell) - startContainer(); - - updateFileSystemAccess(); -} - -void DockerDevicePrivate::updateFileSystemAccess() -{ -#ifdef ALLOW_LOCAL_ACCESS - if (!m_data.useFilePathMapping) { - // Direct access was used previously, but is not wanted anymore. - if (!m_mergedDir.isEmpty()) { - m_mergedDirWatcher.removePath(m_mergedDir); - m_mergedDir.clear(); - } + if (m_shell) return; - } - - if (!DockerPlugin::isDaemonRunning().value_or(false)) - return; - - QtcProcess proc; - proc.setCommand({"docker", {"inspect", "--format={{.GraphDriver.Data.MergedDir}}", m_container}}); - LOG(proc.commandLine().toUserOutput()); - proc.start(); - proc.waitForFinished(); - const QString out = proc.stdOut(); - m_mergedDir = out.trimmed(); - LOG("Found merged dir: " << m_mergedDir); - if (m_mergedDir.endsWith('/')) - m_mergedDir.chop(1); - - if (!QFileInfo(m_mergedDir).isReadable()) { - MessageManager::writeFlashing( - tr("Local read access to docker container %1 unavailable through directory \"%2\".") - .arg(m_container, m_mergedDir) - + '\n' + tr("Output: \"%1\"").arg(out) - + '\n' + tr("Error: \"%1\"").arg(proc.stdErr())); - if (!HostOsInfo::isLinuxHost()) { - // Disabling merged layer access. This is not supported and anything - // related to accessing merged layers on Windows or macOS fails due - // to the need of using wsl or a named pipe. - // TODO investigate how to make it possible nevertheless. - m_mergedDir.clear(); - MessageManager::writeSilently(tr("This is expected on Windows and macOS.")); - return; - } - } - m_mergedDirWatcher.addPath(m_mergedDir); -#endif -} - -bool DockerDevice::hasLocalFileAccess() const -{ -#ifdef ALLOW_LOCAL_ACCESS - static const bool denyLocalAccess = qEnvironmentVariableIsSet("QTC_DOCKER_DENY_LOCAL_ACCESS"); - if (denyLocalAccess) - return false; - return !d->m_mergedDir.isEmpty(); -#else - return false; -#endif + startContainer(); } void DockerDevice::setMounts(const QStringList &mounts) const @@ -1029,50 +924,11 @@ void DockerDevice::setMounts(const QStringList &mounts) const d->stopCurrentContainer(); // Force re-start with new mounts. } -FilePath DockerDevice::mapToLocalAccess(const FilePath &filePath) const -{ -#ifdef ALLOW_LOCAL_ACCESS - QTC_ASSERT(!d->m_mergedDir.isEmpty(), return {}); - QString path = filePath.path(); - for (const QString &mount : qAsConst(d->m_data.mounts)) { - if (path.startsWith(mount + '/')) - return FilePath::fromString(path); - } - if (path.startsWith('/')) - return FilePath::fromString(d->m_mergedDir + path); - return FilePath::fromString(d->m_mergedDir + '/' + path); -#else - QTC_CHECK(false); - Q_UNUSED(filePath) - return {}; -#endif -} - -FilePath DockerDevice::mapFromLocalAccess(const FilePath &filePath) const -{ - QTC_ASSERT(!filePath.needsDevice(), return {}); - return mapFromLocalAccess(filePath.toString()); -} - -FilePath DockerDevice::mapFromLocalAccess(const QString &filePath) const -{ -#ifdef ALLOW_LOCAL_FILE_ACCESS - QTC_ASSERT(!d->m_mergedDir.isEmpty(), return {}); - QTC_ASSERT(filePath.startsWith(d->m_mergedDir), return FilePath::fromString(filePath)); - return mapToGlobalPath(FilePath::fromString(filePath.mid(d->m_mergedDir.size()))); -#else - Q_UNUSED(filePath) - QTC_CHECK(false); - return {}; -#endif -} - const char DockerDeviceDataImageIdKey[] = "DockerDeviceDataImageId"; const char DockerDeviceDataRepoKey[] = "DockerDeviceDataRepo"; const char DockerDeviceDataTagKey[] = "DockerDeviceDataTag"; const char DockerDeviceDataSizeKey[] = "DockerDeviceDataSize"; const char DockerDeviceUseOutsideUser[] = "DockerDeviceUseUidGid"; -const char DockerDeviceUseFilePathMapping[] = "DockerDeviceFilePathMapping"; const char DockerDeviceMappedPaths[] = "DockerDeviceMappedPaths"; void DockerDevice::fromMap(const QVariantMap &map) @@ -1084,8 +940,6 @@ void DockerDevice::fromMap(const QVariantMap &map) d->m_data.size = map.value(DockerDeviceDataSizeKey).toString(); d->m_data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser, HostOsInfo::isLinuxHost()).toBool(); - d->m_data.useFilePathMapping = map.value(DockerDeviceUseFilePathMapping, - HostOsInfo::isLinuxHost()).toBool(); d->m_data.mounts = map.value(DockerDeviceMappedPaths).toStringList(); } @@ -1097,12 +951,11 @@ QVariantMap DockerDevice::toMap() const map.insert(DockerDeviceDataImageIdKey, d->m_data.imageId); map.insert(DockerDeviceDataSizeKey, d->m_data.size); map.insert(DockerDeviceUseOutsideUser, d->m_data.useLocalUidGid); - map.insert(DockerDeviceUseFilePathMapping, d->m_data.useFilePathMapping); map.insert(DockerDeviceMappedPaths, d->m_data.mounts); return map; } -DeviceProcess *DockerDevice::createProcess(QObject *parent) const +QtcProcess *DockerDevice::createProcess(QObject *parent) const { return new DockerDeviceProcess(sharedFromThis(), parent); } @@ -1189,12 +1042,6 @@ bool DockerDevice::isExecutableFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.isExecutableFile(); - LOG("Executable? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-x", path}}); } @@ -1203,12 +1050,6 @@ bool DockerDevice::isReadableFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.isReadableFile(); - LOG("ReadableFile? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-r", path, "-a", "-f", path}}); } @@ -1217,12 +1058,6 @@ bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.isWritableFile(); - LOG("WritableFile? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-w", path, "-a", "-f", path}}); } @@ -1231,12 +1066,6 @@ bool DockerDevice::isReadableDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.isReadableDir(); - LOG("ReadableDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-r", path, "-a", "-d", path}}); } @@ -1245,12 +1074,6 @@ bool DockerDevice::isWritableDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.isWritableDir(); - LOG("WritableDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-w", path, "-a", "-d", path}}); } @@ -1259,12 +1082,6 @@ bool DockerDevice::isFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.isFile(); - LOG("IsFile? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-f", path}}); } @@ -1273,12 +1090,6 @@ bool DockerDevice::isDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.isDir(); - LOG("IsDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-d", path}}); } @@ -1287,12 +1098,6 @@ bool DockerDevice::createDirectory(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.createDir(); - LOG("CreateDirectory? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInContainer({"mkdir", {"-p", path}}); } @@ -1301,12 +1106,6 @@ bool DockerDevice::exists(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.exists(); - LOG("Exists? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"test", {"-e", path}}); } @@ -1315,12 +1114,6 @@ bool DockerDevice::ensureExistingFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.ensureExistingFile(); - LOG("Ensure existing file? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.path(); return d->runInShell({"touch", {path}}); } @@ -1329,12 +1122,6 @@ bool DockerDevice::removeFile(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.removeFile(); - LOG("Remove? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } return d->runInContainer({"rm", {filePath.path()}}); } @@ -1343,12 +1130,6 @@ bool DockerDevice::removeRecursively(const FilePath &filePath) const QTC_ASSERT(handlesFile(filePath), return false); QTC_ASSERT(filePath.path().startsWith('/'), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const bool res = localAccess.removeRecursively(); - LOG("Remove recursively? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } const QString path = filePath.cleanPath().path(); // We are expecting this only to be called in a context of build directories or similar. @@ -1365,13 +1146,6 @@ bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) co QTC_ASSERT(handlesFile(filePath), return false); QTC_ASSERT(handlesFile(target), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const FilePath localTarget = mapToLocalAccess(target); - const bool res = localAccess.copyFile(localTarget); - LOG("Copy " << filePath.toUserOutput() << localAccess.toUserOutput() << localTarget << res); - return res; - } return d->runInContainer({"cp", {filePath.path(), target.path()}}); } @@ -1380,13 +1154,6 @@ bool DockerDevice::renameFile(const FilePath &filePath, const FilePath &target) QTC_ASSERT(handlesFile(filePath), return false); QTC_ASSERT(handlesFile(target), return false); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const FilePath localTarget = mapToLocalAccess(target); - const bool res = localAccess.renameFile(localTarget); - LOG("Move " << filePath.toUserOutput() << localAccess.toUserOutput() << localTarget << res); - return res; - } return d->runInContainer({"mv", {filePath.path(), target.path()}}); } @@ -1394,13 +1161,6 @@ QDateTime DockerDevice::lastModified(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const QDateTime res = localAccess.lastModified(); - LOG("Last modified? " << filePath.toUserOutput() << localAccess.toUserOutput() << res); - return res; - } - const QByteArray output = d->outputForRunInShell({"stat", {"-c", "%Y", filePath.path()}}); qint64 secs = output.toLongLong(); const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); @@ -1411,15 +1171,6 @@ FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - const FilePath target = localAccess.symLinkTarget(); - LOG("SymLinkTarget? " << filePath.toUserOutput() << localAccess.toUserOutput() << target); - if (target.isEmpty()) - return {}; - return mapToGlobalPath(target); - } - const QByteArray output = d->outputForRunInShell({"readlink", {"-n", "-e", filePath.path()}}); const QString out = QString::fromUtf8(output.data(), output.size()); return out.isEmpty() ? FilePath() : filePath.withNewPath(out); @@ -1429,12 +1180,6 @@ qint64 DockerDevice::fileSize(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return -1); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - LOG("File size? " << filePath.toUserOutput() << localAccess.toUserOutput() << localAccess.fileSize()); - return localAccess.fileSize(); - } - const QByteArray output = d->outputForRunInShell({"stat", {"-c", "%s", filePath.path()}}); return output.toLongLong(); } @@ -1443,11 +1188,6 @@ QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) con { QTC_ASSERT(handlesFile(filePath), return {}); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - LOG("Permissions? " << filePath.toUserOutput() << localAccess.toUserOutput() << localAccess.permissions()); - return localAccess.permissions(); - } const QByteArray output = d->outputForRunInShell({"stat", {"-c", "%a", filePath.path()}}); const uint bits = output.toUInt(nullptr, 8); @@ -1470,12 +1210,6 @@ bool DockerDevice::setPermissions(const FilePath &filePath, QFileDevice::Permiss { QTC_ASSERT(handlesFile(filePath), return {}); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath localAccess = mapToLocalAccess(filePath); - LOG("Set permissions? " << filePath.toUserOutput() << localAccess.toUserOutput() << localAccess.permissions()); - return localAccess.setPermissions(permissions); - } - QTC_CHECK(false); // FIXME: Implement. return false; } @@ -1557,55 +1291,12 @@ void DockerDevice::iterateWithFind(const FilePath &filePath, } } -static void filterEntriesHelper(const FilePath &base, - const std::function<bool(const FilePath &)> &callBack, - const QStringList &entries, - const FileFilter &filter) -{ - QTC_CHECK(filter.iteratorFlags != QDirIterator::NoIteratorFlags); // FIXME: Not supported yet below. - - const QList<QRegularExpression> nameRegexps = - transform(filter.nameFilters, [](const QString &filter) { - QRegularExpression re; - re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); - QTC_CHECK(re.isValid()); - return re; - }); - - const auto nameMatches = [&nameRegexps](const QString &fileName) { - for (const QRegularExpression &re : nameRegexps) { - const QRegularExpressionMatch match = re.match(fileName); - if (match.hasMatch()) - return true; - } - return nameRegexps.isEmpty(); - }; - - // FIXME: Handle filters. For now bark on unsupported options. - QTC_CHECK(filter.fileFilters == QDir::NoFilter); - - for (const QString &entry : entries) { - if (!nameMatches(entry)) - continue; - if (!callBack(base.pathAppended(entry))) - break; - } -} - void DockerDevice::iterateDirectory(const FilePath &filePath, const std::function<bool(const FilePath &)> &callBack, const FileFilter &filter) const { QTC_ASSERT(handlesFile(filePath), return); updateContainerAccess(); - if (hasLocalFileAccess()) { - const FilePath local = mapToLocalAccess(filePath); - local.iterateDirectory([&callBack, this](const FilePath &entry) { - return callBack(mapFromLocalAccess(entry)); - }, - filter); - return; - } if (d->m_useFind) { iterateWithFind(filePath, callBack, filter); @@ -1617,17 +1308,14 @@ void DockerDevice::iterateDirectory(const FilePath &filePath, // if we do not have find - use ls as fallback const QByteArray output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}}); - const QString out = QString::fromUtf8(output.data(), output.size()); - const QStringList entries = out.split('\n', Qt::SkipEmptyParts); - filterEntriesHelper(filePath, callBack, entries, filter); + const QStringList entries = QString::fromUtf8(output).split('\n', Qt::SkipEmptyParts); + FileUtils::iterateLsOutput(filePath, entries, filter, callBack); } QByteArray DockerDevice::fileContents(const FilePath &filePath, qint64 limit, qint64 offset) const { QTC_ASSERT(handlesFile(filePath), return {}); updateContainerAccess(); - if (hasLocalFileAccess()) - return mapToLocalAccess(filePath).fileContents(limit, offset); QStringList args = {"if=" + filePath.path(), "status=none"}; if (limit > 0 || offset > 0) { @@ -1650,8 +1338,6 @@ bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray { QTC_ASSERT(handlesFile(filePath), return {}); updateContainerAccess(); - if (hasLocalFileAccess()) - return mapToLocalAccess(filePath).writeFileContents(data); // This following would be the generic Unix solution. // But it doesn't pass input. FIXME: Why? @@ -1685,7 +1371,7 @@ void DockerDevice::runProcess(QtcProcess &process) const if (d->m_container.isEmpty()) { LOG("No container set to run " << process.commandLine().toUserOutput()); QTC_CHECK(false); - process.setResult(QtcProcess::StartFailed); + process.setResult(ProcessResult::StartFailed); return; } @@ -1700,7 +1386,7 @@ void DockerDevice::runProcess(QtcProcess &process) const } if (process.processMode() == ProcessMode::Writer) cmd.addArg("-i"); - if (env.size() != 0 && !hasLocalFileAccess()) { + if (env.size() != 0) { process.unsetEnvironment(); // FIXME the below would be probably correct if the respective tools would use correct // environment already, but most are using the host environment which usually makes diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index f6f8f988539..106325cd249 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -45,7 +45,6 @@ public: QString tag; QString size; bool useLocalUidGid = true; - bool useFilePathMapping = false; QStringList mounts; }; @@ -66,7 +65,7 @@ public: QList<ProjectExplorer::Task> validate() const override; bool canCreateProcess() const override { return true; } - ProjectExplorer::DeviceProcess *createProcess(QObject *parent) const override; + Utils::QtcProcess *createProcess(QObject *parent) const override; bool canAutoDetectPorts() const override; ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override; bool canCreateProcessModel() const override { return false; } @@ -112,13 +111,8 @@ public: DockerDeviceData &data(); void updateContainerAccess() const; - bool hasLocalFileAccess() const; void setMounts(const QStringList &mounts) const; - Utils::FilePath mapToLocalAccess(const Utils::FilePath &filePath) const; - Utils::FilePath mapFromLocalAccess(const Utils::FilePath &filePath) const; - Utils::FilePath mapFromLocalAccess(const QString &filePath) const; - protected: void fromMap(const QVariantMap &map) final; QVariantMap toMap() const final; diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 0ca936d4bff..6f9dc70acd9 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -698,13 +698,8 @@ void FakeVimExCommandsPage::apply() } settings->endArray(); globalCommandMapping.clear(); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) globalCommandMapping.insert(defaultMap); globalCommandMapping.insert(newMapping); -#else - globalCommandMapping.unite(defaultMap); - globalCommandMapping.unite(newMapping); -#endif } } @@ -956,13 +951,8 @@ void FakeVimUserCommandsPage::apply() } settings->endArray(); userMap.clear(); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) userMap.insert(dd->m_defaultUserCommandMap); userMap.insert(current); -#else - userMap.unite(dd->m_defaultUserCommandMap); - userMap.unite(current); -#endif } } diff --git a/src/plugins/genericprojectmanager/genericprojectwizard.cpp b/src/plugins/genericprojectmanager/genericprojectwizard.cpp index af9b4343d4b..23020b97763 100644 --- a/src/plugins/genericprojectmanager/genericprojectwizard.cpp +++ b/src/plugins/genericprojectmanager/genericprojectwizard.cpp @@ -36,7 +36,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/filewizardpage.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QApplication> #include <QDebug> diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp index b56ee47916a..0313304f7d0 100644 --- a/src/plugins/git/changeselectiondialog.cpp +++ b/src/plugins/git/changeselectiondialog.cpp @@ -31,19 +31,20 @@ #include <coreplugin/vcsmanager.h> #include <utils/pathchooser.h> +#include <utils/qtcprocess.h> #include <utils/theme/theme.h> #include <vcsbase/vcscommand.h> +#include <QCompleter> +#include <QDir> +#include <QFileDialog> #include <QFormLayout> -#include <QHBoxLayout> -#include <QPushButton> #include <QLabel> +#include <QLayout> #include <QLineEdit> #include <QPlainTextEdit> -#include <QDir> -#include <QFileDialog> -#include <QCompleter> +#include <QPushButton> #include <QStringListModel> #include <QTimer> @@ -161,7 +162,7 @@ void ChangeSelectionDialog::setDetails() Theme *theme = creatorTheme(); QPalette palette; - if (m_process->result() == QtcProcess::FinishedWithSuccess) { + if (m_process->result() == ProcessResult::FinishedWithSuccess) { m_ui->detailsText->setPlainText(m_process->stdOut()); palette.setColor(QPalette::Text, theme->color(Theme::TextColorNormal)); m_ui->changeNumberEdit->setPalette(palette); diff --git a/src/plugins/git/gerrit/gerritserver.cpp b/src/plugins/git/gerrit/gerritserver.cpp index efc1d02520e..eab2e707590 100644 --- a/src/plugins/git/gerrit/gerritserver.cpp +++ b/src/plugins/git/gerrit/gerritserver.cpp @@ -247,7 +247,7 @@ int GerritServer::testConnection() QtcProcess proc; client->vcsFullySynchronousExec(proc, {}, {curlBinary, arguments}, Core::ShellCommand::NoOutput); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { QString output = proc.stdOut(); // Gerrit returns an empty response for /p/qt-creator/a/accounts/self // so consider this as 404. @@ -357,7 +357,7 @@ void GerritServer::resolveVersion(const GerritParameters &p, bool forceReload) Core::ShellCommand::NoOutput); // REST endpoint for version is only available from 2.8 and up. Do not consider invalid // if it fails. - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { QString output = proc.stdOut(); if (output.isEmpty()) return; diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 3a51e2c043b..cedf7715c37 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -47,7 +47,7 @@ #include <utils/commandline.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/stringutils.h> @@ -678,7 +678,7 @@ public: { ConflictHandler handler(workingDirectory, abortCommand); // No conflicts => do nothing - if (proc.result() == QtcProcess::FinishedWithSuccess) + if (proc.result() == ProcessResult::FinishedWithSuccess) return; handler.readStdOut(proc.stdOut()); handler.readStdErr(proc.stdErr()); @@ -844,7 +844,7 @@ bool GitClient::managesFile(const FilePath &workingDirectory, const QString &fil QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, {"ls-files", "--error-unmatch", fileName}, Core::ShellCommand::NoOutput); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } FilePaths GitClient::unmanagedFiles(const FilePaths &filePaths) const @@ -860,7 +860,7 @@ FilePaths GitClient::unmanagedFiles(const FilePaths &filePaths) const args << transform(it.value(), [&wd](const QString &fp) { return wd.relativeFilePath(fp); }); QtcProcess proc; vcsFullySynchronousExec(proc, it.key(), args, Core::ShellCommand::NoOutput); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return filePaths; const QStringList managedFilePaths = transform(proc.stdOut().split('\0', Qt::SkipEmptyParts), @@ -1433,7 +1433,7 @@ void GitClient::recoverDeletedFiles(const FilePath &workingDirectory) QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, {"ls-files", "--deleted"}, VcsCommand::SuppressCommandLogging); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { const QString stdOut = proc.stdOut().trimmed(); if (stdOut.isEmpty()) { VcsOutputWindow::appendError(tr("Nothing to recover")); @@ -1460,7 +1460,7 @@ bool GitClient::synchronousLog(const FilePath &workingDirectory, const QStringLi QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, allArguments, flags, vcsTimeoutS(), encoding(workingDirectory, "i18n.logOutputEncoding")); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { *output = proc.stdOut(); return true; } else { @@ -1478,7 +1478,7 @@ bool GitClient::synchronousAdd(const FilePath &workingDirectory, args += extraOptions + files; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, args); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } bool GitClient::synchronousDelete(const FilePath &workingDirectory, @@ -1491,7 +1491,7 @@ bool GitClient::synchronousDelete(const FilePath &workingDirectory, arguments.append(files); QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } bool GitClient::synchronousMove(const FilePath &workingDirectory, @@ -1500,7 +1500,7 @@ bool GitClient::synchronousMove(const FilePath &workingDirectory, { QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, {"mv", from, to}); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } bool GitClient::synchronousReset(const FilePath &workingDirectory, @@ -1520,7 +1520,7 @@ bool GitClient::synchronousReset(const FilePath &workingDirectory, // Note that git exits with 1 even if the operation is successful // Assume real failure if the output does not contain "foo.cpp modified" // or "Unstaged changes after reset" (git 1.7.0). - if (proc.result() != QtcProcess::FinishedWithSuccess + if (proc.result() != ProcessResult::FinishedWithSuccess && (!stdOut.contains("modified") && !stdOut.contains("Unstaged changes after reset"))) { if (files.isEmpty()) { msgCannotRun(arguments, workingDirectory, proc.stdErr(), errorMessage); @@ -1541,7 +1541,7 @@ bool GitClient::synchronousInit(const FilePath &workingDirectory) vcsFullySynchronousExec(proc, workingDirectory, QStringList{"init"}); // '[Re]Initialized...' VcsOutputWindow::append(proc.stdOut()); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { resetCachedVcsInfo(workingDirectory); return true; } else { @@ -1567,7 +1567,7 @@ bool GitClient::synchronousCheckoutFiles(const FilePath &workingDirectory, QStri arguments << "--" << files; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, VcsCommand::ExpectRepoChanges); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { const QString fileArg = files.join(", "); //: Meaning of the arguments: %1: revision, %2: files, %3: repository, //: %4: Error message @@ -1619,7 +1619,7 @@ bool GitClient::synchronousRevListCmd(const FilePath &workingDirectory, const QS const QStringList arguments = QStringList({"rev-list", noColorOption}) + extraArguments; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, silentFlags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(arguments, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -1683,7 +1683,7 @@ QString GitClient::synchronousCurrentLocalBranch(const FilePath &workingDirector QString branch; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, {"symbolic-ref", HEAD}, silentFlags); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { branch = proc.stdOut().trimmed(); } else { const QString gitDir = findGitDirForRepository(workingDirectory); @@ -1708,7 +1708,7 @@ bool GitClient::synchronousHeadRefs(const FilePath &workingDirectory, QStringLis const QStringList arguments = {"show-ref", "--head", "--abbrev=10", "--dereference"}; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, silentFlags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(arguments, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -1757,7 +1757,7 @@ QString GitClient::synchronousTopic(const FilePath &workingDirectory) const // No tag or remote branch - try git describe QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, QStringList{"describe"}, VcsCommand::NoOutput); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { const QString stdOut = proc.stdOut().trimmed(); if (!stdOut.isEmpty()) return stdOut; @@ -1772,7 +1772,7 @@ bool GitClient::synchronousRevParseCmd(const FilePath &workingDirectory, const Q QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, silentFlags); *output = proc.stdOut().trimmed(); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(arguments, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -1786,7 +1786,7 @@ QString GitClient::synchronousTopRevision(const FilePath &workingDirectory, QDat const QStringList arguments = {"show", "-s", "--pretty=format:%H:%ct", HEAD}; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, silentFlags); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return QString(); const QStringList output = proc.stdOut().trimmed().split(':'); if (dateTime && output.size() > 1) { @@ -1848,7 +1848,7 @@ QString GitClient::synchronousShortDescription(const FilePath &workingDirectory, "--max-count=1", revision}; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, silentFlags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { VcsOutputWindow::appendSilently(tr("Cannot describe revision \"%1\" in \"%2\": %3") .arg(revision, workingDirectory.toUserOutput(), proc.stdErr())); return revision; @@ -1929,7 +1929,7 @@ bool GitClient::executeSynchronousStash(const FilePath &workingDirectory, | VcsCommand::ShowSuccessMessage; QtcProcess proc; vcsSynchronousExec(proc, workingDirectory, arguments, flags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(arguments, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -1970,7 +1970,7 @@ bool GitClient::synchronousBranchCmd(const FilePath &workingDirectory, QStringLi QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, branchArgs); *output = proc.stdOut(); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(branchArgs, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -1984,7 +1984,7 @@ bool GitClient::synchronousTagCmd(const FilePath &workingDirectory, QStringList QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, tagArgs); *output = proc.stdOut(); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(tagArgs, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -1998,7 +1998,7 @@ bool GitClient::synchronousForEachRefCmd(const FilePath &workingDirectory, QStri QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, args, silentFlags); *output = proc.stdOut(); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(args, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -2022,7 +2022,7 @@ bool GitClient::synchronousRemoteCmd(const FilePath &workingDirectory, QStringLi *errorMessage = stdErr; *output = proc.stdOut(); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(remoteArgs, workingDirectory, stdErr, errorMessage); return false; } @@ -2062,7 +2062,7 @@ QStringList GitClient::synchronousSubmoduleStatus(const FilePath &workingDirecto QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, {"submodule", "status"}, silentFlags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(tr("Cannot retrieve submodule status of \"%1\": %2") .arg(workingDirectory.toUserOutput(), proc.stdErr()), errorMessage); return QStringList(); @@ -2142,7 +2142,7 @@ QByteArray GitClient::synchronousShow(const FilePath &workingDirectory, const QS const QStringList arguments = {"show", decorateOption, noColorOption, "--no-patch", id}; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, flags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(arguments, workingDirectory, proc.stdErr(), nullptr); return {}; } @@ -2158,7 +2158,7 @@ bool GitClient::cleanList(const FilePath &workingDirectory, const QString &modul QtcProcess proc; vcsFullySynchronousExec(proc, directory, arguments, VcsCommand::ForceCLocale); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(arguments, directory, proc.stdErr(), errorMessage); return false; } @@ -2206,7 +2206,7 @@ bool GitClient::synchronousApplyPatch(const FilePath &workingDirectory, QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments); const QString stdErr = proc.stdErr(); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { if (!stdErr.isEmpty()) *errorMessage = tr("There were warnings while applying \"%1\" to \"%2\":\n%3") .arg(file, workingDirectory.toUserOutput(), stdErr); @@ -2347,7 +2347,7 @@ GitClient::StatusResult GitClient::gitStatus(const FilePath &workingDirectory, S if (output) *output = stdOut; - const bool statusRc = proc.result() == QtcProcess::FinishedWithSuccess; + const bool statusRc = proc.result() == ProcessResult::FinishedWithSuccess; const bool branchKnown = !stdOut.startsWith("## HEAD (no branch)\n"); // Is it something really fatal? if (!statusRc && !branchKnown) { @@ -2718,7 +2718,7 @@ bool GitClient::readDataFromCommit(const FilePath &repoDirectory, const QString QtcProcess proc; vcsFullySynchronousExec(proc, repoDirectory, arguments, silentFlags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { if (errorMessage) { *errorMessage = tr("Cannot retrieve last commit data of repository \"%1\".") .arg(repoDirectory.toUserOutput()); @@ -2974,7 +2974,7 @@ bool GitClient::addAndCommit(const FilePath &repositoryDirectory, QtcProcess proc; vcsSynchronousExec(proc, repositoryDirectory, arguments, VcsCommand::NoFullySync); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { VcsOutputWindow::appendMessage(msgCommitted(amendSHA1, commitCount)); GitPlugin::updateCurrentBranch(); return true; @@ -3116,7 +3116,7 @@ bool GitClient::executeAndHandleConflicts(const FilePath &workingDirectory, vcsSynchronousExec(proc, workingDirectory, arguments, flags); // Notify about changed files or abort the rebase. ConflictHandler::handleResponse(proc, workingDirectory, abortCommand); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } void GitClient::pull(const FilePath &workingDirectory, bool rebase) @@ -3175,7 +3175,7 @@ bool GitClient::synchronousSetTrackingBranch(const FilePath &workingDirectory, QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, {"branch", "--set-upstream-to=" + tracking, branch}); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } VcsBase::VcsCommand *GitClient::asyncUpstreamStatus(const FilePath &workingDirectory, @@ -3498,7 +3498,7 @@ bool GitClient::synchronousStashRemove(const FilePath &workingDirectory, const Q QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments); - if (proc.result() == QtcProcess::FinishedWithSuccess) { + if (proc.result() == ProcessResult::FinishedWithSuccess) { const QString output = proc.stdOut(); if (!output.isEmpty()) VcsOutputWindow::append(output); @@ -3517,7 +3517,7 @@ bool GitClient::synchronousStashList(const FilePath &workingDirectory, QList<Sta const QStringList arguments = {"stash", "list", noColorOption}; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, VcsCommand::ForceCLocale); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(arguments, workingDirectory, proc.stdErr(), errorMessage); return false; } @@ -3557,7 +3557,7 @@ QString GitClient::readOneLine(const FilePath &workingDirectory, const QStringLi QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments, silentFlags, vcsTimeoutS(), codec); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return QString(); return proc.stdOut().trimmed(); } @@ -3584,7 +3584,7 @@ unsigned GitClient::synchronousGitVersion(QString *errorMessage) const // run git --version QtcProcess proc; vcsSynchronousExec(proc, {}, {"--version"}, silentFlags); - if (proc.result() != QtcProcess::FinishedWithSuccess) { + if (proc.result() != ProcessResult::FinishedWithSuccess) { msgCannotRun(tr("Cannot determine Git version: %1").arg(proc.stdErr()), errorMessage); return 0; } diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp index f460cb9c7f3..4395c96bd12 100644 --- a/src/plugins/git/gitgrep.cpp +++ b/src/plugins/git/gitgrep.cpp @@ -204,13 +204,13 @@ public: proc.setTimeoutS(0); m_command->runCommand(proc, {m_vcsBinary, arguments}); switch (proc.result()) { - case QtcProcess::TerminatedAbnormally: - case QtcProcess::StartFailed: - case QtcProcess::Hang: + case ProcessResult::TerminatedAbnormally: + case ProcessResult::StartFailed: + case ProcessResult::Hang: fi.reportCanceled(); break; - case QtcProcess::FinishedWithSuccess: - case QtcProcess::FinishedWithError: + case ProcessResult::FinishedWithSuccess: + case ProcessResult::FinishedWithError: // When no results are found, git-grep exits with non-zero status. // Do not consider this as an error. break; diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 2dbcfeb56ff..74faef36ddb 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -61,6 +61,7 @@ #include <texteditor/texteditor.h> #include <utils/algorithm.h> +#include <utils/commandline.h> #include <utils/infobar.h> #include <utils/parameteraction.h> #include <utils/pathchooser.h> diff --git a/src/plugins/git/mergetool.cpp b/src/plugins/git/mergetool.cpp index b1da451b6b0..61c51749411 100644 --- a/src/plugins/git/mergetool.cpp +++ b/src/plugins/git/mergetool.cpp @@ -58,7 +58,8 @@ bool MergeTool::start(const FilePath &workingDirectory, const QStringList &files Environment env = Environment::systemEnvironment(); env.set("LANG", "C"); env.set("LANGUAGE", "C"); - m_process = new QtcProcess(ProcessMode::Writer); + m_process = new QtcProcess; + m_process->setProcessMode(ProcessMode::Writer); m_process->setWorkingDirectory(workingDirectory); m_process->setEnvironment(env); m_process->setProcessChannelMode(QProcess::MergedChannels); diff --git a/src/plugins/imageviewer/imageviewerfile.cpp b/src/plugins/imageviewer/imageviewerfile.cpp index 1353de9a491..0133cce05c6 100644 --- a/src/plugins/imageviewer/imageviewerfile.cpp +++ b/src/plugins/imageviewer/imageviewerfile.cpp @@ -30,7 +30,7 @@ #include <coreplugin/editormanager/documentmodel.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <QFileInfo> diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp index 30484fbd50a..e71882bd7da 100644 --- a/src/plugins/ios/iosconfigurations.cpp +++ b/src/plugins/ios/iosconfigurations.cpp @@ -100,7 +100,7 @@ const char profileTeamIdTag[] = "TeamIdentifier"; static const QString xcodePlistPath = QDir::homePath() + "/Library/Preferences/com.apple.dt.Xcode.plist"; static const QString provisioningProfileDirPath = QDir::homePath() + "/Library/MobileDevice/Provisioning Profiles"; -static Utils::Id deviceId(const QString &sdkName) +static Id deviceId(const QString &sdkName) { if (sdkName.startsWith("iphoneos", Qt::CaseInsensitive)) return Constants::IOS_DEVICE_TYPE; @@ -109,7 +109,7 @@ static Utils::Id deviceId(const QString &sdkName) return {}; } -static bool isSimulatorDeviceId(const Utils::Id &id) +static bool isSimulatorDeviceId(const Id &id) { return id == Constants::IOS_SIMULATOR_TYPE; } @@ -126,7 +126,7 @@ static QList<ClangToolChain *> clangToolChains(const Toolchains &toolChains) static QList<ClangToolChain *> autoDetectedIosToolChains() { const QList<ClangToolChain *> toolChains = clangToolChains(ToolChainManager::toolchains()); - return Utils::filtered(toolChains, [](ClangToolChain *toolChain) { + return filtered(toolChains, [](ClangToolChain *toolChain) { return toolChain->isAutoDetected() && (toolChain->displayName().startsWith("iphone") || toolChain->displayName().startsWith("Apple Clang")); // TODO tool chains should be marked directly @@ -138,15 +138,15 @@ static ToolChainPair findToolChainForPlatform(const XcodePlatform &platform, const QList<ClangToolChain *> &toolChains) { ToolChainPair platformToolChains; - auto toolchainMatch = [](ClangToolChain *toolChain, const Utils::FilePath &compilerPath, const QStringList &flags) { + auto toolchainMatch = [](ClangToolChain *toolChain, const FilePath &compilerPath, const QStringList &flags) { return compilerPath == toolChain->compilerCommand() && flags == toolChain->platformCodeGenFlags() && flags == toolChain->platformLinkerFlags(); }; - platformToolChains.first = Utils::findOrDefault(toolChains, std::bind(toolchainMatch, std::placeholders::_1, + platformToolChains.first = findOrDefault(toolChains, std::bind(toolchainMatch, std::placeholders::_1, platform.cCompilerPath, target.backendFlags)); - platformToolChains.second = Utils::findOrDefault(toolChains, std::bind(toolchainMatch, std::placeholders::_1, + platformToolChains.second = findOrDefault(toolChains, std::bind(toolchainMatch, std::placeholders::_1, platform.cxxCompilerPath, target.backendFlags)); return platformToolChains; @@ -169,8 +169,8 @@ static QHash<XcodePlatform::ToolchainTarget, ToolChainPair> findToolChains(const static QSet<Kit *> existingAutoDetectedIosKits() { - return Utils::toSet(Utils::filtered(KitManager::kits(), [](Kit *kit) -> bool { - Utils::Id deviceKind = DeviceTypeKitAspect::deviceTypeId(kit); + return toSet(filtered(KitManager::kits(), [](Kit *kit) -> bool { + Id deviceKind = DeviceTypeKitAspect::deviceTypeId(kit); return kit->isAutoDetected() && (deviceKind == Constants::IOS_DEVICE_TYPE || deviceKind == Constants::IOS_SIMULATOR_TYPE); })); @@ -182,8 +182,8 @@ static void printKits(const QSet<Kit *> &kits) qCDebug(kitSetupLog) << " -" << kit->displayName(); } -static void setupKit(Kit *kit, Utils::Id pDeviceType, const ToolChainPair& toolChains, - const QVariant &debuggerId, const Utils::FilePath &sdkPath, QtVersion *qtVersion) +static void setupKit(Kit *kit, Id pDeviceType, const ToolChainPair& toolChains, + const QVariant &debuggerId, const FilePath &sdkPath, QtVersion *qtVersion) { DeviceTypeKitAspect::setDeviceTypeId(kit, pDeviceType); if (toolChains.first) @@ -214,7 +214,7 @@ static void setupKit(Kit *kit, Utils::Id pDeviceType, const ToolChainPair& toolC SysRootKitAspect::setSysRoot(kit, sdkPath); } -static QVersionNumber findXcodeVersion(const Utils::FilePath &developerPath) +static QVersionNumber findXcodeVersion(const FilePath &developerPath) { const FilePath xcodeInfo = developerPath.parentDir().pathAppended("Info.plist"); if (xcodeInfo.exists()) { @@ -231,12 +231,12 @@ static QByteArray decodeProvisioningProfile(const QString &path) { QTC_ASSERT(!path.isEmpty(), return QByteArray()); - Utils::QtcProcess p; + QtcProcess p; p.setTimeoutS(3); // path is assumed to be valid file path to .mobileprovision p.setCommand({"openssl", {"smime", "-inform", "der", "-verify", "-in", path}}); p.runBlocking(); - if (p.result() != Utils::QtcProcess::FinishedWithSuccess) + if (p.result() != ProcessResult::FinishedWithSuccess) qCDebug(iosCommonLog) << "Reading signed provisioning file failed" << path; return p.stdOut().toLatin1(); } @@ -251,7 +251,7 @@ void IosConfigurations::updateAutomaticKitList() // target -> tool chain const auto targetToolChainHash = findToolChains(platforms); - const auto qtVersions = Utils::toSet(QtVersionManager::versions([](const QtVersion *v) { + const auto qtVersions = toSet(QtVersionManager::versions([](const QtVersion *v) { return v->isValid() && v->type() == Constants::IOSQT; })); @@ -264,8 +264,8 @@ void IosConfigurations::updateAutomaticKitList() QSet<Kit *> resultingKits; for (const XcodePlatform &platform : platforms) { for (const auto &sdk : platform.sdks) { - const auto targets = Utils::filtered(platform.targets, - [&sdk](const XcodePlatform::ToolchainTarget &target) { + const auto targets = filtered(platform.targets, + [&sdk](const XcodePlatform::ToolchainTarget &target) { return sdk.architectures.first() == target.architecture; }); if (targets.empty()) @@ -278,7 +278,7 @@ void IosConfigurations::updateAutomaticKitList() qCDebug(kitSetupLog) << " - No tool chain found"; continue; } - Utils::Id pDeviceType = deviceId(sdk.directoryName); + Id pDeviceType = deviceId(sdk.directoryName); if (!pDeviceType.isValid()) { qCDebug(kitSetupLog) << "Unsupported/Invalid device type" << sdk.directoryName; continue; @@ -286,7 +286,7 @@ void IosConfigurations::updateAutomaticKitList() for (QtVersion *qtVersion : qtVersions) { qCDebug(kitSetupLog) << " - Qt version:" << qtVersion->displayName(); - Kit *kit = Utils::findOrDefault(existingKits, [&pDeviceType, &platformToolchains, &qtVersion](const Kit *kit) { + Kit *kit = findOrDefault(existingKits, [&pDeviceType, &platformToolchains, &qtVersion](const Kit *kit) { // we do not compare the sdk (thus automatically upgrading it in place if a // new Xcode is used). Change? return DeviceTypeKitAspect::deviceTypeId(kit) == pDeviceType @@ -418,7 +418,7 @@ void IosConfigurations::updateSimulators() { // currently we have just one simulator DeviceManager *devManager = DeviceManager::instance(); - Utils::Id devId = Constants::IOS_SIMULATOR_DEVICE_ID; + Id devId = Constants::IOS_SIMULATOR_DEVICE_ID; IDevice::ConstPtr dev = devManager->find(devId); if (dev.isNull()) { dev = IDevice::ConstPtr(new IosSimulator(devId)); @@ -492,7 +492,7 @@ void IosConfigurations::loadProvisioningData(bool notify) } // Sort team id's to move the free provisioning teams at last of the list. - Utils::sort(teams, [](const QVariantMap &teamInfo1, const QVariantMap &teamInfo2) { + sort(teams, [](const QVariantMap &teamInfo1, const QVariantMap &teamInfo2) { return teamInfo1.value(freeTeamTag).toInt() < teamInfo2.value(freeTeamTag).toInt(); }); @@ -564,7 +564,7 @@ DevelopmentTeamPtr IosConfigurations::developmentTeam(const QString &teamID) QTC_CHECK(m_instance); m_instance->initializeProvisioningData(); return findOrDefault(m_instance->m_developerTeams, - Utils::equal(&DevelopmentTeam::identifier, teamID)); + equal(&DevelopmentTeam::identifier, teamID)); } const ProvisioningProfiles &IosConfigurations::provisioningProfiles() @@ -578,8 +578,8 @@ ProvisioningProfilePtr IosConfigurations::provisioningProfile(const QString &pro { QTC_CHECK(m_instance); m_instance->initializeProvisioningData(); - return Utils::findOrDefault(m_instance->m_provisioningProfiles, - Utils::equal(&ProvisioningProfile::identifier, profileID)); + return findOrDefault(m_instance->m_provisioningProfiles, + equal(&ProvisioningProfile::identifier, profileID)); } IosToolChainFactory::IosToolChainFactory() @@ -598,7 +598,7 @@ Toolchains IosToolChainFactory::autoDetect(const ToolchainDetector &detector) co for (const XcodePlatform::ToolchainTarget &target : platform.targets) { ToolChainPair platformToolchains = findToolChainForPlatform(platform, target, existingClangToolChains); - auto createOrAdd = [&](ClangToolChain *toolChain, Utils::Id l) { + auto createOrAdd = [&](ClangToolChain *toolChain, Id l) { if (!toolChain) { toolChain = new ClangToolChain; toolChain->setDetection(ToolChain::AutoDetection); diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp index 84bfa561027..49ae0a7cd20 100644 --- a/src/plugins/ios/iosprobe.cpp +++ b/src/plugins/ios/iosprobe.cpp @@ -68,7 +68,7 @@ void XcodeProbe::detectDeveloperPaths() selectedXcode.setTimeoutS(5); selectedXcode.setCommand({"/usr/bin/xcode-select", {"--print-path"}}); selectedXcode.runBlocking(); - if (selectedXcode.result() != QtcProcess::FinishedWithSuccess) + if (selectedXcode.result() != ProcessResult::FinishedWithSuccess) qCWarning(probeLog) << QString::fromLatin1("Could not detect selected Xcode using xcode-select"); else diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp index 0bf72b8a2f2..0334e11218c 100644 --- a/src/plugins/ios/simulatorcontrol.cpp +++ b/src/plugins/ios/simulatorcontrol.cpp @@ -88,7 +88,7 @@ static bool runCommand(const CommandLine &command, QString *stdOutput, QString * *stdOutput = p.stdOut(); if (allOutput) *allOutput = p.allOutput(); - return p.result() == QtcProcess::FinishedWithSuccess; + return p.result() == ProcessResult::FinishedWithSuccess; } static bool runSimCtlCommand(QStringList args, QString *output, QString *allOutput = nullptr) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 541ba0d3e21..d88c0ae86d2 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -57,7 +57,7 @@ #include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditorsettings.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcprocess.h> @@ -301,13 +301,18 @@ void Client::initialize() InitializeParams params; params.setCapabilities(m_clientCapabilities); params.setInitializationOptions(m_initializationOptions); - if (m_project) { + if (m_project) params.setRootUri(DocumentUri::fromFilePath(m_project->projectDirectory())); - params.setWorkSpaceFolders(Utils::transform(SessionManager::projects(), [](Project *pro) { - return WorkSpaceFolder(DocumentUri::fromFilePath(pro->projectDirectory()), - pro->displayName()); - })); - } + + const QList<WorkSpaceFolder> workspaces + = Utils::transform(SessionManager::projects(), [](Project *pro) { + return WorkSpaceFolder(DocumentUri::fromFilePath(pro->projectDirectory()), + pro->displayName()); + }); + if (workspaces.isEmpty()) + params.setWorkSpaceFolders(nullptr); + else + params.setWorkSpaceFolders(workspaces); InitializeRequest initRequest(params); initRequest.setResponseCallback([this](const InitializeRequest::Response &initResponse){ initializeCallback(initResponse); @@ -375,7 +380,7 @@ void Client::openDocument(TextEditor::TextDocument *document) const FilePath &filePath = document->filePath(); const QString method(DidOpenTextDocumentNotification::methodName); if (Utils::optional<bool> registered = m_dynamicCapabilities.isRegistered(method)) { - if (!registered.value()) + if (!*registered) return; const TextDocumentRegistrationOptions option( m_dynamicCapabilities.option(method).toObject()); @@ -385,7 +390,7 @@ void Client::openDocument(TextEditor::TextDocument *document) } } else if (Utils::optional<ServerCapabilities::TextDocumentSync> _sync = m_serverCapabilities.textDocumentSync()) { - if (auto options = Utils::get_if<TextDocumentSyncOptions>(&_sync.value())) { + if (auto options = Utils::get_if<TextDocumentSyncOptions>(&*_sync)) { if (!options->openClose().value_or(true)) return; } @@ -565,7 +570,7 @@ void Client::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *widget) const Id &id = TextEditor::TextEditorWidget::CodeSemanticsSelection; QList<QTextEdit::ExtraSelection> selections; const Utils::optional<DocumentHighlightsResult> &result = response.result(); - if (!result.has_value() || holds_alternative<std::nullptr_t>(result.value())) { + if (!result.has_value() || holds_alternative<std::nullptr_t>(*result)) { widget->setExtraSelections(id, selections); return; } @@ -573,7 +578,7 @@ void Client::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *widget) const QTextCharFormat &format = widget->textDocument()->fontSettings().toTextCharFormat(TextEditor::C_OCCURRENCES); QTextDocument *document = widget->document(); - for (const auto &highlight : get<QList<DocumentHighlight>>(result.value())) { + for (const auto &highlight : get<QList<DocumentHighlight>>(*result)) { QTextEdit::ExtraSelection selection{widget->textCursor(), format}; const int &start = highlight.range().start().toPositionInDocument(document); const int &end = highlight.range().end().toPositionInDocument(document); @@ -658,7 +663,7 @@ void Client::documentContentsSaved(TextEditor::TextDocument *document) bool includeText = false; const QString method(DidSaveTextDocumentNotification::methodName); if (Utils::optional<bool> registered = m_dynamicCapabilities.isRegistered(method)) { - sendMessage = registered.value(); + sendMessage = *registered; if (sendMessage) { const TextDocumentSaveRegistrationOptions option( m_dynamicCapabilities.option(method).toObject()); @@ -670,9 +675,9 @@ void Client::documentContentsSaved(TextEditor::TextDocument *document) } } else if (Utils::optional<ServerCapabilities::TextDocumentSync> _sync = m_serverCapabilities.textDocumentSync()) { - if (auto options = Utils::get_if<TextDocumentSyncOptions>(&_sync.value())) { + if (auto options = Utils::get_if<TextDocumentSyncOptions>(&*_sync)) { if (Utils::optional<SaveOptions> saveOptions = options->save()) - includeText = saveOptions.value().includeText().value_or(includeText); + includeText = saveOptions->includeText().value_or(includeText); } } if (!sendMessage) @@ -693,7 +698,7 @@ void Client::documentWillSave(Core::IDocument *document) bool sendMessage = false; const QString method(WillSaveTextDocumentNotification::methodName); if (Utils::optional<bool> registered = m_dynamicCapabilities.isRegistered(method)) { - sendMessage = registered.value(); + sendMessage = *registered; if (sendMessage) { const TextDocumentRegistrationOptions option(m_dynamicCapabilities.option(method)); if (option.isValid()) { @@ -703,7 +708,7 @@ void Client::documentWillSave(Core::IDocument *document) } } else if (Utils::optional<ServerCapabilities::TextDocumentSync> _sync = m_serverCapabilities.textDocumentSync()) { - if (auto options = Utils::get_if<TextDocumentSyncOptions>(&_sync.value())) + if (auto options = Utils::get_if<TextDocumentSyncOptions>(&*_sync)) sendMessage = options->willSave().value_or(sendMessage); } if (!sendMessage) @@ -723,7 +728,7 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document, const QString method(DidChangeTextDocumentNotification::methodName); TextDocumentSyncKind syncKind = m_serverCapabilities.textDocumentSyncKindHelper(); if (Utils::optional<bool> registered = m_dynamicCapabilities.isRegistered(method)) { - syncKind = registered.value() ? TextDocumentSyncKind::Full : TextDocumentSyncKind::None; + syncKind = *registered ? TextDocumentSyncKind::Full : TextDocumentSyncKind::None; if (syncKind != TextDocumentSyncKind::None) { const TextDocumentChangeRegistrationOptions option( m_dynamicCapabilities.option(method).toObject()); @@ -864,8 +869,20 @@ SymbolSupport &Client::symbolSupport() return m_symbolSupport; } +void Client::requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, + const LanguageServerProtocol::Diagnostic &diagnostic) +{ + requestCodeActions(uri, diagnostic.range(), {diagnostic}); +} + void Client::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> &diagnostics) { + requestCodeActions(uri, {}, diagnostics); +} + +void Client::requestCodeActions(const DocumentUri &uri, const Range &range, + const QList<Diagnostic> &diagnostics) +{ const Utils::FilePath fileName = uri.toFilePath(); TextEditor::TextDocument *doc = TextEditor::TextDocument::textDocumentForFilePath(fileName); if (!doc) @@ -876,10 +893,14 @@ void Client::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> context.setDiagnostics(diagnostics); codeActionParams.setContext(context); codeActionParams.setTextDocument(TextDocumentIdentifier(uri)); - Position start(0, 0); - const QTextBlock &lastBlock = doc->document()->lastBlock(); - Position end(lastBlock.blockNumber(), lastBlock.length() - 1); - codeActionParams.setRange(Range(start, end)); + if (range.isEmpty()) { + Position start(0, 0); + const QTextBlock &lastBlock = doc->document()->lastBlock(); + Position end(lastBlock.blockNumber(), lastBlock.length() - 1); + codeActionParams.setRange(Range(start, end)); + } else { + codeActionParams.setRange(range); + } CodeActionRequest request(codeActionParams); request.setResponseCallback( [uri, self = QPointer<Client>(this)](const CodeActionRequest::Response &response) { @@ -899,7 +920,7 @@ void Client::requestCodeActions(const CodeActionRequest &request) const QString method(CodeActionRequest::methodName); if (Utils::optional<bool> registered = m_dynamicCapabilities.isRegistered(method)) { - if (!registered.value()) + if (!*registered) return; const TextDocumentRegistrationOptions option( m_dynamicCapabilities.option(method).toObject()); @@ -920,9 +941,8 @@ void Client::handleCodeActionResponse(const CodeActionRequest::Response &respons { if (const Utils::optional<CodeActionRequest::Response::Error> &error = response.error()) log(*error); - if (const Utils::optional<CodeActionResult> &_result = response.result()) { - const CodeActionResult &result = _result.value(); - if (auto list = Utils::get_if<QList<Utils::variant<Command, CodeAction>>>(&result)) { + if (const Utils::optional<CodeActionResult> &result = response.result()) { + if (auto list = Utils::get_if<QList<Utils::variant<Command, CodeAction>>>(&*result)) { for (const Utils::variant<Command, CodeAction> &item : *list) { if (auto action = Utils::get_if<CodeAction>(&item)) updateCodeActionRefactoringMarker(this, *action, uri); @@ -1000,6 +1020,17 @@ void Client::projectClosed(ProjectExplorer::Project *project) } } +void Client::updateConfiguration(const QJsonValue &configuration) +{ + if (m_dynamicCapabilities.isRegistered(DidChangeConfigurationNotification::methodName) + .value_or(true)) { + DidChangeConfigurationParams params; + params.setSettings(configuration); + DidChangeConfigurationNotification notification(params); + sendContent(notification); + } +} + void Client::setSupportedLanguage(const LanguageFilter &filter) { m_languagFilter = filter; @@ -1089,6 +1120,31 @@ void Client::setCompletionAssistProvider(LanguageClientCompletionAssistProvider m_clientProviders.completionAssistProvider = provider; } +void Client::setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider) +{ + delete m_clientProviders.quickFixAssistProvider; + m_clientProviders.quickFixAssistProvider = provider; +} + +bool Client::supportsDocumentSymbols(const TextEditor::TextDocument *doc) const +{ + if (!doc) + return false; + DynamicCapabilities dc = dynamicCapabilities(); + if (dc.isRegistered(DocumentSymbolsRequest::methodName).value_or(false)) { + TextDocumentRegistrationOptions options(dc.option(DocumentSymbolsRequest::methodName)); + return !options.isValid() + || options.filterApplies(doc->filePath(), Utils::mimeTypeForName(doc->mimeType())); + } + const Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> &provider + = capabilities().documentSymbolProvider(); + if (!provider.has_value()) + return false; + if (Utils::holds_alternative<bool>(*provider)) + return Utils::get<bool>(*provider); + return true; +} + void Client::start() { LanguageClientManager::addClient(this); @@ -1169,6 +1225,11 @@ void Client::log(const QString &message) const } } +TextEditor::RefactoringChangesData *Client::createRefactoringChangesBackend() const +{ + return new TextEditor::RefactoringChangesData; +} + const ServerCapabilities &Client::capabilities() const { return m_serverCapabilities; @@ -1208,7 +1269,7 @@ LanguageClientValue<MessageActionItem> Client::showMessageBox( } QHash<QAbstractButton *, MessageActionItem> itemForButton; if (const Utils::optional<QList<MessageActionItem>> actions = message.actions()) { - for (const MessageActionItem &action : actions.value()) + for (const MessageActionItem &action : *actions) itemForButton.insert(box->addButton(action.title(), QMessageBox::InvalidRole), action); } box->exec(); @@ -1477,45 +1538,45 @@ void Client::initializeCallback(const InitializeRequest::Response &initResponse) { QTC_ASSERT(m_state == InitializeRequested, return); if (optional<ResponseError<InitializeError>> error = initResponse.error()) { - if (error.value().data().has_value() && error.value().data().value().retry()) { - const QString title(tr("Language Server \"%1\" Initialize Error").arg(m_displayName)); - auto result = QMessageBox::warning(Core::ICore::dialogParent(), - title, - error.value().message(), - QMessageBox::Retry | QMessageBox::Cancel, - QMessageBox::Retry); - if (result == QMessageBox::Retry) { - m_state = Uninitialized; - initialize(); - return; + if (Utils::optional<InitializeError> data = error->data()) { + if (data->retry()) { + const QString title(tr("Language Server \"%1\" Initialize Error").arg(m_displayName)); + auto result = QMessageBox::warning(Core::ICore::dialogParent(), + title, + error->message(), + QMessageBox::Retry | QMessageBox::Cancel, + QMessageBox::Retry); + if (result == QMessageBox::Retry) { + m_state = Uninitialized; + initialize(); + return; + } } } - setError(tr("Initialize error: ") + error.value().message()); + setError(tr("Initialize error: ") + error->message()); emit finished(); return; } - const optional<InitializeResult> &_result = initResponse.result(); - if (!_result.has_value()) {// continue on ill formed result - log(tr("No initialize result.")); - } else { - const InitializeResult &result = _result.value(); - if (!result.isValid()) { // continue on ill formed result - log(QJsonDocument(result).toJson(QJsonDocument::Indented) + '\n' + if (const optional<InitializeResult> &result = initResponse.result()) { + if (!result->isValid()) { // continue on ill formed result + log(QJsonDocument(*result).toJson(QJsonDocument::Indented) + '\n' + tr("Initialize result is not valid")); } - const Utils::optional<ServerInfo> serverInfo = result.serverInfo(); + const Utils::optional<ServerInfo> serverInfo = result->serverInfo(); if (serverInfo) { if (!serverInfo->isValid()) { - log(QJsonDocument(result).toJson(QJsonDocument::Indented) + '\n' + log(QJsonDocument(*result).toJson(QJsonDocument::Indented) + '\n' + tr("Server Info is not valid")); } else { m_serverName = serverInfo->name(); if (const Utils::optional<QString> version = serverInfo->version()) - m_serverVersion = version.value(); + m_serverVersion = *version; } } - m_serverCapabilities = result.capabilities(); + m_serverCapabilities = result->capabilities(); + } else { + log(tr("No initialize result.")); } if (auto completionProvider = qobject_cast<LanguageClientCompletionAssistProvider *>( @@ -1549,6 +1610,12 @@ void Client::initializeCallback(const InitializeRequest::Response &initResponse) } } + if (const BaseSettings *settings = LanguageClientManager::settingForClient(this)) { + const QJsonValue configuration = settings->configuration(); + if (!configuration.isNull()) + updateConfiguration(configuration); + } + for (TextEditor::TextDocument *doc : m_postponedDocuments) openDocument(doc); m_postponedDocuments.clear(); @@ -1579,10 +1646,10 @@ bool Client::sendWorkspceFolderChanges() const return true; } if (auto workspace = m_serverCapabilities.workspace()) { - if (auto folder = workspace.value().workspaceFolders()) { - if (folder.value().supported().value_or(false)) { + if (auto folder = workspace->workspaceFolders()) { + if (folder->supported().value_or(false)) { // holds either the Id for deregistration or whether it is registered - auto notification = folder.value().changeNotifications().value_or(false); + auto notification = folder->changeNotifications().value_or(false); return holds_alternative<QString>(notification) || (holds_alternative<bool>(notification) && get<bool>(notification)); } diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index f1e9c581f48..3327437d41f 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -165,9 +165,12 @@ public: ProjectExplorer::Project *project() const; virtual void projectOpened(ProjectExplorer::Project *project); virtual void projectClosed(ProjectExplorer::Project *project); + void updateConfiguration(const QJsonValue &configuration); // commands void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, + const LanguageServerProtocol::Diagnostic &diagnostic); + void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, const QList<LanguageServerProtocol::Diagnostic> &diagnostics); void requestCodeActions(const LanguageServerProtocol::CodeActionRequest &request); void handleCodeActionResponse(const LanguageServerProtocol::CodeActionRequest::Response &response, @@ -192,6 +195,8 @@ public: LanguageServerProtocol::SymbolStringifier symbolStringifier() const; void setSnippetsGroup(const QString &group); void setCompletionAssistProvider(LanguageClientCompletionAssistProvider *provider); + void setQuickFixAssistProvider(LanguageClientQuickFixProvider *provider); + virtual bool supportsDocumentSymbols(const TextEditor::TextDocument *doc) const; // logging enum class LogTarget { Console, Ui }; @@ -206,6 +211,9 @@ public: using CustomInspectorTabs = QList<CustomInspectorTab>; virtual const CustomInspectorTabs createCustomInspectorTabs() { return {}; } + // Caller takes ownership + virtual TextEditor::RefactoringChangesData *createRefactoringChangesBackend() const; + signals: void initialized(const LanguageServerProtocol::ServerCapabilities &capabilities); void capabilitiesChanged(const DynamicCapabilities &capabilities); @@ -247,6 +255,9 @@ private: void requestDocumentHighlightsNow(TextEditor::TextEditorWidget *widget); LanguageServerProtocol::SemanticRequestTypes supportedSemanticRequests(TextEditor::TextDocument *document) const; void handleSemanticTokens(const LanguageServerProtocol::SemanticTokens &tokens); + void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, + const LanguageServerProtocol::Range &range, + const QList<LanguageServerProtocol::Diagnostic> &diagnostics); void documentClosed(Core::IDocument *document); virtual void handleDocumentClosed(TextEditor::TextDocument *) {} diff --git a/src/plugins/languageclient/documentsymbolcache.cpp b/src/plugins/languageclient/documentsymbolcache.cpp index 1a46048d345..916fa8d688f 100644 --- a/src/plugins/languageclient/documentsymbolcache.cpp +++ b/src/plugins/languageclient/documentsymbolcache.cpp @@ -40,7 +40,13 @@ DocumentSymbolCache::DocumentSymbolCache(Client *client) { auto connectDocument = [this](Core::IDocument *document) { connect(document, &Core::IDocument::contentsChanged, this, [document, this]() { + const auto uri = DocumentUri::fromFilePath(document->filePath()); m_cache.remove(DocumentUri::fromFilePath(document->filePath())); + auto requestIdIt = m_runningRequests.find(uri); + if (requestIdIt != m_runningRequests.end()) { + m_client->cancelRequest(requestIdIt.value()); + m_runningRequests.erase(requestIdIt); + } }); }; @@ -67,6 +73,13 @@ void DocumentSymbolCache::requestSymbols(const DocumentUri &uri, Schedule schedu } } +bool clientSupportsDocumentSymbols(const Client *client, const DocumentUri &uri) +{ + QTC_ASSERT(client, return false); + const auto doc = TextEditor::TextDocument::textDocumentForFilePath(uri.toFilePath()); + return client->supportsDocumentSymbols(doc); +} + void DocumentSymbolCache::requestSymbolsImpl() { if (!m_client->reachable()) { @@ -80,6 +93,11 @@ void DocumentSymbolCache::requestSymbolsImpl() continue; } + if (!LanguageClient::clientSupportsDocumentSymbols(m_client, uri)) { + emit gotSymbols(uri, nullptr); + continue; + } + const DocumentSymbolParams params((TextDocumentIdentifier(uri))); DocumentSymbolsRequest request(params); request.setResponseCallback([uri, self = QPointer<DocumentSymbolCache>(this)]( @@ -87,6 +105,7 @@ void DocumentSymbolCache::requestSymbolsImpl() if (self) self->handleResponse(uri, response); }); + m_runningRequests[uri] = request.id(); m_client->sendContent(request); } m_compressedUris.clear(); @@ -95,9 +114,10 @@ void DocumentSymbolCache::requestSymbolsImpl() void DocumentSymbolCache::handleResponse(const DocumentUri &uri, const DocumentSymbolsRequest::Response &response) { + m_runningRequests.remove(uri); if (Utils::optional<DocumentSymbolsRequest::Response::Error> error = response.error()) { if (m_client) - m_client->log(error.value()); + m_client->log(*error); } const DocumentSymbolsResult &symbols = response.result().value_or(DocumentSymbolsResult()); m_cache[uri] = symbols; diff --git a/src/plugins/languageclient/documentsymbolcache.h b/src/plugins/languageclient/documentsymbolcache.h index ada0953c071..8ae17bbd6df 100644 --- a/src/plugins/languageclient/documentsymbolcache.h +++ b/src/plugins/languageclient/documentsymbolcache.h @@ -60,6 +60,7 @@ private: const LanguageServerProtocol::DocumentSymbolsRequest::Response &response); QMap<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::DocumentSymbolsResult> m_cache; + QMap<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::MessageId> m_runningRequests; Client *m_client = nullptr; QTimer m_compressionTimer; QSet<LanguageServerProtocol::DocumentUri> m_compressedUris; diff --git a/src/plugins/languageclient/languageclientcompletionassist.cpp b/src/plugins/languageclient/languageclientcompletionassist.cpp index 3cec5a2e491..7024445aa06 100644 --- a/src/plugins/languageclient/languageclientcompletionassist.cpp +++ b/src/plugins/languageclient/languageclientcompletionassist.cpp @@ -66,7 +66,7 @@ bool LanguageClientCompletionItem::implicitlyApplies() const bool LanguageClientCompletionItem::prematurelyApplies(const QChar &typedCharacter) const { - if (m_item.commitCharacters().has_value() && m_item.commitCharacters().value().contains(typedCharacter)) { + if (m_item.commitCharacters() && m_item.commitCharacters()->contains(typedCharacter)) { m_triggeredCommitCharacter = typedCharacter; return true; } @@ -194,10 +194,8 @@ bool LanguageClientCompletionItem::hasSortText() const QString LanguageClientCompletionItem::filterText() const { - if (m_filterText.isEmpty()) { - const Utils::optional<QString> filterText = m_item.filterText(); - m_filterText = filterText.has_value() ? filterText.value() : m_item.label(); - } + if (m_filterText.isEmpty()) + m_filterText = m_item.filterText().value_or(m_item.label()); return m_filterText; } @@ -211,7 +209,7 @@ bool LanguageClientCompletionItem::isPerfectMatch(int pos, QTextDocument *doc) c QTC_ASSERT(doc, return false); using namespace Utils::Text; if (auto additionalEdits = m_item.additionalTextEdits()) { - if (!additionalEdits.value().isEmpty()) + if (!additionalEdits->isEmpty()) return false; } if (isSnippet()) @@ -393,7 +391,7 @@ void LanguageClientCompletionAssistProcessor::cancel() { if (m_currentRequest.has_value()) { if (m_client) { - m_client->cancelRequest(m_currentRequest.value()); + m_client->cancelRequest(*m_currentRequest); m_client->removeAssistProcessor(this); } m_currentRequest.reset(); @@ -410,7 +408,7 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse( m_currentRequest.reset(); QTC_ASSERT(m_client, setAsyncProposalAvailable(nullptr); return); if (auto error = response.error()) - m_client->log(error.value()); + m_client->log(*error); const Utils::optional<CompletionResult> &result = response.result(); if (!result || Utils::holds_alternative<std::nullptr_t>(*result)) { diff --git a/src/plugins/languageclient/languageclientformatter.cpp b/src/plugins/languageclient/languageclientformatter.cpp index 6233d79bcc5..cfb4fd4fac2 100644 --- a/src/plugins/languageclient/languageclientformatter.cpp +++ b/src/plugins/languageclient/languageclientformatter.cpp @@ -30,7 +30,7 @@ #include <texteditor/tabsettings.h> #include <texteditor/textdocument.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QTextDocument> @@ -77,7 +77,7 @@ QFutureWatcher<ChangeSet> *LanguageClientFormatter::format( const DynamicCapabilities dynamicCapabilities = m_client->dynamicCapabilities(); const QString method(DocumentRangeFormattingRequest::methodName); if (optional<bool> registered = dynamicCapabilities.isRegistered(method)) { - if (!registered.value()) + if (!*registered) return nullptr; const TextDocumentRegistrationOptions option(dynamicCapabilities.option(method).toObject()); if (option.isValid() diff --git a/src/plugins/languageclient/languageclientfunctionhint.cpp b/src/plugins/languageclient/languageclientfunctionhint.cpp index f554d4a3b0e..1e7c6dc551f 100644 --- a/src/plugins/languageclient/languageclientfunctionhint.cpp +++ b/src/plugins/languageclient/languageclientfunctionhint.cpp @@ -103,7 +103,7 @@ IAssistProposal *FunctionHintProcessor::perform(const AssistInterface *interface void FunctionHintProcessor::cancel() { if (running()) { - m_client->cancelRequest(m_currentRequest.value()); + m_client->cancelRequest(*m_currentRequest); m_client->removeAssistProcessor(this); m_currentRequest.reset(); } @@ -113,7 +113,7 @@ void FunctionHintProcessor::handleSignatureResponse(const SignatureHelpRequest:: { m_currentRequest.reset(); if (auto error = response.error()) - m_client->log(error.value()); + m_client->log(*error); m_client->removeAssistProcessor(this); auto result = response.result().value_or(LanguageClientValue<SignatureHelp>()); if (result.isNull()) { diff --git a/src/plugins/languageclient/languageclienthoverhandler.cpp b/src/plugins/languageclient/languageclienthoverhandler.cpp index 3fc137f9387..6cb306fe435 100644 --- a/src/plugins/languageclient/languageclienthoverhandler.cpp +++ b/src/plugins/languageclient/languageclienthoverhandler.cpp @@ -29,7 +29,7 @@ #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/tooltip/tooltip.h> @@ -58,7 +58,8 @@ void HoverHandler::setHelpItem(const LanguageServerProtocol::MessageId &msgId, const Core::HelpItem &help) { if (msgId == m_response.id()) { - setContent(m_response.result().value().content()); + if (Utils::optional<Hover> result = m_response.result()) + setContent(result->content()); m_response = {}; setLastHelpItemIdentified(help); m_report(priority()); @@ -95,7 +96,7 @@ void HoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget, sendMessage = Utils::get<bool>(*provider); if (Utils::optional<bool> registered = m_client->dynamicCapabilities().isRegistered( HoverRequest::methodName)) { - sendMessage = registered.value(); + sendMessage = *registered; if (sendMessage) { const TextDocumentRegistrationOptions option( m_client->dynamicCapabilities().option(HoverRequest::methodName).toObject()); @@ -126,7 +127,7 @@ void HoverHandler::handleResponse(const HoverRequest::Response &response) m_currentRequest.reset(); if (Utils::optional<HoverRequest::Response::Error> error = response.error()) { if (m_client) - m_client->log(error.value()); + m_client->log(*error); } if (Utils::optional<Hover> result = response.result()) { if (m_helpItemProvider) { @@ -134,7 +135,7 @@ void HoverHandler::handleResponse(const HoverRequest::Response &response) m_helpItemProvider(response, m_uri); return; } - setContent(result.value().content()); + setContent(result->content()); } m_report(priority()); } diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp index b2ac3836be9..a22ea669767 100644 --- a/src/plugins/languageclient/languageclientinterface.cpp +++ b/src/plugins/languageclient/languageclientinterface.cpp @@ -88,8 +88,9 @@ void BaseClientInterface::parseData(const QByteArray &data) } StdIOClientInterface::StdIOClientInterface() - : m_process(ProcessMode::Writer) { + m_process.setProcessMode(ProcessMode::Writer); + connect(&m_process, &QtcProcess::readyReadStandardError, this, &StdIOClientInterface::readError); connect(&m_process, &QtcProcess::readyReadStandardOutput, diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 992138f06a1..762bac6e49f 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -42,7 +42,6 @@ #include <texteditor/textmark.h> #include <utils/algorithm.h> #include <utils/executeondestruction.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/theme/theme.h> #include <utils/utilsicons.h> diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp index 27f4ac263cf..f4ebb0ecf06 100644 --- a/src/plugins/languageclient/languageclientoutline.cpp +++ b/src/plugins/languageclient/languageclientoutline.cpp @@ -28,18 +28,19 @@ #include "languageclientmanager.h" #include "languageclientutils.h" -#include <coreplugin/find/itemviewfind.h> #include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/find/itemviewfind.h> #include <languageserverprotocol/languagefeatures.h> #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> #include <utils/itemviews.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/treemodel.h> #include <utils/treeviewcombobox.h> #include <utils/utilsicons.h> +#include <QAction> #include <QBoxLayout> +#include <QSortFilterProxyModel> using namespace LanguageServerProtocol; @@ -128,6 +129,10 @@ public: public: QList<QAction *> filterMenuActions() const override; void setCursorSynchronization(bool syncWithCursor) override; + void setSorted(bool) override; + bool isSorted() const override; + void restoreSettings(const QVariantMap &map) override; + QVariantMap settings() const override; private: void handleResponse(const DocumentUri &uri, const DocumentSymbolsResult &response); @@ -138,9 +143,11 @@ private: QPointer<Client> m_client; QPointer<TextEditor::BaseTextEditor> m_editor; LanguageClientOutlineModel m_model; + QSortFilterProxyModel m_proxyModel; Utils::TreeView m_view; DocumentUri m_uri; bool m_sync = false; + bool m_sorted = false; }; LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client, @@ -167,7 +174,8 @@ LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client, layout->addWidget(Core::ItemViewFind::createSearchableWrapper(&m_view)); setLayout(layout); m_model.setSymbolStringifier(m_client->symbolStringifier()); - m_view.setModel(&m_model); + m_proxyModel.setSourceModel(&m_model); + m_view.setModel(&m_proxyModel); m_view.setHeaderHidden(true); m_view.setExpandsOnDoubleClick(false); m_view.setFrameStyle(QFrame::NoFrame); @@ -192,6 +200,27 @@ void LanguageClientOutlineWidget::setCursorSynchronization(bool syncWithCursor) updateSelectionInTree(m_editor->textCursor()); } +void LanguageClientOutlineWidget::setSorted(bool sorted) +{ + m_sorted = sorted; + m_proxyModel.sort(sorted ? 0 : -1); +} + +bool LanguageClientOutlineWidget::isSorted() const +{ + return m_sorted; +} + +void LanguageClientOutlineWidget::restoreSettings(const QVariantMap &map) +{ + setSorted(map.value(QString("LspOutline.Sort"), false).toBool()); +} + +QVariantMap LanguageClientOutlineWidget::settings() const +{ + return {{QString("LspOutline.Sort"), m_sorted}}; +} + void LanguageClientOutlineWidget::handleResponse(const DocumentUri &uri, const DocumentSymbolsResult &result) { @@ -210,23 +239,36 @@ void LanguageClientOutlineWidget::handleResponse(const DocumentUri &uri, void LanguageClientOutlineWidget::updateTextCursor(const QModelIndex &proxyIndex) { - LanguageClientOutlineItem *item = m_model.itemForIndex(proxyIndex); + LanguageClientOutlineItem *item = m_model.itemForIndex(m_proxyModel.mapToSource(proxyIndex)); const Position &pos = item->pos(); // line has to be 1 based, column 0 based! m_editor->editorWidget()->gotoLine(pos.line() + 1, pos.character(), true, true); } -void LanguageClientOutlineWidget::updateSelectionInTree(const QTextCursor ¤tCursor) +static LanguageClientOutlineItem *itemForCursor(const LanguageClientOutlineModel &m_model, + const QTextCursor &cursor) { - QItemSelection selection; - const Position pos(currentCursor); - m_model.forAllItems([&](const LanguageClientOutlineItem *item) { - if (item->contains(pos)) - selection.select(m_model.indexForItem(item), m_model.indexForItem(item)); + const Position pos(cursor); + LanguageClientOutlineItem *result = nullptr; + m_model.forAllItems([&](LanguageClientOutlineItem *candidate){ + if (!candidate->contains(pos)) + return; + if (result && candidate->range().contains(result->range())) + return; // skip item if the range is equal or bigger than the previous found range + result = candidate; }); - m_view.selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); - if (!selection.isEmpty()) - m_view.scrollTo(selection.indexes().first()); + return result; +} + +void LanguageClientOutlineWidget::updateSelectionInTree(const QTextCursor ¤tCursor) +{ + if (LanguageClientOutlineItem *item = itemForCursor(m_model, currentCursor)) { + const QModelIndex index = m_proxyModel.mapFromSource(m_model.indexForItem(item)); + m_view.selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); + m_view.scrollTo(index); + } else { + m_view.clearSelection(); + } } void LanguageClientOutlineWidget::onItemActivated(const QModelIndex &index) @@ -238,46 +280,29 @@ void LanguageClientOutlineWidget::onItemActivated(const QModelIndex &index) m_editor->widget()->setFocus(); } -bool LanguageClientOutlineWidgetFactory::clientSupportsDocumentSymbols( - const Client *client, const TextEditor::TextDocument *doc) -{ - if (!client) - return false; - DynamicCapabilities dc = client->dynamicCapabilities(); - if (dc.isRegistered(DocumentSymbolsRequest::methodName).value_or(false)) { - TextDocumentRegistrationOptions options(dc.option(DocumentSymbolsRequest::methodName)); - return !options.isValid() - || options.filterApplies(doc->filePath(), Utils::mimeTypeForName(doc->mimeType())); - } - const Utils::optional<Utils::variant<bool, WorkDoneProgressOptions>> &provider - = client->capabilities().documentSymbolProvider(); - if (!provider.has_value()) - return false; - if (Utils::holds_alternative<bool>(*provider)) - return Utils::get<bool>(*provider); - return true; -} - bool LanguageClientOutlineWidgetFactory::supportsEditor(Core::IEditor *editor) const { - auto doc = qobject_cast<TextEditor::TextDocument *>(editor->document()); - if (!doc) - return false; - return clientSupportsDocumentSymbols(LanguageClientManager::clientForDocument(doc), doc); + if (auto doc = qobject_cast<TextEditor::TextDocument *>(editor->document())) { + if (Client *client = LanguageClientManager::clientForDocument(doc)) + return client->supportsDocumentSymbols(doc); + } + return false; } TextEditor::IOutlineWidget *LanguageClientOutlineWidgetFactory::createWidget(Core::IEditor *editor) { auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor); QTC_ASSERT(textEditor, return nullptr); - Client *client = LanguageClientManager::clientForDocument(textEditor->textDocument()); - if (!client || !clientSupportsDocumentSymbols(client, textEditor->textDocument())) - return nullptr; - return new LanguageClientOutlineWidget(client, textEditor); + if (Client *client = LanguageClientManager::clientForDocument(textEditor->textDocument())) { + if (client->supportsDocumentSymbols(textEditor->textDocument())) + return new LanguageClientOutlineWidget(client, textEditor); + } + return nullptr; } class OutlineComboBox : public Utils::TreeViewComboBox { + Q_DECLARE_TR_FUNCTIONS(LanguageClient::OutlineComboBox) public: OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor); @@ -286,23 +311,21 @@ private: void updateEntry(); void activateEntry(); void documentUpdated(TextEditor::TextDocument *document); + void setSorted(bool sorted); LanguageClientOutlineModel m_model; + QSortFilterProxyModel m_proxyModel; QPointer<Client> m_client; TextEditor::TextEditorWidget *m_editorWidget; const DocumentUri m_uri; }; -Utils::TreeViewComboBox *LanguageClientOutlineWidgetFactory::createComboBox(Client *client, - Core::IEditor *editor) +Utils::TreeViewComboBox *LanguageClientOutlineWidgetFactory::createComboBox( + Client *client, TextEditor::BaseTextEditor *editor) { - auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor); - QTC_ASSERT(textEditor, return nullptr); - TextEditor::TextDocument *document = textEditor->textDocument(); - if (!client || !clientSupportsDocumentSymbols(client, document)) - return nullptr; - - return new OutlineComboBox(client, textEditor); + if (client && client->supportsDocumentSymbols(editor->textDocument())) + return new OutlineComboBox(client, editor); + return nullptr; } OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *editor) @@ -311,19 +334,29 @@ OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *edi , m_uri(DocumentUri::fromFilePath(editor->document()->filePath())) { m_model.setSymbolStringifier(client->symbolStringifier()); - setModel(&m_model); + m_proxyModel.setSourceModel(&m_model); + const bool sorted = LanguageClientSettings::outlineComboBoxIsSorted(); + m_proxyModel.sort(sorted ? 0 : -1); + setModel(&m_proxyModel); setMinimumContentsLength(13); QSizePolicy policy = sizePolicy(); policy.setHorizontalPolicy(QSizePolicy::Expanding); setSizePolicy(policy); setMaxVisibleItems(40); + setContextMenuPolicy(Qt::ActionsContextMenu); + auto sortAction = new QAction(tr("Sort Alphabetically"), this); + sortAction->setCheckable(true); + sortAction->setChecked(sorted); + addAction(sortAction); + connect(client->documentSymbolCache(), &DocumentSymbolCache::gotSymbols, this, &OutlineComboBox::updateModel); connect(client, &Client::documentUpdated, this, &OutlineComboBox::documentUpdated); connect(m_editorWidget, &TextEditor::TextEditorWidget::cursorPositionChanged, this, &OutlineComboBox::updateEntry); connect(this, QOverload<int>::of(&QComboBox::activated), this, &OutlineComboBox::activateEntry); + connect(sortAction, &QAction::toggled, this, &OutlineComboBox::setSorted); documentUpdated(editor->textDocument()); } @@ -346,22 +379,13 @@ void OutlineComboBox::updateModel(const DocumentUri &resultUri, const DocumentSy void OutlineComboBox::updateEntry() { - const Position pos(m_editorWidget->textCursor()); - LanguageClientOutlineItem *itemForCursor = nullptr; - m_model.forAllItems([&](LanguageClientOutlineItem *candidate){ - if (!candidate->contains(pos)) - return; - if (itemForCursor && candidate->range().contains(itemForCursor->range())) - return; // skip item if the range is equal or bigger than the previous found range - itemForCursor = candidate; - }); - if (itemForCursor) - setCurrentIndex(m_model.indexForItem(itemForCursor)); + if (LanguageClientOutlineItem *item = itemForCursor(m_model, m_editorWidget->textCursor())) + setCurrentIndex(m_proxyModel.mapFromSource(m_model.indexForItem(item))); } void OutlineComboBox::activateEntry() { - const QModelIndex modelIndex = view()->currentIndex(); + const QModelIndex modelIndex = m_proxyModel.mapToSource(view()->currentIndex()); if (modelIndex.isValid()) { const Position &pos = m_model.itemForIndex(modelIndex)->pos(); Core::EditorManager::cutForwardNavigationHistory(); @@ -378,4 +402,10 @@ void OutlineComboBox::documentUpdated(TextEditor::TextDocument *document) m_client->documentSymbolCache()->requestSymbols(m_uri, Schedule::Delayed); } +void OutlineComboBox::setSorted(bool sorted) +{ + LanguageClientSettings::setOutlineComboBoxSorted(sorted); + m_proxyModel.sort(sorted ? 0 : -1); +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientoutline.h b/src/plugins/languageclient/languageclientoutline.h index 2384ea7f6ab..f397fe701b9 100644 --- a/src/plugins/languageclient/languageclientoutline.h +++ b/src/plugins/languageclient/languageclientoutline.h @@ -27,7 +27,10 @@ #include <texteditor/ioutlinewidget.h> -namespace TextEditor { class TextDocument; } +namespace TextEditor { +class TextDocument; +class BaseTextEditor; +} // namespace TextEditor namespace Utils { class TreeViewComboBox; } namespace LanguageClient { @@ -39,13 +42,12 @@ class LanguageClientOutlineWidgetFactory : public TextEditor::IOutlineWidgetFact public: using IOutlineWidgetFactory::IOutlineWidgetFactory; - static Utils::TreeViewComboBox *createComboBox(Client *client, Core::IEditor *editor); - static bool clientSupportsDocumentSymbols(const Client *client, - const TextEditor::TextDocument *doc); + static Utils::TreeViewComboBox *createComboBox(Client *client, TextEditor::BaseTextEditor *editor); // IOutlineWidgetFactory interface public: bool supportsEditor(Core::IEditor *editor) const override; TextEditor::IOutlineWidget *createWidget(Core::IEditor *editor) override; + bool supportsSorting() const override { return true; } }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientquickfix.cpp b/src/plugins/languageclient/languageclientquickfix.cpp index 93d85b15f21..08b96946684 100644 --- a/src/plugins/languageclient/languageclientquickfix.cpp +++ b/src/plugins/languageclient/languageclientquickfix.cpp @@ -30,7 +30,6 @@ #include <texteditor/codeassist/assistinterface.h> #include <texteditor/codeassist/genericproposal.h> -#include <texteditor/codeassist/iassistprocessor.h> #include <texteditor/quickfix.h> @@ -74,22 +73,6 @@ private: QPointer<Client> m_client; }; -class LanguageClientQuickFixAssistProcessor : public IAssistProcessor -{ -public: - explicit LanguageClientQuickFixAssistProcessor(Client *client) : m_client(client) {} - bool running() override { return m_currentRequest.has_value(); } - IAssistProposal *perform(const AssistInterface *interface) override; - void cancel() override; - -private: - void handleCodeActionResponse(const CodeActionRequest::Response &response); - - QSharedPointer<const AssistInterface> m_assistInterface; - Client *m_client = nullptr; // not owned - Utils::optional<MessageId> m_currentRequest; -}; - IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInterface *interface) { m_assistInterface = QSharedPointer<const AssistInterface>(interface); @@ -125,22 +108,20 @@ IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInte void LanguageClientQuickFixAssistProcessor::cancel() { if (running()) { - m_client->cancelRequest(m_currentRequest.value()); + m_client->cancelRequest(*m_currentRequest); m_client->removeAssistProcessor(this); m_currentRequest.reset(); } } -void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse( - const CodeActionRequest::Response &response) +void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse(const CodeActionRequest::Response &response) { m_currentRequest.reset(); if (const Utils::optional<CodeActionRequest::Response::Error> &error = response.error()) m_client->log(*error); QuickFixOperations ops; - if (const Utils::optional<CodeActionResult> &_result = response.result()) { - const CodeActionResult &result = _result.value(); - if (auto list = Utils::get_if<QList<Utils::variant<Command, CodeAction>>>(&result)) { + if (const Utils::optional<CodeActionResult> &result = response.result()) { + if (auto list = Utils::get_if<QList<Utils::variant<Command, CodeAction>>>(&*result)) { for (const Utils::variant<Command, CodeAction> &item : *list) { if (auto action = Utils::get_if<CodeAction>(&item)) ops << new CodeActionQuickFixOperation(*action, m_client); @@ -150,6 +131,11 @@ void LanguageClientQuickFixAssistProcessor::handleCodeActionResponse( } } m_client->removeAssistProcessor(this); + handleProposalReady(ops); +} + +void LanguageClientQuickFixAssistProcessor::handleProposalReady(const QuickFixOperations &ops) +{ setAsyncProposalAvailable(GenericProposal::createProposal(m_assistInterface.data(), ops)); } diff --git a/src/plugins/languageclient/languageclientquickfix.h b/src/plugins/languageclient/languageclientquickfix.h index 955f12bc87e..165760b44e3 100644 --- a/src/plugins/languageclient/languageclientquickfix.h +++ b/src/plugins/languageclient/languageclientquickfix.h @@ -28,12 +28,15 @@ #include "languageclient_global.h" #include <texteditor/codeassist/iassistprovider.h> +#include <texteditor/codeassist/iassistprocessor.h> #include <texteditor/quickfix.h> #include <languageserverprotocol/languagefeatures.h> #include <QPointer> +namespace TextEditor { class IAssistProposal; } + namespace LanguageClient { class Client; @@ -56,8 +59,32 @@ public: IAssistProvider::RunType runType() const override; TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const override; +protected: + Client *client() const { return m_client; } + private: Client *m_client = nullptr; // not owned }; +class LANGUAGECLIENT_EXPORT LanguageClientQuickFixAssistProcessor + : public TextEditor::IAssistProcessor +{ +public: + explicit LanguageClientQuickFixAssistProcessor(Client *client) : m_client(client) {} + bool running() override { return m_currentRequest.has_value(); } + TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override; + void cancel() override; + +protected: + void setOnlyKinds(const QList<LanguageServerProtocol::CodeActionKind> &only); + +private: + void handleCodeActionResponse(const LanguageServerProtocol::CodeActionRequest::Response &response); + virtual void handleProposalReady(const TextEditor::QuickFixOperations &ops); + + QSharedPointer<const TextEditor::AssistInterface> m_assistInterface; + Client *m_client = nullptr; // not owned + Utils::optional<LanguageServerProtocol::MessageId> m_currentRequest; +}; + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index e994fefb498..09fa2d7a1cf 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -37,13 +37,15 @@ #include <projectexplorer/project.h> #include <projectexplorer/session.h> +#include <texteditor/plaintexteditorfactory.h> +#include <texteditor/textmark.h> + #include <utils/algorithm.h> -#include <utils/utilsicons.h> #include <utils/delegates.h> #include <utils/fancylineedit.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/jsontreeitem.h> #include <utils/stringutils.h> +#include <utils/utilsicons.h> #include <utils/variablechooser.h> #include <QBoxLayout> @@ -75,11 +77,13 @@ constexpr char startupBehaviorKey[] = "startupBehavior"; constexpr char mimeTypeKey[] = "mimeType"; constexpr char filePatternKey[] = "filePattern"; constexpr char initializationOptionsKey[] = "initializationOptions"; +constexpr char configurationKey[] = "configuration"; constexpr char executableKey[] = "executable"; constexpr char argumentsKey[] = "arguments"; constexpr char settingsGroupKey[] = "LanguageClient"; constexpr char clientsKey[] = "clients"; constexpr char typedClientsKey[] = "typedClients"; +constexpr char outlineSortedKey[] = "outlineSorted"; constexpr char mimeType[] = "application/language.client.setting"; namespace LanguageClient { @@ -523,6 +527,16 @@ QJsonObject BaseSettings::initializationOptions() const expand(m_initializationOptions).toUtf8()).object(); } +QJsonValue BaseSettings::configuration() const +{ + const QJsonDocument document = QJsonDocument::fromJson(m_configuration.toUtf8()); + if (document.isArray()) + return document.array(); + if (document.isObject()) + return document.object(); + return {}; +} + bool BaseSettings::applyFromSettingsWidget(QWidget *widget) { bool changed = false; @@ -593,6 +607,7 @@ QVariantMap BaseSettings::toMap() const map.insert(mimeTypeKey, m_languageFilter.mimeTypes); map.insert(filePatternKey, m_languageFilter.filePattern); map.insert(initializationOptionsKey, m_initializationOptions); + map.insert(configurationKey, m_configuration); return map; } @@ -607,6 +622,7 @@ void BaseSettings::fromMap(const QVariantMap &map) m_languageFilter.filePattern = map[filePatternKey].toStringList(); m_languageFilter.filePattern.removeAll(QString()); // remove empty entries m_initializationOptions = map[initializationOptionsKey].toString(); + m_configuration = map[configurationKey].toString(); } static LanguageClientSettingsPage &settingsPage() @@ -686,6 +702,23 @@ void LanguageClientSettings::toSettings(QSettings *settings, settings->endGroup(); } +bool LanguageClientSettings::outlineComboBoxIsSorted() +{ + auto settings = Core::ICore::settings(); + settings->beginGroup(settingsGroupKey); + bool sorted = settings->value(outlineSortedKey).toBool(); + settings->endGroup(); + return sorted; +} + +void LanguageClientSettings::setOutlineComboBoxSorted(bool sorted) +{ + auto settings = Core::ICore::settings(); + settings->beginGroup(settingsGroupKey); + settings->setValue(outlineSortedKey, sorted); + settings->endGroup(); +} + bool StdIOSettings::applyFromSettingsWidget(QWidget *widget) { bool changed = false; @@ -1025,4 +1058,39 @@ bool LanguageFilter::operator!=(const LanguageFilter &other) const return this->filePattern != other.filePattern || this->mimeTypes != other.mimeTypes; } +TextEditor::BaseTextEditor *jsonEditor() +{ + using namespace TextEditor; + BaseTextEditor *editor = PlainTextEditorFactory::createPlainTextEditor(); + TextDocument *document = editor->textDocument(); + TextEditorWidget *widget = editor->editorWidget(); + widget->configureGenericHighlighter(Utils::mimeTypeForName("application/json")); + widget->setLineNumbersVisible(false); + widget->setMarksVisible(false); + widget->setRevisionsVisible(false); + widget->setCodeFoldingSupported(false); + QObject::connect(document, &TextDocument::contentsChanged, widget, [document](){ + const Utils::Id jsonMarkId("LanguageClient.JsonTextMarkId"); + qDeleteAll( + Utils::filtered(document->marks(), Utils::equal(&TextMark::category, jsonMarkId))); + const QString content = document->plainText().trimmed(); + if (content.isEmpty()) + return; + QJsonParseError error; + QJsonDocument::fromJson(content.toUtf8(), &error); + if (error.error == QJsonParseError::NoError) + return; + const Utils::OptionalLineColumn lineColumn + = Utils::Text::convertPosition(document->document(), error.offset); + if (!lineColumn.has_value()) + return; + auto mark = new TextMark(Utils::FilePath(), lineColumn->line, jsonMarkId); + mark->setLineAnnotation(error.errorString()); + mark->setColor(Utils::Theme::CodeModel_Error_TextMarkColor); + mark->setIcon(Utils::Icons::CODEMODEL_ERROR.icon()); + document->addMark(mark); + }); + return editor; +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientsettings.h b/src/plugins/languageclient/languageclientsettings.h index 03a9cf9f84a..ad2676454bd 100644 --- a/src/plugins/languageclient/languageclientsettings.h +++ b/src/plugins/languageclient/languageclientsettings.h @@ -51,6 +51,7 @@ class FancyLineEdit; namespace Core { class IDocument; } namespace ProjectExplorer { class Project; } +namespace TextEditor { class BaseTextEditor; } namespace LanguageClient { @@ -88,8 +89,10 @@ public: StartBehavior m_startBehavior = RequiresFile; LanguageFilter m_languageFilter; QString m_initializationOptions; + QString m_configuration; QJsonObject initializationOptions() const; + QJsonValue configuration() const; virtual bool applyFromSettingsWidget(QWidget *widget); virtual QWidget *createSettingsWidget(QWidget *parent = nullptr) const; @@ -169,6 +172,9 @@ public: static void addSettings(BaseSettings *settings); static void enableSettings(const QString &id); static void toSettings(QSettings *settings, const QList<BaseSettings *> &languageClientSettings); + + static bool outlineComboBoxIsSorted(); + static void setOutlineComboBoxSorted(bool sorted); }; class LANGUAGECLIENT_EXPORT BaseSettingsWidget : public QWidget @@ -212,4 +218,6 @@ private: QLineEdit *m_arguments = nullptr; }; +LANGUAGECLIENT_EXPORT TextEditor::BaseTextEditor *jsonEditor(); + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index 8199084c416..447577d6a83 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -31,7 +31,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/find/searchresultwindow.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QFile> @@ -76,13 +76,12 @@ static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response & Utils::ProcessLinkCallback callback, Utils::optional<Utils::Link> linkUnderCursor) { - if (Utils::optional<GotoResult> _result = response.result()) { - const GotoResult result = _result.value(); - if (Utils::holds_alternative<std::nullptr_t>(result)) { + if (Utils::optional<GotoResult> result = response.result()) { + if (Utils::holds_alternative<std::nullptr_t>(*result)) { callback({}); - } else if (auto ploc = Utils::get_if<Location>(&result)) { + } else if (auto ploc = Utils::get_if<Location>(&*result)) { callback(linkUnderCursor.value_or(ploc->toLink())); - } else if (auto plloc = Utils::get_if<QList<Location>>(&result)) { + } else if (auto plloc = Utils::get_if<QList<Location>>(&*result)) { if (!plloc->isEmpty()) callback(linkUnderCursor.value_or(plloc->value(0).toLink())); else @@ -206,8 +205,7 @@ void SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Re if (result) { Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch( tr("Find References with %1 for:").arg(m_client->name()), "", wordUnderCursor); - search->addResults(generateSearchResultItems(result.value()), - Core::SearchResult::AddOrdered); + search->addResults(generateSearchResultItems(*result), Core::SearchResult::AddOrdered); QObject::connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) { @@ -421,7 +419,7 @@ void SymbolSupport::applyRename(const QList<Core::SearchResultItem> &checkedItem } for (auto it = editsForDocuments.begin(), end = editsForDocuments.end(); it != end; ++it) - applyTextEdits(it.key(), it.value()); + applyTextEdits(m_client, it.key(), it.value()); } Core::Search::TextRange SymbolSupport::convertRange(const Range &range) diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index f0a2db61ffb..77c2b20f967 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -88,17 +88,22 @@ bool applyTextDocumentEdit(const Client *client, const TextDocumentEdit &edit) LanguageClientValue<int> version = edit.textDocument().version(); if (!version.isNull() && version.value(0) < client->documentVersion(filePath)) return false; - return applyTextEdits(uri, edits); + return applyTextEdits(client, uri, edits); } -bool applyTextEdits(const DocumentUri &uri, const QList<TextEdit> &edits) +bool applyTextEdits(const Client *client, const DocumentUri &uri, const QList<TextEdit> &edits) { if (edits.isEmpty()) return true; - RefactoringChanges changes; + RefactoringChangesData * const backend = client->createRefactoringChangesBackend(); + RefactoringChanges changes(backend); RefactoringFilePtr file; file = changes.file(uri.toFilePath()); file->setChangeSet(editsToChangeSet(edits, file->document())); + if (backend) { + for (const TextEdit &edit : edits) + file->appendIndentRange(convertRange(file->document(), edit.range())); + } return file->apply(); } @@ -130,7 +135,7 @@ bool applyWorkspaceEdit(const Client *client, const WorkspaceEdit &edit) } else { const WorkspaceEdit::Changes &changes = edit.changes().value_or(WorkspaceEdit::Changes()); for (auto it = changes.cbegin(); it != changes.cend(); ++it) - result |= applyTextEdits(it.key(), it.value()); + result |= applyTextEdits(client, it.key(), it.value()); return result; } return result; @@ -161,22 +166,21 @@ void updateCodeActionRefactoringMarker(Client *client, marker.type = client->id(); if (action.isValid()) marker.tooltip = action.title(); - if (action.edit().has_value()) { - WorkspaceEdit edit = action.edit().value(); + if (Utils::optional<WorkspaceEdit> edit = action.edit()) { marker.callback = [client, edit](const TextEditorWidget *) { - applyWorkspaceEdit(client, edit); + applyWorkspaceEdit(client, *edit); }; if (diagnostics.isEmpty()) { QList<TextEdit> edits; - if (optional<QList<TextDocumentEdit>> documentChanges = edit.documentChanges()) { + if (optional<QList<TextDocumentEdit>> documentChanges = edit->documentChanges()) { QList<TextDocumentEdit> changesForUri = Utils::filtered( - documentChanges.value(), [uri](const TextDocumentEdit &edit) { + *documentChanges, [uri](const TextDocumentEdit &edit) { return edit.textDocument().uri() == uri; }); for (const TextDocumentEdit &edit : changesForUri) edits << edit.edits(); - } else if (optional<WorkspaceEdit::Changes> localChanges = edit.changes()) { - edits = localChanges.value()[uri]; + } else if (optional<WorkspaceEdit::Changes> localChanges = edit->changes()) { + edits = (*localChanges)[uri]; } for (const TextEdit &edit : qAsConst(edits)) { marker.cursor = endOfLineCursor(edit.range().start().toTextCursor(doc->document())); @@ -273,8 +277,8 @@ void updateEditorToolBar(Core::IEditor *editor) }); } - if (!extras->m_client || extras->m_client != client || - !LanguageClientOutlineWidgetFactory::clientSupportsDocumentSymbols(client, document)) { + if (!extras->m_client || !client || extras->m_client != client + || !client->supportsDocumentSymbols(document)) { if (extras->m_outlineAction) { widget->toolBar()->removeAction(extras->m_outlineAction); delete extras->m_outlineAction; @@ -283,10 +287,11 @@ void updateEditorToolBar(Core::IEditor *editor) } if (!extras->m_client) { - if (QWidget *comboBox = LanguageClientOutlineWidgetFactory::createComboBox(client, editor)) { + QWidget *comboBox = LanguageClientOutlineWidgetFactory::createComboBox(client, textEditor); + if (comboBox) { extras->m_client = client; extras->m_outlineAction = widget->insertExtraToolBarWidget(TextEditorWidget::Left, - comboBox); + comboBox); } } } diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h index c8120766077..f97c9a9930d 100644 --- a/src/plugins/languageclient/languageclientutils.h +++ b/src/plugins/languageclient/languageclientutils.h @@ -51,7 +51,8 @@ Utils::ChangeSet editsToChangeSet(const QList<LanguageServerProtocol::TextEdit> bool LANGUAGECLIENT_EXPORT applyWorkspaceEdit(const Client *client, const LanguageServerProtocol::WorkspaceEdit &edit); bool LANGUAGECLIENT_EXPORT applyTextDocumentEdit(const Client *client, const LanguageServerProtocol::TextDocumentEdit &edit); -bool LANGUAGECLIENT_EXPORT applyTextEdits(const LanguageServerProtocol::DocumentUri &uri, +bool LANGUAGECLIENT_EXPORT applyTextEdits(const Client *client, + const LanguageServerProtocol::DocumentUri &uri, const QList<LanguageServerProtocol::TextEdit> &edits); void LANGUAGECLIENT_EXPORT applyTextEdit(TextEditor::TextDocumentManipulatorInterface &manipulator, const LanguageServerProtocol::TextEdit &edit, diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index 5ccc4d175a0..e7a6aed4e98 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -210,9 +210,9 @@ QList<Core::LocatorFilterEntry> DocumentLocatorFilter::matchesFor( QTC_ASSERT(m_currentSymbols.has_value(), return {}); - if (auto list = Utils::get_if<QList<DocumentSymbol>>(&m_currentSymbols.value())) + if (auto list = Utils::get_if<QList<DocumentSymbol>>(&*m_currentSymbols)) return generateEntries(*list, entry); - else if (auto list = Utils::get_if<QList<SymbolInformation>>(&m_currentSymbols.value())) + else if (auto list = Utils::get_if<QList<SymbolInformation>>(&*m_currentSymbols)) return generateEntries(*list, entry); return {}; diff --git a/src/plugins/languageclient/semantichighlightsupport.cpp b/src/plugins/languageclient/semantichighlightsupport.cpp index d6abceaba91..031794e8ec5 100644 --- a/src/plugins/languageclient/semantichighlightsupport.cpp +++ b/src/plugins/languageclient/semantichighlightsupport.cpp @@ -32,7 +32,7 @@ #include <texteditor/syntaxhighlighter.h> #include <texteditor/texteditor.h> #include <texteditor/texteditorsettings.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QTextDocument> @@ -301,14 +301,15 @@ SemanticRequestTypes SemanticTokenSupport::supportedSemanticRequests(TextDocumen }; const QString dynamicMethod = "textDocument/semanticTokens"; const DynamicCapabilities &dynamicCapabilities = m_client->dynamicCapabilities(); - if (auto registered = dynamicCapabilities.isRegistered(dynamicMethod); - registered.has_value()) { - if (!registered.value()) + if (auto registered = dynamicCapabilities.isRegistered(dynamicMethod)) { + if (!*registered) return SemanticRequestType::None; return supportedRequests(dynamicCapabilities.option(dynamicMethod).toObject()); } - if (m_client->capabilities().semanticTokensProvider().has_value()) - return supportedRequests(m_client->capabilities().semanticTokensProvider().value()); + if (Utils::optional<SemanticTokensOptions> provider = m_client->capabilities() + .semanticTokensProvider()) { + return supportedRequests(*provider); + } return SemanticRequestType::None; } @@ -359,10 +360,9 @@ void SemanticTokenSupport::handleSemanticTokensDelta( return; for (const auto start = data.begin() + edit.start(); it < start; ++it) newData.append(*it); - const Utils::optional<QList<int>> editData = edit.data(); - if (editData.has_value()) { - newData.append(editData.value()); - qCDebug(LOGLSPHIGHLIGHT) << edit.start() << edit.deleteCount() << editData.value(); + if (const Utils::optional<QList<int>> editData = edit.data()) { + newData.append(*editData); + qCDebug(LOGLSPHIGHLIGHT) << edit.start() << edit.deleteCount() << *editData; } else { qCDebug(LOGLSPHIGHLIGHT) << edit.start() << edit.deleteCount(); } diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt index 6d7602c2f0e..b40b82443fa 100644 --- a/src/plugins/mcusupport/CMakeLists.txt +++ b/src/plugins/mcusupport/CMakeLists.txt @@ -12,11 +12,11 @@ add_qtc_plugin(McuSupport mcusupportoptionspage.cpp mcusupportoptionspage.h mcupackage.cpp mcupackage.h mcutarget.cpp mcutarget.h + mcutargetfactory.cpp mcutargetfactory.h mcusupportplugin.cpp mcusupportplugin.h mcusupportsdk.cpp mcusupportsdk.h mcusupportrunconfiguration.cpp mcusupportrunconfiguration.h mcusupportversiondetection.cpp mcusupportversiondetection.h - mcusupportcmakemapper.cpp mcusupportcmakemapper.h mcutargetdescription.h ) diff --git a/src/plugins/mcusupport/mcuabstractpackage.h b/src/plugins/mcusupport/mcuabstractpackage.h index e0499ef06fa..7a7ebd151f2 100644 --- a/src/plugins/mcusupport/mcuabstractpackage.h +++ b/src/plugins/mcusupport/mcuabstractpackage.h @@ -46,6 +46,7 @@ public: }; virtual QString label() const = 0; + virtual const QString &cmakeVariableName() const = 0; virtual const QString &environmentVariableName() const = 0; virtual bool isAddToSystemPath() const = 0; virtual void setVersions(const QStringList &) = 0; @@ -54,6 +55,7 @@ public: virtual Utils::FilePath path() const = 0; virtual Utils::FilePath defaultPath() const = 0; virtual Utils::FilePath detectionPath() const = 0; + virtual QString settingsKey() const = 0; virtual void updateStatus() = 0; virtual Status status() const = 0; diff --git a/src/plugins/mcusupport/mcukitinformation.cpp b/src/plugins/mcusupport/mcukitinformation.cpp index 2ab9af35d30..7c7bde5c7de 100644 --- a/src/plugins/mcusupport/mcukitinformation.cpp +++ b/src/plugins/mcusupport/mcukitinformation.cpp @@ -25,7 +25,10 @@ #include "mcukitinformation.h" +#include <cmakeprojectmanager/cmakekitinformation.h> #include <utils/qtcassert.h> +#include <utils/algorithm.h> +#include <utils/filepath.h> using namespace ProjectExplorer; @@ -57,31 +60,31 @@ McuDependenciesKitAspect::McuDependenciesKitAspect() setPriority(28500); } -Tasks McuDependenciesKitAspect::validate(const Kit *k) const +Tasks McuDependenciesKitAspect::validate(const Kit *kit) const { Tasks result; - QTC_ASSERT(k, return result); + QTC_ASSERT(kit, return result); - const QVariant checkFormat = k->value(McuDependenciesKitAspect::id()); - if (!checkFormat.isNull() && !checkFormat.canConvert(QVariant::List)) + // check dependencies are defined properly for this kit + const QVariant checkFormat = kit->value(McuDependenciesKitAspect::id()); + if (!checkFormat.isValid() || checkFormat.isNull()) + return result; + if (!checkFormat.canConvert(QVariant::List)) return {BuildSystemTask(Task::Error, tr("The MCU dependencies setting value is invalid."))}; - const QVariant envStringList = k->value(EnvironmentKitAspect::id()); - if (!envStringList.isNull() && !envStringList.canConvert(QVariant::List)) - return {BuildSystemTask(Task::Error, tr("The environment setting value is invalid."))}; - - const auto environment = Utils::NameValueDictionary(envStringList.toStringList()); - for (const auto &dependency : dependencies(k)) { - if (!environment.hasKey(dependency.name)) { - result << BuildSystemTask(Task::Warning, - tr("Environment variable %1 not defined.") - .arg(dependency.name)); + // check paths defined in cmake variables for given dependencies exist + const auto cMakeEntries = Utils::NameValueDictionary(configuration(kit)); + for (const auto &dependency: dependencies(kit)) { + auto givenPath = Utils::FilePath::fromString(cMakeEntries.value(dependency.name)); + if (givenPath.isEmpty()) { + result << BuildSystemTask(Task::Warning, tr("CMake variable %1 not defined.").arg( + dependency.name)); } else { - const auto path = Utils::FilePath::fromUserInput(environment.value(dependency.name) - + "/" + dependency.value); - if (!path.exists()) { - result << BuildSystemTask(Task::Warning, - tr("%1 not found.").arg(path.toUserOutput())); + const auto detectionPath = givenPath.resolvePath(dependency.value); + if (!detectionPath.exists()) { + result << BuildSystemTask(Task::Warning, tr("CMake variable %1: path %2 does not exist.").arg( + dependency.name, + detectionPath.toUserOutput())); } } } @@ -89,40 +92,40 @@ Tasks McuDependenciesKitAspect::validate(const Kit *k) const return result; } -void McuDependenciesKitAspect::fix(Kit *k) +void McuDependenciesKitAspect::fix(Kit *kit) { - QTC_ASSERT(k, return); + QTC_ASSERT(kit, return); - const QVariant variant = k->value(McuDependenciesKitAspect::id()); + const QVariant variant = kit->value(McuDependenciesKitAspect::id()); if (!variant.isNull() && !variant.canConvert(QVariant::List)) { - qWarning("Kit \"%s\" has a wrong mcu dependencies value set.", qPrintable(k->displayName())); - setDependencies(k, Utils::NameValueItems()); + qWarning("Kit \"%s\" has a wrong mcu dependencies value set.", qPrintable(kit->displayName())); + setDependencies(kit, Utils::NameValueItems()); } } -KitAspectWidget *McuDependenciesKitAspect::createConfigWidget(Kit *k) const +KitAspectWidget *McuDependenciesKitAspect::createConfigWidget(Kit *kit) const { - QTC_ASSERT(k, return nullptr); - return new McuDependenciesKitAspectWidget(k, this); + QTC_ASSERT(kit, return nullptr); + return new McuDependenciesKitAspectWidget(kit, this); } -KitAspect::ItemList McuDependenciesKitAspect::toUserOutput(const Kit *k) const +KitAspect::ItemList McuDependenciesKitAspect::toUserOutput(const Kit *kit) const { - Q_UNUSED(k) + Q_UNUSED(kit) return {}; } Utils::Id McuDependenciesKitAspect::id() { - return "PE.Profile.McuDependencies"; + return "PE.Profile.McuCMakeDependencies"; } -Utils::NameValueItems McuDependenciesKitAspect::dependencies(const Kit *k) +Utils::NameValueItems McuDependenciesKitAspect::dependencies(const Kit *kit) { - if (k) + if (kit) return Utils::NameValueItem::fromStringList( - k->value(McuDependenciesKitAspect::id()).toStringList()); + kit->value(McuDependenciesKitAspect::id()).toStringList()); return Utils::NameValueItems(); } @@ -133,5 +136,14 @@ void McuDependenciesKitAspect::setDependencies(Kit *k, const Utils::NameValueIte Utils::NameValueItem::toStringList(dependencies)); } +Utils::NameValuePairs McuDependenciesKitAspect::configuration(const Kit *kit) +{ + using namespace CMakeProjectManager; + const auto config = CMakeConfigurationKitAspect::configuration(kit).toList(); + return Utils::transform<Utils::NameValuePairs>(config, [](const CMakeConfigItem &it) { + return Utils::NameValuePair(QString::fromUtf8(it.key), QString::fromUtf8(it.value)); + }); +} + } // namespace Internal } // namespace McuSupport diff --git a/src/plugins/mcusupport/mcukitinformation.h b/src/plugins/mcusupport/mcukitinformation.h index 85c811ad668..516efa3af0f 100644 --- a/src/plugins/mcusupport/mcukitinformation.h +++ b/src/plugins/mcusupport/mcukitinformation.h @@ -37,16 +37,17 @@ class McuDependenciesKitAspect final : public ProjectExplorer::KitAspect public: McuDependenciesKitAspect(); - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const override; - void fix(ProjectExplorer::Kit *k) override; + ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *kit) const override; + void fix(ProjectExplorer::Kit *kit) override; - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *k) const override; + ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *kit) const override; - ItemList toUserOutput(const ProjectExplorer::Kit *k) const override; + ItemList toUserOutput(const ProjectExplorer::Kit *kit) const override; static Utils::Id id(); - static Utils::NameValueItems dependencies(const ProjectExplorer::Kit *k); - static void setDependencies(ProjectExplorer::Kit *k, const Utils::NameValueItems &dependencies); + static Utils::NameValueItems dependencies(const ProjectExplorer::Kit *kit); + static void setDependencies(ProjectExplorer::Kit *kit, const Utils::NameValueItems &dependencies); + static Utils::NameValuePairs configuration(const ProjectExplorer::Kit *kit); }; } // namespace Internal diff --git a/src/plugins/mcusupport/mcukitmanager.cpp b/src/plugins/mcusupport/mcukitmanager.cpp index a563a3828a3..45091155f49 100644 --- a/src/plugins/mcusupport/mcukitmanager.cpp +++ b/src/plugins/mcusupport/mcukitmanager.cpp @@ -26,12 +26,12 @@ #include "mcukitmanager.h" #include "mcusupportoptions.h" -#include "mcusupportconstants.h" #include "mcukitinformation.h" #include "mcupackage.h" -#include "mcutarget.h" +#include "mcusupportconstants.h" #include "mcusupportplugin.h" #include "mcusupportsdk.h" +#include "mcutarget.h" #include <cmakeprojectmanager/cmakekitinformation.h> #include <cmakeprojectmanager/cmaketoolmanager.h> @@ -46,203 +46,307 @@ #include <QMessageBox> #include <QPushButton> +using CMakeProjectManager::CMakeConfig; using CMakeProjectManager::CMakeConfigItem; using CMakeProjectManager::CMakeConfigurationKitAspect; using namespace ProjectExplorer; using namespace Utils; -namespace McuSupport { -namespace Internal { -namespace McuKitManager { +namespace McuSupport::Internal { -static const int KIT_VERSION = 9; // Bumps up whenever details in Kit creation change - -static void setKitToolchains(Kit *k, const McuToolChainPackage *tcPackage) +// Utils for managing CMake Configurations +static QMap<QByteArray, QByteArray> cMakeConfigToMap(const CMakeConfig &config) { - switch (tcPackage->toolchainType()) { - case McuToolChainPackage::ToolChainType::Unsupported: - return; - - case McuToolChainPackage::ToolChainType::GHS: - case McuToolChainPackage::ToolChainType::GHSArm: - return; // No Green Hills toolchain, because support for it is missing. - - case McuToolChainPackage::ToolChainType::IAR: - case McuToolChainPackage::ToolChainType::KEIL: - case McuToolChainPackage::ToolChainType::MSVC: - case McuToolChainPackage::ToolChainType::GCC: - case McuToolChainPackage::ToolChainType::ArmGcc: - ToolChainKitAspect::setToolChain(k, - tcPackage->toolChain( - ProjectExplorer::Constants::C_LANGUAGE_ID)); - ToolChainKitAspect::setToolChain(k, - tcPackage->toolChain( - ProjectExplorer::Constants::CXX_LANGUAGE_ID)); - return; - - default: - Q_UNREACHABLE(); + QMap<QByteArray, QByteArray> map; + for (const auto &configItem : qAsConst(config.toList())) { + map.insert(configItem.key, configItem.value); } + return map; } - -static void setKitProperties(const QString &kitName, - Kit *k, - const McuTarget *mcuTarget, - const FilePath &sdkPath) +static CMakeConfig mapToCMakeConfig(const QMap<QByteArray, QByteArray> &map) { - using namespace Constants; + QList<CMakeConfigItem> asList; + for (auto it = map.constKeyValueBegin(); it != map.constKeyValueEnd(); ++it) { + asList.append(CMakeConfigItem(it->first, it->second)); + } - k->setUnexpandedDisplayName(kitName); - k->setValue(KIT_MCUTARGET_VENDOR_KEY, mcuTarget->platform().vendor); - k->setValue(KIT_MCUTARGET_MODEL_KEY, mcuTarget->platform().name); - k->setValue(KIT_MCUTARGET_COLORDEPTH_KEY, mcuTarget->colorDepth()); - k->setValue(KIT_MCUTARGET_SDKVERSION_KEY, mcuTarget->qulVersion().toString()); - k->setValue(KIT_MCUTARGET_KITVERSION_KEY, KIT_VERSION); - k->setValue(KIT_MCUTARGET_OS_KEY, static_cast<int>(mcuTarget->os())); - k->setValue(KIT_MCUTARGET_TOOCHAIN_KEY, mcuTarget->toolChainPackage()->toolChainName()); - k->setAutoDetected(false); - k->makeSticky(); - if (mcuTarget->toolChainPackage()->isDesktopToolchain()) - k->setDeviceTypeForIcon(DEVICE_TYPE); - k->setValue(QtSupport::SuppliesQtQuickImportPath::id(), true); - k->setValue(QtSupport::KitQmlImportPath::id(), sdkPath.pathAppended("include/qul").toVariant()); - k->setValue(QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), true); - QSet<Id> irrelevant = { - SysRootKitAspect::id(), - QtSupport::SuppliesQtQuickImportPath::id(), - QtSupport::KitQmlImportPath::id(), - QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), - }; - if (!McuSupportOptions::kitsNeedQtVersion()) - irrelevant.insert(QtSupport::QtKitAspect::id()); - k->setIrrelevantAspects(irrelevant); + return CMakeConfig(asList); } +namespace McuKitManager { -static void setKitDebugger(Kit *k, const McuToolChainPackage *tcPackage) -{ - if (tcPackage->isDesktopToolchain()) { - // Qt Creator seems to be smart enough to deduce the right Kit debugger from the ToolChain - return; - } +static const int KIT_VERSION = 9; // Bumps up whenever details in Kit creation change - switch (tcPackage->toolchainType()) { - case McuToolChainPackage::ToolChainType::Unsupported: - case McuToolChainPackage::ToolChainType::GHS: - case McuToolChainPackage::ToolChainType::GHSArm: - case McuToolChainPackage::ToolChainType::IAR: - return; // No Green Hills and IAR debugger, because support for it is missing. - - case McuToolChainPackage::ToolChainType::KEIL: - case McuToolChainPackage::ToolChainType::MSVC: - case McuToolChainPackage::ToolChainType::GCC: - case McuToolChainPackage::ToolChainType::ArmGcc: { - const QVariant debuggerId = tcPackage->debuggerId(); - if (debuggerId.isValid()) { - Debugger::DebuggerKitAspect::setDebugger(k, debuggerId); +class McuKitFactory +{ +public: + static void setKitToolchains(Kit *k, const McuToolChainPackage *tcPackage) + { + switch (tcPackage->toolchainType()) { + case McuToolChainPackage::ToolChainType::Unsupported: + return; + + case McuToolChainPackage::ToolChainType::GHS: + case McuToolChainPackage::ToolChainType::GHSArm: + return; // No Green Hills toolchain, because support for it is missing. + + case McuToolChainPackage::ToolChainType::IAR: + case McuToolChainPackage::ToolChainType::KEIL: + case McuToolChainPackage::ToolChainType::MSVC: + case McuToolChainPackage::ToolChainType::GCC: + case McuToolChainPackage::ToolChainType::ArmGcc: + ToolChainKitAspect::setToolChain(k, + tcPackage->toolChain( + ProjectExplorer::Constants::C_LANGUAGE_ID)); + ToolChainKitAspect::setToolChain(k, + tcPackage->toolChain( + ProjectExplorer::Constants::CXX_LANGUAGE_ID)); + return; + + default: + Q_UNREACHABLE(); } - return; } - default: - Q_UNREACHABLE(); + static void setKitProperties(const QString &kitName, + Kit *k, + const McuTarget *mcuTarget, + const FilePath &sdkPath) + { + using namespace Constants; + + k->setUnexpandedDisplayName(kitName); + k->setValue(KIT_MCUTARGET_VENDOR_KEY, mcuTarget->platform().vendor); + k->setValue(KIT_MCUTARGET_MODEL_KEY, mcuTarget->platform().name); + k->setValue(KIT_MCUTARGET_COLORDEPTH_KEY, mcuTarget->colorDepth()); + k->setValue(KIT_MCUTARGET_SDKVERSION_KEY, mcuTarget->qulVersion().toString()); + k->setValue(KIT_MCUTARGET_KITVERSION_KEY, KIT_VERSION); + k->setValue(KIT_MCUTARGET_OS_KEY, static_cast<int>(mcuTarget->os())); + k->setValue(KIT_MCUTARGET_TOOCHAIN_KEY, mcuTarget->toolChainPackage()->toolChainName()); + k->setAutoDetected(false); + k->makeSticky(); + if (mcuTarget->toolChainPackage()->isDesktopToolchain()) + k->setDeviceTypeForIcon(DEVICE_TYPE); + k->setValue(QtSupport::SuppliesQtQuickImportPath::id(), true); + k->setValue(QtSupport::KitQmlImportPath::id(), + sdkPath.pathAppended("include/qul").toVariant()); + k->setValue(QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), true); + QSet<Id> irrelevant = { + SysRootKitAspect::id(), + QtSupport::SuppliesQtQuickImportPath::id(), + QtSupport::KitQmlImportPath::id(), + QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), + }; + if (!McuSupportOptions::kitsNeedQtVersion()) + irrelevant.insert(QtSupport::QtKitAspect::id()); + k->setIrrelevantAspects(irrelevant); } -} -static void setKitDevice(Kit *k, const McuTarget *mcuTarget) -{ - // "Device Type" Desktop is the default. We use that for the Qt for MCUs Desktop Kit - if (mcuTarget->toolChainPackage()->isDesktopToolchain()) - return; + static void setKitDebugger(Kit *k, const McuToolChainPackage *tcPackage) + { + if (tcPackage->isDesktopToolchain()) { + // Qt Creator seems to be smart enough to deduce the right Kit debugger from the ToolChain + return; + } - DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DEVICE_TYPE); -} + switch (tcPackage->toolchainType()) { + case McuToolChainPackage::ToolChainType::Unsupported: + case McuToolChainPackage::ToolChainType::GHS: + case McuToolChainPackage::ToolChainType::GHSArm: + case McuToolChainPackage::ToolChainType::IAR: + return; // No Green Hills and IAR debugger, because support for it is missing. + + case McuToolChainPackage::ToolChainType::KEIL: + case McuToolChainPackage::ToolChainType::MSVC: + case McuToolChainPackage::ToolChainType::GCC: + case McuToolChainPackage::ToolChainType::ArmGcc: { + const QVariant debuggerId = tcPackage->debuggerId(); + if (debuggerId.isValid()) { + Debugger::DebuggerKitAspect::setDebugger(k, debuggerId); + } + return; + } -static void setKitDependencies(Kit *k, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) -{ - NameValueItems dependencies; + default: + Q_UNREACHABLE(); + } + } - auto processPackage = [&dependencies](const McuAbstractPackage *package) { - if (!package->environmentVariableName().isEmpty()) - dependencies.append({package->environmentVariableName(), - package->detectionPath().toUserOutput()}); - }; - for (auto package : mcuTarget->packages()) - processPackage(package); - processPackage(qtForMCUsSdkPackage); + static void setKitDevice(Kit *k, const McuTarget *mcuTarget) + { + // "Device Type" Desktop is the default. We use that for the Qt for MCUs Desktop Kit + if (mcuTarget->toolChainPackage()->isDesktopToolchain()) + return; - McuDependenciesKitAspect::setDependencies(k, dependencies); + DeviceTypeKitAspect::setDeviceTypeId(k, Constants::DEVICE_TYPE); + } - auto irrelevant = k->irrelevantAspects(); - irrelevant.insert(McuDependenciesKitAspect::id()); - k->setIrrelevantAspects(irrelevant); -} + static void setKitDependencies(Kit *k, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) + { + NameValueItems dependencies; + + auto processPackage = [&dependencies](const McuAbstractPackage *package) { + const auto cmakeVariableName = package->cmakeVariableName(); + if (!cmakeVariableName.isEmpty()) + dependencies.append({cmakeVariableName, package->detectionPath().toUserOutput()}); + }; + for (auto package : mcuTarget->packages()) + processPackage(package); + processPackage(qtForMCUsSdkPackage); + + McuDependenciesKitAspect::setDependencies(k, dependencies); + + auto irrelevant = k->irrelevantAspects(); + irrelevant.insert(McuDependenciesKitAspect::id()); + k->setIrrelevantAspects(irrelevant); + } -static void setKitCMakeOptions(Kit *k, const McuTarget *mcuTarget, const FilePath &qulDir) -{ - using namespace CMakeProjectManager; - - CMakeConfig config = CMakeConfigurationKitAspect::configuration(k); - // CMake ToolChain file for ghs handles CMAKE_*_COMPILER autonomously - if (mcuTarget->toolChainPackage()->toolchainType() != McuToolChainPackage::ToolChainType::GHS - && mcuTarget->toolChainPackage()->toolchainType() != McuToolChainPackage::ToolChainType::GHSArm) { - config.append(CMakeConfigItem("CMAKE_CXX_COMPILER", "%{Compiler:Executable:Cxx}")); - config.append(CMakeConfigItem("CMAKE_C_COMPILER", "%{Compiler:Executable:C}")); + static void setKitEnvironment(Kit *k, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) + { + EnvironmentItems changes; + QStringList pathAdditions; // clazy:exclude=inefficient-qlist-soft + + // The Desktop version depends on the Qt shared libs in Qul_DIR/bin. + // If CMake's fileApi is avaialble, we can rely on the "Add library search path to PATH" + // feature of the run configuration. Otherwise, we just prepend the path, here. + if (mcuTarget->toolChainPackage()->isDesktopToolchain() + && !CMakeProjectManager::CMakeToolManager::defaultCMakeTool()->hasFileApi()) + pathAdditions.append(qtForMCUsSdkPackage->path().pathAppended("bin").toUserOutput()); + + auto processPackage = [&pathAdditions](const McuAbstractPackage *package) { + if (package->isAddToSystemPath()) + pathAdditions.append(package->path().toUserOutput()); + }; + + for (auto package : mcuTarget->packages()) + processPackage(package); + processPackage(qtForMCUsSdkPackage); + + if (!pathAdditions.isEmpty()) { + const QString path = QLatin1String(HostOsInfo::isWindowsHost() ? "Path" : "PATH"); + pathAdditions.append("${" + path + "}"); + changes.append({path, pathAdditions.join(HostOsInfo::pathListSeparator())}); + } + + if (McuSupportOptions::kitsNeedQtVersion()) + changes.append({QLatin1String("LD_LIBRARY_PATH"), "%{Qt:QT_INSTALL_LIBS}"}); + + EnvironmentKitAspect::setEnvironmentChanges(k, changes); } - if (!mcuTarget->toolChainPackage()->isDesktopToolchain()) { - const FilePath cMakeToolchainFile = qulDir.pathAppended( - "lib/cmake/Qul/toolchain/" + mcuTarget->toolChainPackage()->cmakeToolChainFileName()); + static void setKitCMakeOptions(Kit *k, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) + { + using namespace CMakeProjectManager; + auto configMap = cMakeConfigToMap(CMakeConfigurationKitAspect::configuration(k)); + + // CMake ToolChain file for ghs handles CMAKE_*_COMPILER autonomously + if (mcuTarget->toolChainPackage()->toolchainType() != McuToolChainPackage::ToolChainType::GHS + && mcuTarget->toolChainPackage()->toolchainType() + != McuToolChainPackage::ToolChainType::GHSArm) { + configMap.insert("CMAKE_CXX_COMPILER", "%{Compiler:Executable:Cxx}"); + configMap.insert("CMAKE_C_COMPILER", "%{Compiler:Executable:C}"); + } - config.append( - CMakeConfigItem("CMAKE_TOOLCHAIN_FILE", cMakeToolchainFile.toString().toUtf8())); - if (!cMakeToolchainFile.exists()) { + if (!mcuTarget->toolChainPackage()->isDesktopToolchain()) { + const FilePath cMakeToolchainFile = qtForMCUsSdkPackage->path().pathAppended( + "lib/cmake/Qul/toolchain/" + + mcuTarget->toolChainPackage()->cmakeToolChainFileName()); + + configMap.insert("CMAKE_TOOLCHAIN_FILE", cMakeToolchainFile.toString().toUtf8()); + if (!cMakeToolchainFile.exists()) { + printMessage( + McuTarget::tr( + "Warning for target %1: missing CMake toolchain file expected at %2.") + .arg(generateKitNameFromTarget(mcuTarget), + cMakeToolchainFile.toUserOutput()), + false); + } + } + + const FilePath generatorsPath = qtForMCUsSdkPackage->path().pathAppended( + "/lib/cmake/Qul/QulGenerators.cmake"); + configMap.insert("QUL_GENERATORS", generatorsPath.toString().toUtf8()); + if (!generatorsPath.exists()) { printMessage(McuTarget::tr( - "Warning for target %1: missing CMake toolchain file expected at %2.") - .arg(kitName(mcuTarget), - cMakeToolchainFile.toUserOutput()), + "Warning for target %1: missing QulGenerators expected at %2.") + .arg(generateKitNameFromTarget(mcuTarget), + generatorsPath.toUserOutput()), false); } - } - const FilePath generatorsPath = qulDir.pathAppended("/lib/cmake/Qul/QulGenerators.cmake"); - config.append(CMakeConfigItem("QUL_GENERATORS", generatorsPath.toString().toUtf8())); - if (!generatorsPath.exists()) { - printMessage(McuTarget::tr("Warning for target %1: missing QulGenerators expected at %2.") - .arg(kitName(mcuTarget), generatorsPath.toUserOutput()), - false); - } + configMap.insert("QUL_PLATFORM", mcuTarget->platform().name.toUtf8()); + + if (mcuTarget->colorDepth() != McuTarget::UnspecifiedColorDepth) + configMap.insert("QUL_COLOR_DEPTH", QString::number(mcuTarget->colorDepth()).toLatin1()); + if (McuSupportOptions::kitsNeedQtVersion()) + configMap.insert("CMAKE_PREFIX_PATH", "%{Qt:QT_INSTALL_PREFIX}"); - config.append(CMakeConfigItem("QUL_PLATFORM", mcuTarget->platform().name.toUtf8())); - - if (mcuTarget->colorDepth() != McuTarget::UnspecifiedColorDepth) - config.append(CMakeConfigItem("QUL_COLOR_DEPTH", - QString::number(mcuTarget->colorDepth()).toLatin1())); - if (McuSupportOptions::kitsNeedQtVersion()) - config.append(CMakeConfigItem("CMAKE_PREFIX_PATH", "%{Qt:QT_INSTALL_PREFIX}")); - CMakeConfigurationKitAspect::setConfiguration(k, config); - - if (HostOsInfo::isWindowsHost()) { - auto type = mcuTarget->toolChainPackage()->toolchainType(); - if (type == McuToolChainPackage::ToolChainType::GHS || type == McuToolChainPackage::ToolChainType::GHSArm) { - // See https://bugreports.qt.io/browse/UL-4247?focusedCommentId=565802&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-565802 - // and https://bugreports.qt.io/browse/UL-4247?focusedCommentId=565803&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-565803 - CMakeGeneratorKitAspect::setGenerator(k, "NMake Makefiles JOM"); + if (HostOsInfo::isWindowsHost()) { + auto type = mcuTarget->toolChainPackage()->toolchainType(); + if (type == McuToolChainPackage::ToolChainType::GHS + || type == McuToolChainPackage::ToolChainType::GHSArm) { + // See https://bugreports.qt.io/browse/UL-4247?focusedCommentId=565802&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-565802 + // and https://bugreports.qt.io/browse/UL-4247?focusedCommentId=565803&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-565803 + CMakeGeneratorKitAspect::setGenerator(k, "NMake Makefiles JOM"); + } } + + auto processPackage = [&configMap](const McuAbstractPackage *package) { + if (!package->cmakeVariableName().isEmpty()) + configMap.insert(package->cmakeVariableName().toUtf8(), + package->path().toUserOutput().toUtf8()); + }; + + for (auto package : mcuTarget->packages()) + processPackage(package); + processPackage(qtForMCUsSdkPackage); + + CMakeConfigurationKitAspect::setConfiguration(k, mapToCMakeConfig(configMap)); } -} -static void setKitQtVersionOptions(Kit *k) + static void setKitQtVersionOptions(Kit *k) + { + if (!McuSupportOptions::kitsNeedQtVersion()) + QtSupport::QtKitAspect::setQtVersion(k, nullptr); + // else: auto-select a Qt version + } + +}; // class McuKitFactory + +// Construct kit +Kit *newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk) { - if (!McuSupportOptions::kitsNeedQtVersion()) - QtSupport::QtKitAspect::setQtVersion(k, nullptr); - // else: auto-select a Qt version + const auto init = [mcuTarget, qtForMCUsSdk](Kit *k) { + KitGuard kitGuard(k); + + McuKitFactory::setKitProperties(generateKitNameFromTarget(mcuTarget), + k, + mcuTarget, + qtForMCUsSdk->path()); + McuKitFactory::setKitDevice(k, mcuTarget); + McuKitFactory::setKitToolchains(k, mcuTarget->toolChainPackage()); + McuKitFactory::setKitDebugger(k, mcuTarget->toolChainPackage()); + McuKitFactory::setKitEnvironment(k, mcuTarget, qtForMCUsSdk); + McuKitFactory::setKitCMakeOptions(k, mcuTarget, qtForMCUsSdk); + McuKitFactory::setKitDependencies(k, mcuTarget, qtForMCUsSdk); + McuKitFactory::setKitQtVersionOptions(k); + + k->setup(); + k->fix(); + }; + + return KitManager::registerKit(init); } -QString kitName(const McuTarget *mcuTarget) +// Kit Information +QString generateKitNameFromTarget(const McuTarget *mcuTarget) { const McuToolChainPackage *tcPkg = mcuTarget->toolChainPackage(); const QString compilerName = tcPkg && !tcPkg->isDesktopToolchain() @@ -263,6 +367,36 @@ QString kitName(const McuTarget *mcuTarget) compilerName); } +// Kit Information +QVersionNumber kitQulVersion(const Kit *kit) +{ + return QVersionNumber::fromString( + kit->value(McuSupport::Constants::KIT_MCUTARGET_SDKVERSION_KEY).toString()); +} + +// Kit Information +static FilePath kitDependencyPath(const Kit *kit, const QString &variableName) +{ + const auto config = CMakeConfigurationKitAspect::configuration(kit).toList(); + const auto keyName = variableName.toUtf8(); + for (const CMakeConfigItem &configItem : config) { + if (configItem.key == keyName) + return FilePath::fromUserInput(QString::fromUtf8(configItem.value)); + } + return FilePath(); +} + +// Kit Information +bool kitIsUpToDate(const Kit *kit, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage) +{ + return kitQulVersion(kit) == mcuTarget->qulVersion() + && kitDependencyPath(kit, qtForMCUsSdkPackage->cmakeVariableName()).toUserOutput() + == qtForMCUsSdkPackage->path().toUserOutput(); +} + +// Queries QList<Kit *> existingKits(const McuTarget *mcuTarget) { using namespace Constants; @@ -279,36 +413,38 @@ QList<Kit *> existingKits(const McuTarget *mcuTarget) }); } -QList<Kit *> matchingKits(const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) +// Queries +QList<Kit *> matchingKits(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdkPackage) { return Utils::filtered(existingKits(mcuTarget), [mcuTarget, qtForMCUsSdkPackage](Kit *kit) { return kitIsUpToDate(kit, mcuTarget, qtForMCUsSdkPackage); }); } +// Queries QList<Kit *> upgradeableKits(const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) + const McuAbstractPackage *qtForMCUsSdkPackage) { return Utils::filtered(existingKits(mcuTarget), [mcuTarget, qtForMCUsSdkPackage](Kit *kit) { return !kitIsUpToDate(kit, mcuTarget, qtForMCUsSdkPackage); }); } +// Queries QList<Kit *> kitsWithMismatchedDependencies(const McuTarget *mcuTarget) { return Utils::filtered(existingKits(mcuTarget), [mcuTarget](Kit *kit) { - const auto environment = Utils::NameValueDictionary( - Utils::NameValueItem::toStringList(EnvironmentKitAspect::environmentChanges(kit))); - return Utils::anyOf(mcuTarget->packages(), - [&environment](const McuAbstractPackage *package) { - return !package->environmentVariableName().isEmpty() - && environment.value(package->environmentVariableName()) - != package->path().toUserOutput(); - }); + const auto entries = Utils::NameValueDictionary( + McuDependenciesKitAspect::configuration(kit)); + return Utils::anyOf(mcuTarget->packages(), [&entries](const McuAbstractPackage *package) { + const QString cmakeVariableName = package->cmakeVariableName(); + return !cmakeVariableName.isEmpty() + && entries.value(cmakeVariableName) != package->path().toUserOutput(); + }); }); } +// Queries QList<Kit *> outdatedKits() { return Utils::filtered(KitManager::kits(), [](Kit *kit) { @@ -317,57 +453,7 @@ QList<Kit *> outdatedKits() }); } -void removeOutdatedKits() -{ - for (auto kit : outdatedKits()) - KitManager::deregisterKit(kit); -} - -Kit *newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk) -{ - const auto init = [mcuTarget, qtForMCUsSdk](Kit *k) { - KitGuard kitGuard(k); - - setKitProperties(kitName(mcuTarget), k, mcuTarget, qtForMCUsSdk->path()); - setKitDevice(k, mcuTarget); - setKitToolchains(k, mcuTarget->toolChainPackage()); - setKitDebugger(k, mcuTarget->toolChainPackage()); - McuSupportOptions::setKitEnvironment(k, mcuTarget, qtForMCUsSdk); - setKitDependencies(k, mcuTarget, qtForMCUsSdk); - setKitCMakeOptions(k, mcuTarget, qtForMCUsSdk->path()); - setKitQtVersionOptions(k); - - k->setup(); - k->fix(); - }; - - return KitManager::registerKit(init); -} - -QVersionNumber kitQulVersion(const Kit *kit) -{ - return QVersionNumber::fromString( - kit->value(McuSupport::Constants::KIT_MCUTARGET_SDKVERSION_KEY).toString()); -} - -static FilePath kitDependencyPath(const Kit *kit, const QString &variableName) -{ - for (const NameValueItem &nameValueItem : EnvironmentKitAspect::environmentChanges(kit)) { - if (nameValueItem.name == variableName) - return FilePath::fromUserInput(nameValueItem.value); - } - return FilePath(); -} - -bool kitIsUpToDate(const Kit *kit, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) -{ - return kitQulVersion(kit) == mcuTarget->qulVersion() - && kitDependencyPath(kit, qtForMCUsSdkPackage->environmentVariableName()).toUserOutput() - == qtForMCUsSdkPackage->path().toUserOutput(); -} - +// Maintenance void createAutomaticKits() { auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); @@ -385,14 +471,16 @@ void createAutomaticKits() break; } case McuAbstractPackage::Status::InvalidPath: { - printMessage(McuPackage::tr("Path %1 does not exist. Add the path in Tools > Options > " - "Devices > MCU.") + printMessage(McuPackage::tr( + "Path %1 does not exist. Add the path in Tools > Options > " + "Devices > MCU.") .arg(qtForMCUsPackage->path().toUserOutput()), true); break; } case McuAbstractPackage::Status::EmptyPath: { - printMessage(McuPackage::tr("Missing %1. Add the path in Tools > Options > Devices > MCU.") + printMessage(McuPackage::tr( + "Missing %1. Add the path in Tools > Options > Devices > MCU.") .arg(qtForMCUsPackage->detectionPath().toUserOutput()), true); return; @@ -404,9 +492,11 @@ void createAutomaticKits() } if (CMakeProjectManager::CMakeToolManager::cmakeTools().isEmpty()) { - printMessage(McuPackage::tr("No CMake tool was detected. Add a CMake tool in Tools > Options > " - "Kits > CMake."), - true); + printMessage( + McuPackage::tr( + "No CMake tool was detected. Add a CMake tool in Tools > Options > " + "Kits > CMake."), + true); return; } @@ -441,6 +531,11 @@ void createAutomaticKits() delete qtForMCUsPackage; } +// Maintenance +// when the SDK version has changed, and the user has given permission +// to upgrade, create new kits with current data, for the targets +// for which kits already existed +// function parameter is option to keep the old ones or delete them void upgradeKitsByCreatingNewPackage(UpgradeOption upgradeOption) { if (upgradeOption == UpgradeOption::Ignore) @@ -474,16 +569,26 @@ void upgradeKitsByCreatingNewPackage(UpgradeOption upgradeOption) delete qtForMCUsPackage; } +// Maintenance +// when the user manually asks to upgrade a specific kit +// button is available if SDK version changed void upgradeKitInPlace(ProjectExplorer::Kit *kit, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdk) + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdk) { - setKitProperties(kitName(mcuTarget), kit, mcuTarget, qtForMCUsSdk->path()); - McuSupportOptions::setKitEnvironment(kit, mcuTarget, qtForMCUsSdk); - setKitDependencies(kit, mcuTarget, qtForMCUsSdk); + McuKitFactory::setKitProperties(generateKitNameFromTarget(mcuTarget), + kit, + mcuTarget, + qtForMCUsSdk->path()); + McuKitFactory::setKitEnvironment(kit, mcuTarget, qtForMCUsSdk); + McuKitFactory::setKitCMakeOptions(kit, mcuTarget, qtForMCUsSdk); + McuKitFactory::setKitDependencies(kit, mcuTarget, qtForMCUsSdk); } -void fixKitsDependencies() +// Maintenance +// If the user changed a path in the McuSupport plugin's UI +// update the corresponding cmake variables in all existing kits +void updatePathsInExistingKits() { auto qtForMCUsPackage = Sdk::createQtForMCUsPackage(); @@ -493,7 +598,23 @@ void fixKitsDependencies() for (const auto &target : qAsConst(repo.mcuTargets)) { if (target->isValid()) { for (auto *kit : kitsWithMismatchedDependencies(target)) { - McuSupportOptions::updateKitEnvironment(kit, target); + auto changes = cMakeConfigToMap(CMakeConfigurationKitAspect::configuration(kit)); + + const auto updateForPackage = [&changes](const McuAbstractPackage *package) { + if (!package->cmakeVariableName().isEmpty() && package->isValidStatus()) { + changes.insert(package->cmakeVariableName().toUtf8(), + package->path().toUserOutput().toUtf8()); + } + }; + + for (auto package : target->packages()) { + updateForPackage(package); + } + updateForPackage(qtForMCUsPackage); + + CMakeConfigurationKitAspect::setConfiguration(kit, + CMakeProjectManager::CMakeConfig( + mapToCMakeConfig(changes))); } } } @@ -502,9 +623,9 @@ void fixKitsDependencies() delete qtForMCUsPackage; } -/** - * @brief Fix/update existing kits if needed - */ +// Maintenance +// if we changed minor details in the kits across versions of QtCreator +// this function updates those details in existing older kits void fixExistingKits() { for (Kit *kit : KitManager::kits()) { @@ -567,7 +688,8 @@ void fixExistingKits() for (const auto &target : qAsConst(repo.mcuTargets)) for (auto kit : existingKits(target)) { if (McuDependenciesKitAspect::dependencies(kit).isEmpty()) { - setKitDependencies(kit, target, qtForMCUsPackage); + McuKitFactory::setKitCMakeOptions(kit, target, qtForMCUsPackage); + McuKitFactory::setKitDependencies(kit, target, qtForMCUsPackage); } } @@ -576,6 +698,13 @@ void fixExistingKits() delete qtForMCUsPackage; } +// Maintenance +// removes kits with older schemes +void removeOutdatedKits() +{ + for (auto kit : outdatedKits()) + KitManager::deregisterKit(kit); +} + } // namespace McuKitManager -} // namespace Internal -} // namespace McuSupport +} // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcukitmanager.h b/src/plugins/mcusupport/mcukitmanager.h index 1814fb26432..64eefff3efa 100644 --- a/src/plugins/mcusupport/mcukitmanager.h +++ b/src/plugins/mcusupport/mcukitmanager.h @@ -25,8 +25,8 @@ #pragma once -#include <utils/environmentfwd.h> #include "mcusupport_global.h" +#include <utils/environmentfwd.h> #include <QCoreApplication> #include <QObject> @@ -44,42 +44,41 @@ class McuAbstractPackage; class McuToolChainPackage; class McuTarget; -namespace McuKitManager -{ - enum class UpgradeOption { - Ignore, - Keep, - Replace - }; +namespace McuKitManager { +enum class UpgradeOption { Ignore, Keep, Replace }; - // Creating kits: - ProjectExplorer::Kit *newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk); - void createAutomaticKits(); +// Kit Factory +ProjectExplorer::Kit *newKit(const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk); - // Querying the kits: - QList<ProjectExplorer::Kit *> existingKits(const McuTarget *mcuTarget); - QList<ProjectExplorer::Kit *> matchingKits(const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage); - QList<ProjectExplorer::Kit *> upgradeableKits( - const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdkPackage); - QList<ProjectExplorer::Kit *> kitsWithMismatchedDependencies(const McuTarget *mcuTarget); +// Kit information +QString generateKitNameFromTarget(const McuTarget *mcuTarget); +QVersionNumber kitQulVersion(const ProjectExplorer::Kit *kit); +bool kitIsUpToDate(const ProjectExplorer::Kit *kit, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage); - // Upgrading kits: - void upgradeKitsByCreatingNewPackage(UpgradeOption upgradeOption); - void upgradeKitInPlace(ProjectExplorer::Kit *kit, const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdk); +// Queries +QList<ProjectExplorer::Kit *> existingKits(const McuTarget *mcuTarget); +QList<ProjectExplorer::Kit *> matchingKits(const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage); +QList<ProjectExplorer::Kit *> upgradeableKits(const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdkPackage); +QList<ProjectExplorer::Kit *> kitsWithMismatchedDependencies(const McuTarget *mcuTarget); +QList<ProjectExplorer::Kit *> outdatedKits(); - // Fixing kits: - void fixKitsDependencies(); - void fixExistingKits(); +// Maintenance +void createAutomaticKits(); +void upgradeKitsByCreatingNewPackage(UpgradeOption upgradeOption); +void upgradeKitInPlace(ProjectExplorer::Kit *kit, + const McuTarget *mcuTarget, + const McuAbstractPackage *qtForMCUsSdk); - // Outdated kits: - QList<ProjectExplorer::Kit *> outdatedKits(); - void removeOutdatedKits(); +// Fixing kits: +void updatePathsInExistingKits(); +void fixExistingKits(); - // Querying kits: - QString kitName(const McuTarget* mcuTarget); - QVersionNumber kitQulVersion(const ProjectExplorer::Kit *kit); - bool kitIsUpToDate(const ProjectExplorer::Kit *kit, const McuTarget *mcuTarget, const McuAbstractPackage *qtForMCUsSdkPackage); +// Outdated kits: +void removeOutdatedKits(); } // namespace McuKitManager } // namespace Internal diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index 9d717b3154d..cd4d0f0661b 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -53,6 +53,7 @@ McuPackage::McuPackage(const QString &label, const FilePath &defaultPath, const FilePath &detectionPath, const QString &settingsKey, + const QString &cmakeVarName, const QString &envVarName, const QString &downloadUrl, const McuPackageVersionDetector *versionDetector, @@ -64,6 +65,7 @@ McuPackage::McuPackage(const QString &label, , m_settingsKey(settingsKey) , m_versionDetector(versionDetector) , m_relativePathModifier(relativePathModifier) + , m_cmakeVariableName(cmakeVarName) , m_environmentVariableName(envVarName) , m_downloadUrl(downloadUrl) , m_addToSystemPath(addToSystemPath) @@ -76,6 +78,16 @@ QString McuPackage::label() const return m_label; } +QString McuPackage::settingsKey() const +{ + return m_settingsKey; +} + +const QString &McuPackage::cmakeVariableName() const +{ + return m_cmakeVariableName; +} + const QString &McuPackage::environmentVariableName() const { return m_environmentVariableName; @@ -148,7 +160,6 @@ bool McuPackage::isValidStatus() const return m_status == Status::ValidPackage || m_status == Status::ValidPackageMismatchedVersion; } - void McuPackage::updateStatusUi() { switch (m_status) { @@ -269,15 +280,15 @@ QWidget *McuPackage::widget() return m_widget; } - McuToolChainPackage::McuToolChainPackage(const QString &label, const FilePath &defaultPath, const FilePath &detectionPath, const QString &settingsKey, McuToolChainPackage::ToolChainType type, + const QString &cmakeVarName, const QString &envVarName, const McuPackageVersionDetector *versionDetector) - : McuPackage(label, defaultPath, detectionPath, settingsKey, envVarName, {}, versionDetector) + : McuPackage(label, defaultPath, detectionPath, settingsKey, cmakeVarName, envVarName, {}, versionDetector) , m_type(type) {} @@ -466,5 +477,4 @@ QVariant McuToolChainPackage::debuggerId() const return DebuggerItemManager::registerDebugger(newDebugger); } - } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcupackage.h b/src/plugins/mcusupport/mcupackage.h index 5a57fad9658..91068f786ec 100644 --- a/src/plugins/mcusupport/mcupackage.h +++ b/src/plugins/mcusupport/mcupackage.h @@ -57,7 +57,8 @@ public: const Utils::FilePath &defaultPath, const Utils::FilePath &detectionPath, const QString &settingsKey, - const QString &envVarName = {}, + const QString &cmakeVarName, + const QString &envVarName, const QString &downloadUrl = {}, const McuPackageVersionDetector *versionDetector = nullptr, const bool addToPath = false, @@ -66,6 +67,7 @@ public: ~McuPackage() override = default; QString label() const override; + const QString &cmakeVariableName() const override; const QString &environmentVariableName() const override; bool isAddToSystemPath() const override; void setVersions(const QStringList &versions) override; @@ -74,6 +76,7 @@ public: Utils::FilePath path() const override; Utils::FilePath defaultPath() const override; Utils::FilePath detectionPath() const override; + QString settingsKey() const final; void updateStatus() override; Status status() const override; @@ -102,6 +105,7 @@ private: Utils::FilePath m_relativePathModifier; // relative path to m_path to be returned by path() QString m_detectedVersion; QStringList m_versions; + const QString m_cmakeVariableName; const QString m_environmentVariableName; const QString m_downloadUrl; const bool m_addToSystemPath; @@ -119,6 +123,7 @@ public: const Utils::FilePath &detectionPath, const QString &settingsKey, ToolChainType toolchainType, + const QString &cmakeVarName = {}, const QString &envVarName = {}, const McuPackageVersionDetector *versionDetector = nullptr); diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs index 22adc0ff2af..e20751ab592 100644 --- a/src/plugins/mcusupport/mcusupport.qbs +++ b/src/plugins/mcusupport/mcusupport.qbs @@ -23,6 +23,8 @@ QtcPlugin { "mcupackage.h", "mcutarget.cpp", "mcutarget.h", + "mcutargetfactory.cpp", + "mcutargetfactory.h", "mcusupport.qrc", "mcusupport_global.h", "mcusupportconstants.h", @@ -42,8 +44,6 @@ QtcPlugin { "mcusupportrunconfiguration.h", "mcusupportversiondetection.cpp", "mcusupportversiondetection.h", - "mcusupportcmakemapper.h", - "mcusupportcmakemapper.cpp", "mcutargetdescription.h", "mcukitinformation.cpp", "mcukitinformation.h" diff --git a/src/plugins/mcusupport/mcusupportcmakemapper.cpp b/src/plugins/mcusupport/mcusupportcmakemapper.cpp deleted file mode 100644 index 7f69f4f6716..00000000000 --- a/src/plugins/mcusupport/mcusupportcmakemapper.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ -#include "mcusupportcmakemapper.h" -#include "utils/namevalueitem.h" - -#include <utils/algorithm.h> - -namespace { -static const QHash<QString, QString> &envVarToCMakeVarMapping() -{ - static const QHash<QString, QString> mapping = { - {"EVK_MIMXRT1060_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"EVK_MIMXRT1064_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"EVK_MIMXRT595_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"EVK_MIMXRT1170_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"EVKB_IMXRT1050_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"STM32Cube_FW_F7_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"STM32Cube_FW_F4_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"STM32Cube_FW_L4_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"STM32Cube_FW_H7_SDK_PATH", "QUL_BOARD_SDK_DIR"}, - {"RGL_DIR", "QUL_BOARD_SDK_DIR"}, - {"TVII_GRAPHICS_DRIVER_DIR", "QUL_BOARD_SDK_DIR"}, - {"EK_RA6M3G_FSP_PATH", "QUL_BOARD_SDK_DIR"}, - {"ARMGCC_DIR", "QUL_TARGET_TOOLCHAIN_DIR"}, - {"IAR_ARM_COMPILER_DIR", "QUL_TARGET_TOOLCHAIN_DIR"}, - {"GHS_COMPILER_DIR", "QUL_TARGET_TOOLCHAIN_DIR"}, - {"GHS_ARM_COMPILER_DIR", "QUL_TARGET_TOOLCHAIN_DIR"}, - {"EVK_MIMXRT1170_FREERTOS_PATH", "FREERTOS_DIR"}, - {"IMXRT1050_FREERTOS_DIR", "FREERTOS_DIR"}, - {"IMXRT1064_FREERTOS_DIR", "FREERTOS_DIR"}, - {"IMXRT595_FREERTOS_DIR", "FREERTOS_DIR"}, - {"STM32F7_FREERTOS_DIR", "FREERTOS_DIR"}, - {"eFlashLoad_PATH", "eFlashLoad_PATH"}, - {"RenesasFlashProgrammer_PATH", "RENESAS_FLASH_PROGRAMMER_PATH"}, - {"MCUXpressoIDE_PATH", "MCUXPRESSO_IDE_PATH"}, - {"JLINK_PATH", "JLINK_PATH"}, - {"CYPRESS_AUTO_FLASH_UTILITY_DIR", "INFINEON_AUTO_FLASH_UTILITY_DIR"}, - {"EK_RA6M3G_E2_PROJECT_PATH", "EK_RA6M3G_E2_PROJECT_PATH"}, - }; - return mapping; -} -} // namespace - -QList<CMakeProjectManager::CMakeConfigItem> McuSupport::Internal::mapEnvVarsToQul2xCmakeVars( - const Utils::EnvironmentItems &envVars) -{ - const auto &mapping = envVarToCMakeVarMapping(); - auto cmakeVars - = Utils::transform(envVars, [mapping](const Utils::EnvironmentItem &envVar) { - return CMakeProjectManager::CMakeConfigItem(mapping.value(envVar.name, "").toUtf8(), - envVar.value.toUtf8()); - }).toList(); - - return Utils::filtered(cmakeVars, [](const CMakeProjectManager::CMakeConfigItem &item) { - return !item.key.isEmpty(); - }); -} diff --git a/src/plugins/mcusupport/mcusupportcmakemapper.h b/src/plugins/mcusupport/mcusupportcmakemapper.h deleted file mode 100644 index f5da2092def..00000000000 --- a/src/plugins/mcusupport/mcusupportcmakemapper.h +++ /dev/null @@ -1,35 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ -#pragma once - -#include "cmakeprojectmanager/cmakeconfigitem.h" -#include "utils/environmentfwd.h" - -namespace McuSupport { -namespace Internal { -QList<CMakeProjectManager::CMakeConfigItem> mapEnvVarsToQul2xCmakeVars( - const Utils::EnvironmentItems &envVars); -} -} // namespace McuSupport diff --git a/src/plugins/mcusupport/mcusupportconstants.h b/src/plugins/mcusupport/mcusupportconstants.h index ec46414fb7f..7b5e66f8271 100644 --- a/src/plugins/mcusupport/mcusupportconstants.h +++ b/src/plugins/mcusupport/mcusupportconstants.h @@ -43,6 +43,7 @@ const char KIT_MCUTARGET_TOOCHAIN_KEY[] = "McuSupport.McuTargetToolchain"; const char SETTINGS_GROUP[] = "McuSupport"; const char SETTINGS_KEY_PACKAGE_PREFIX[] = "Package_"; +const char SETTINGS_KEY_FREERTOS_PREFIX[] = "FreeRTOSSourcePackage_"; const char SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK[] = "QtForMCUsSdk"; // Key known by SDK installer const char SETTINGS_KEY_AUTOMATIC_KIT_CREATION[] = "AutomaticKitCreation"; diff --git a/src/plugins/mcusupport/mcusupportdevice.cpp b/src/plugins/mcusupport/mcusupportdevice.cpp index 46fb430ac22..876bddd06fd 100644 --- a/src/plugins/mcusupport/mcusupportdevice.cpp +++ b/src/plugins/mcusupport/mcusupportdevice.cpp @@ -26,7 +26,6 @@ #include "mcusupportdevice.h" #include "mcusupportconstants.h" -#include <projectexplorer/devicesupport/deviceprocess.h> #include <projectexplorer/runcontrol.h> using namespace ProjectExplorer; diff --git a/src/plugins/mcusupport/mcusupportoptions.cpp b/src/plugins/mcusupport/mcusupportoptions.cpp index df06b96767e..aedfb74440a 100644 --- a/src/plugins/mcusupport/mcusupportoptions.cpp +++ b/src/plugins/mcusupport/mcusupportoptions.cpp @@ -29,7 +29,6 @@ #include "mcutarget.h" #include "mcukitmanager.h" #include "mcukitinformation.h" -#include "mcusupportcmakemapper.h" #include "mcusupportconstants.h" #include "mcusupportsdk.h" #include "mcusupportplugin.h" @@ -145,88 +144,8 @@ void McuSupportOptions::setQulDir(const FilePath &dir) FilePath McuSupportOptions::qulDirFromSettings() { return Sdk::packagePathFromSettings(Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, - QSettings::UserScope, {}); -} - -void McuSupportOptions::remapQul2xCmakeVars(Kit *kit, const EnvironmentItems &envItems) -{ - const auto cmakeVars = mapEnvVarsToQul2xCmakeVars(envItems); - const auto cmakeVarNames = Utils::transform(cmakeVars, &CMakeConfigItem::key); - - // First filter out all Qul2.x CMake vars - auto config = Utils::filtered(CMakeConfigurationKitAspect::configuration(kit), - [&](const auto &configItem) { - return !cmakeVarNames.contains(configItem.key); - }); - // Then append them with new values - config.append(cmakeVars); - CMakeConfigurationKitAspect::setConfiguration(kit, config); -} - -static bool expectsCmakeVars(const McuTarget *mcuTarget) -{ - return mcuTarget->qulVersion() >= QVersionNumber{2, 0}; -} - -void McuSupportOptions::setKitEnvironment(Kit *k, - const McuTarget *mcuTarget, - const McuAbstractPackage *qtForMCUsSdkPackage) -{ - EnvironmentItems changes; - QStringList pathAdditions; - - // The Desktop version depends on the Qt shared libs in Qul_DIR/bin. - // If CMake's fileApi is avaialble, we can rely on the "Add library search path to PATH" - // feature of the run configuration. Otherwise, we just prepend the path, here. - if (mcuTarget->toolChainPackage()->isDesktopToolchain() - && !CMakeProjectManager::CMakeToolManager::defaultCMakeTool()->hasFileApi()) - pathAdditions.append(qtForMCUsSdkPackage->path().pathAppended("bin").toUserOutput()); - - auto processPackage = [&pathAdditions, &changes](const McuAbstractPackage *package) { - if (package->isAddToSystemPath()) - pathAdditions.append(package->path().toUserOutput()); - if (!package->environmentVariableName().isEmpty()) - changes.append({package->environmentVariableName(), package->path().toUserOutput()}); - }; - for (auto package : mcuTarget->packages()) - processPackage(package); - processPackage(qtForMCUsSdkPackage); - - if (McuSupportOptions::kitsNeedQtVersion()) - changes.append({QLatin1String("LD_LIBRARY_PATH"), "%{Qt:QT_INSTALL_LIBS}"}); - - // Hack, this problem should be solved in lower layer - if (expectsCmakeVars(mcuTarget)) { - McuSupportOptions::remapQul2xCmakeVars(k, changes); - } - - EnvironmentKitAspect::setEnvironmentChanges(k, changes); -} - -void McuSupportOptions::updateKitEnvironment(Kit *k, const McuTarget *mcuTarget) -{ - EnvironmentItems changes = EnvironmentKitAspect::environmentChanges(k); - for (auto package : mcuTarget->packages()) { - const QString varName = package->environmentVariableName(); - if (!varName.isEmpty() && package->isValidStatus()) { - const int index = Utils::indexOf(changes, [varName](const EnvironmentItem &item) { - return item.name == varName; - }); - const EnvironmentItem item = {package->environmentVariableName(), - package->path().toUserOutput()}; - if (index != -1) - changes.replace(index, item); - else - changes.append(item); - } - } - - // Hack, this problem should be solved in lower layer - if (expectsCmakeVars(mcuTarget)) { - remapQul2xCmakeVars(k, changes); - } - - EnvironmentKitAspect::setEnvironmentChanges(k, changes); + QSettings::UserScope, + {}); } McuKitManager::UpgradeOption McuSupportOptions::askForKitUpgrades() @@ -250,13 +169,11 @@ McuKitManager::UpgradeOption McuSupportOptions::askForKitUpgrades() return McuKitManager::UpgradeOption::Ignore; } - void McuSupportOptions::deletePackagesAndTargets() { sdkRepository.deletePackagesAndTargets(); } - void McuSupportOptions::checkUpgradeableKits() { if (!qtForMCUsSdkPackage->isValidStatus() || sdkRepository.mcuTargets.length() == 0) @@ -276,7 +193,6 @@ bool McuSupportOptions::kitsNeedQtVersion() return !HostOsInfo::isWindowsHost(); } - bool McuSupportOptions::automaticKitCreationEnabled() const { return m_automaticKitCreation; diff --git a/src/plugins/mcusupport/mcusupportoptions.h b/src/plugins/mcusupport/mcusupportoptions.h index 04dbb24b6ae..144462a064d 100644 --- a/src/plugins/mcusupport/mcusupportoptions.h +++ b/src/plugins/mcusupport/mcusupportoptions.h @@ -74,11 +74,6 @@ public: McuSdkRepository sdkRepository; void setQulDir(const Utils::FilePath &dir); - static void setKitEnvironment(ProjectExplorer::Kit *, - const McuTarget *, - const McuAbstractPackage *); - static void updateKitEnvironment(ProjectExplorer::Kit *, const McuTarget *); - static void remapQul2xCmakeVars(ProjectExplorer::Kit *, const Utils::EnvironmentItems &); static Utils::FilePath qulDirFromSettings(); static McuKitManager::UpgradeOption askForKitUpgrades(); diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index 2b96f02ce74..3229759df80 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -24,12 +24,12 @@ ****************************************************************************/ #include "mcusupportoptionspage.h" +#include "mcukitmanager.h" #include "mcupackage.h" -#include "mcutarget.h" #include "mcusupportconstants.h" #include "mcusupportoptions.h" #include "mcusupportsdk.h" -#include "mcukitmanager.h" +#include "mcutarget.h" #include <cmakeprojectmanager/cmakeprojectconstants.h> #include <cmakeprojectmanager/cmaketoolmanager.h> @@ -169,7 +169,8 @@ McuSupportOptionsWidget::McuSupportOptionsWidget() m_kitUpdatePushButton = new QPushButton(tr("Update Kit")); m_kitUpdatePushButton->setSizePolicy(m_kitCreationPushButton->sizePolicy()); connect(m_kitUpdatePushButton, &QPushButton::clicked, this, [this] { - for (auto kit: McuKitManager::upgradeableKits(currentMcuTarget(), m_options.qtForMCUsSdkPackage)) + for (auto kit : + McuKitManager::upgradeableKits(currentMcuTarget(), m_options.qtForMCUsSdkPackage)) McuKitManager::upgradeKitInPlace(kit, currentMcuTarget(), m_options.qtForMCUsSdkPackage); updateStatus(); }); @@ -220,8 +221,8 @@ void McuSupportOptionsWidget::updateStatus() m_kitCreationPushButton->setVisible(mcuTargetValid); m_kitUpdatePushButton->setVisible(mcuTargetValid); if (mcuTargetValid) { - const bool hasMatchingKits = !McuKitManager::matchingKits( - mcuTarget, m_options.qtForMCUsSdkPackage).isEmpty(); + const bool hasMatchingKits + = !McuKitManager::matchingKits(mcuTarget, m_options.qtForMCUsSdkPackage).isEmpty(); const bool hasUpgradeableKits = !hasMatchingKits && !McuKitManager::upgradeableKits( mcuTarget, m_options.qtForMCUsSdkPackage).isEmpty(); @@ -306,7 +307,7 @@ void McuSupportOptionsWidget::apply() if (pathsChanged) { m_options.checkUpgradeableKits(); - McuKitManager::fixKitsDependencies(); + McuKitManager::updatePathsInExistingKits(); } } @@ -315,9 +316,8 @@ void McuSupportOptionsWidget::populateMcuTargetsComboBox() m_options.populatePackagesAndTargets(); m_mcuTargetsComboBox->clear(); m_mcuTargetsComboBox->addItems( - Utils::transform<QStringList>(m_options.sdkRepository.mcuTargets, [](McuTarget *t) { - return McuKitManager::kitName(t); - })); + Utils::transform<QStringList>(m_options.sdkRepository.mcuTargets, + [](McuTarget *t) { return McuKitManager::generateKitNameFromTarget(t); })); updateStatus(); } diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index f0778b04b24..833fb93e51e 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -125,6 +125,7 @@ void McuSupportPlugin::askUserAboutMcuSupportKitsSetup() tr("Create Kits for Qt for MCUs? " "To do it later, select Options > Devices > MCU."), Utils::InfoBarEntry::GlobalSuppression::Enabled); + // clazy:excludeall=connect-3arg-lambda info.addCustomButton(tr("Create Kits for Qt for MCUs"), [setupMcuSupportKits] { ICore::infoBar()->removeInfo(setupMcuSupportKits); QTimer::singleShot(0, []() { ICore::showOptionsDialog(Constants::SETTINGS_ID); }); @@ -144,7 +145,7 @@ void McuSupportPlugin::askUserAboutMcuSupportKitsUpgrade() Utils::InfoBarEntry::GlobalSuppression::Enabled); static McuKitManager::UpgradeOption selectedOption = McuKitManager::UpgradeOption::Keep; - const QStringList options = { tr("Create new kits"), tr("Replace existing kits") }; + const QStringList options = {tr("Create new kits"), tr("Replace existing kits")}; info.setComboInfo(options, [options](const QString &selected) { selectedOption = options.indexOf(selected) == 0 ? McuKitManager::UpgradeOption::Keep : McuKitManager::UpgradeOption::Replace; diff --git a/src/plugins/mcusupport/mcusupportrunconfiguration.cpp b/src/plugins/mcusupport/mcusupportrunconfiguration.cpp index 361fb09b899..fbee8c56f74 100644 --- a/src/plugins/mcusupport/mcusupportrunconfiguration.cpp +++ b/src/plugins/mcusupport/mcusupportrunconfiguration.cpp @@ -27,10 +27,7 @@ #include "mcusupportconstants.h" #include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/devicesupport/deviceusedportsgatherer.h> #include <projectexplorer/project.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> #include <cmakeprojectmanager/cmakekitinformation.h> @@ -99,7 +96,7 @@ public: CommandLine::Raw}; r.workingDirectory = target->activeBuildConfiguration()->buildDirectory(); r.environment = target->activeBuildConfiguration()->environment(); - SimpleTargetRunner::doStart(r, {}); + SimpleTargetRunner::doStart(r); }); } }; diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp index b32af50e5c7..06a85db3de3 100644 --- a/src/plugins/mcusupport/mcusupportsdk.cpp +++ b/src/plugins/mcusupport/mcusupportsdk.cpp @@ -24,14 +24,15 @@ ****************************************************************************/ #include "mcusupportsdk.h" +#include "mcukitmanager.h" #include "mcupackage.h" -#include "mcutarget.h" #include "mcusupportconstants.h" #include "mcusupportoptions.h" -#include "mcukitmanager.h" +#include "mcusupportplugin.h" #include "mcusupportversiondetection.h" +#include "mcutarget.h" #include "mcutargetdescription.h" -#include "mcusupportplugin.h" +#include "mcutargetfactory.h" #include <baremetal/baremetalconstants.h> #include <coreplugin/icore.h> @@ -73,25 +74,108 @@ McuPackage *createQtForMCUsPackage() FileUtils::homePath(), // defaultPath FilePath("bin/qmltocpp").withExecutableSuffix(), // detectionPath Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, // settingsKey + QStringLiteral("Qul_ROOT"), // cmakeVarName QStringLiteral("Qul_DIR")); // envVarName } -static McuToolChainPackage *createMsvcToolChainPackage() +static McuPackageVersionDetector *generatePackageVersionDetector(const QString &envVar) { - return new McuToolChainPackage({}, {}, {}, {}, McuToolChainPackage::ToolChainType::MSVC); + if (envVar.startsWith("EVK")) + return new McuPackageXmlVersionDetector("*_manifest_*.xml", "ksdk", "version", ".*"); + + if (envVar.startsWith("STM32")) + return new McuPackageXmlVersionDetector("package.xml", + "PackDescription", + "Release", + R"(\b(\d+\.\d+\.\d+)\b)"); + + if (envVar.startsWith("RGL")) + return new McuPackageDirectoryVersionDetector("rgl_*_obj_*", R"(\d+\.\d+\.\w+)", false); + + return nullptr; } -static McuToolChainPackage *createGccToolChainPackage() + +/// Create the McuPackage by checking the "boardSdk" property in the JSON file for the board. +/// The name of the environment variable pointing to the the SDK for the board will be defined in the "envVar" property +/// inside the "boardSdk". +McuPackage *createBoardSdkPackage(const McuTargetDescription &desc) { - return new McuToolChainPackage({}, {}, {}, {}, McuToolChainPackage::ToolChainType::GCC); + const auto generateSdkName = [](const QString &envVar) { + qsizetype postfixPos = envVar.indexOf("_SDK_PATH"); + if (postfixPos < 0) { + postfixPos = envVar.indexOf("_DIR"); + } + const QString sdkName = postfixPos > 0 ? envVar.left(postfixPos) : envVar; + return QString{"MCU SDK (%1)"}.arg(sdkName); + }; + const QString sdkName = desc.boardSdk.name.isEmpty() ? generateSdkName(desc.boardSdk.envVar) + : desc.boardSdk.name; + + const FilePath defaultPath = [&] { + const auto envVar = desc.boardSdk.envVar.toLatin1(); + if (qEnvironmentVariableIsSet(envVar)) + return FilePath::fromUserInput(qEnvironmentVariable(envVar)); + if (!desc.boardSdk.defaultPath.isEmpty()) { + FilePath defaultPath = FilePath::fromUserInput(QDir::rootPath() + + desc.boardSdk.defaultPath); + if (defaultPath.exists()) + return defaultPath; + } + return FilePath(); + }(); + + const auto versionDetector = generatePackageVersionDetector(desc.boardSdk.envVar); + + return new McuPackage(sdkName, + defaultPath, + {}, // detection path + desc.boardSdk.envVar, // settings key + "QUL_BOARD_SDK_DIR", // cmake var + desc.boardSdk.envVar, // env var + {}, // download URL + versionDetector); } -static McuToolChainPackage *createUnsupportedToolChainPackage() +McuPackage *createFreeRTOSSourcesPackage(const QString &envVar, + const FilePath &boardSdkDir, + const QString &freeRTOSBoardSdkSubDir) +{ + const QString envVarPrefix = envVar.chopped(int(strlen("_FREERTOS_DIR"))); + + FilePath defaultPath; + if (qEnvironmentVariableIsSet(envVar.toLatin1())) + defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar.toLatin1())); + else if (!boardSdkDir.isEmpty() && !freeRTOSBoardSdkSubDir.isEmpty()) + defaultPath = boardSdkDir / freeRTOSBoardSdkSubDir; + + return new McuPackage(QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix), + defaultPath, + {}, // detection path + QString::fromLatin1("FreeRTOSSourcePackage_%1").arg(envVarPrefix), // settings key + "FREERTOS_DIR", // cmake var + envVar, // env var + "https://freertos.org"); // download url + +} + + +McuToolChainPackage *createUnsupportedToolChainPackage() { return new McuToolChainPackage({}, {}, {}, {}, McuToolChainPackage::ToolChainType::Unsupported); } -static McuToolChainPackage *createArmGccPackage() +static McuToolChainPackage *createMsvcToolChainPackage() +{ + return new McuToolChainPackage({}, {}, {}, {}, McuToolChainPackage::ToolChainType::MSVC); +} + +static McuToolChainPackage *createGccToolChainPackage() +{ + return new McuToolChainPackage({}, {}, {}, {}, McuToolChainPackage::ToolChainType::GCC); +} + +static McuToolChainPackage *createArmGccToolchainPackage() { const char envVar[] = "ARMGCC_DIR"; @@ -119,8 +203,9 @@ static McuToolChainPackage *createArmGccPackage() defaultPath, detectionPath, "GNUArmEmbeddedToolchain", // settingsKey - McuToolChainPackage::ToolChainType::ArmGcc, - envVar, + McuToolChainPackage::ToolChainType::ArmGcc, // toolchainType + "QUL_TARGET_TOOLCHAIN_DIR", // cmake var + envVar, // env var versionDetector); } @@ -139,8 +224,9 @@ static McuToolChainPackage *createGhsToolchainPackage() defaultPath, FilePath("ccv850").withExecutableSuffix(), // detectionPath "GHSToolchain", // settingsKey - McuToolChainPackage::ToolChainType::GHS, - envVar, + McuToolChainPackage::ToolChainType::GHS, // toolchainType + "QUL_TARGET_TOOLCHAIN_DIR", // cmake var + envVar, // env var versionDetector); } @@ -159,8 +245,9 @@ static McuToolChainPackage *createGhsArmToolchainPackage() defaultPath, FilePath("cxarm").withExecutableSuffix(), // detectionPath "GHSArmToolchain", // settingsKey - McuToolChainPackage::ToolChainType::GHSArm, - envVar, + McuToolChainPackage::ToolChainType::GHSArm, // toolchainType + "QUL_TARGET_TOOLCHAIN_DIR", // cmake var + envVar, // env var versionDetector); } @@ -192,37 +279,12 @@ static McuToolChainPackage *createIarToolChainPackage() defaultPath, detectionPath, "IARToolchain", // settings key - McuToolChainPackage::ToolChainType::IAR, - envVar, + McuToolChainPackage::ToolChainType::IAR, // toolchainType + "QUL_TARGET_TOOLCHAIN_DIR", // cmake var + envVar, // env var versionDetector); } -static McuPackage *createRGLPackage() -{ - const char envVar[] = "RGL_DIR"; - - FilePath defaultPath; - if (qEnvironmentVariableIsSet(envVar)) { - defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar)); - } else if (Utils::HostOsInfo::isWindowsHost()) { - const FilePath rglPath = FilePath::fromString(QDir::rootPath()) - / "Renesas_Electronics/D1x_RGL"; - if (rglPath.exists()) { - defaultPath = rglPath; - const FilePaths subDirs = defaultPath.dirEntries( - {{"rgl_ghs_D1Mx_*"}, QDir::Dirs | QDir::NoDotAndDotDot}); - if (subDirs.count() == 1) - defaultPath = subDirs.first(); - } - } - - return new McuPackage("Renesas Graphics Library", - defaultPath, - {}, // detection path - "RGL", - envVar); -} - static McuPackage *createStm32CubeProgrammerPackage() { FilePath defaultPath; @@ -248,7 +310,8 @@ static McuPackage *createStm32CubeProgrammerPackage() defaultPath, detectionPath, "Stm32CubeProgrammer", - {}, // env var + {}, // cmake var + {}, // env var "https://www.st.com/en/development-tools/stm32cubeprog.html", // download url nullptr, // version detector true, // add to path @@ -284,6 +347,7 @@ static McuPackage *createMcuXpressoIdePackage() defaultPath, FilePath("ide/binaries/crt_emu_cm_redlink").withExecutableSuffix(), // detection path "MCUXpressoIDE", // settings key + "MCUXPRESSO_IDE_PATH", // cmake var envVar, "https://www.nxp.com/mcuxpresso/ide"); // download url } @@ -309,8 +373,9 @@ static McuPackage *createCypressProgrammerPackage() auto result = new McuPackage("Cypress Auto Flash Utility", defaultPath, FilePath("/bin/openocd").withExecutableSuffix(), - "CypressAutoFlashUtil", - envVar); + "CypressAutoFlashUtil", // settings key + "INFINEON_AUTO_FLASH_UTILITY_DIR", // cmake var + envVar); // env var return result; } @@ -335,271 +400,17 @@ static McuPackage *createRenesasProgrammerPackage() auto result = new McuPackage("Renesas Flash Programmer", defaultPath, FilePath("rfp-cli").withExecutableSuffix(), - "RenesasFlashProgrammer", - envVar); + "RenesasFlashProgrammer", // settings key + "RENESAS_FLASH_PROGRAMMER_PATH", // cmake var + envVar); // env var return result; } -static McuPackageVersionDetector *generatePackageVersionDetector(QString envVar) -{ - if (envVar.startsWith("EVK")) - return new McuPackageXmlVersionDetector("*_manifest_*.xml", "ksdk", "version", ".*"); - - if (envVar.startsWith("STM32")) - return new McuPackageXmlVersionDetector("package.xml", - "PackDescription", - "Release", - "\\b(\\d+\\.\\d+\\.\\d+)\\b"); - - if (envVar.startsWith("RGL")) - return new McuPackageDirectoryVersionDetector("rgl_*_obj_*", "\\d+\\.\\d+\\.\\w+", false); - - return nullptr; -} - -/// Create the McuPackage by checking the "boardSdk" property in the JSON file for the board. -/// The name of the environment variable pointing to the the SDK for the board will be defined in the "envVar" property -/// inside the "boardSdk". -static McuPackage *createBoardSdkPackage(const McuTargetDescription &desc) -{ - const auto generateSdkName = [](const QString &envVar) { - auto postfixPos = envVar.indexOf("_SDK_PATH"); - if (postfixPos < 0) { - postfixPos = envVar.indexOf("_DIR"); - } - auto sdkName = postfixPos > 0 ? envVar.left(postfixPos) : envVar; - return QString::fromLatin1("MCU SDK (%1)").arg(sdkName); - }; - const QString sdkName = desc.boardSdk.name.isEmpty() ? generateSdkName(desc.boardSdk.envVar) - : desc.boardSdk.name; - - const FilePath defaultPath = [&] { - const auto envVar = desc.boardSdk.envVar.toLatin1(); - if (qEnvironmentVariableIsSet(envVar)) - return FilePath::fromUserInput(qEnvironmentVariable(envVar)); - if (!desc.boardSdk.defaultPath.isEmpty()) { - FilePath defaultPath = FilePath::fromUserInput(QDir::rootPath() - + desc.boardSdk.defaultPath); - if (defaultPath.exists()) - return defaultPath; - } - return FilePath(); - }(); - - const auto versionDetector = generatePackageVersionDetector(desc.boardSdk.envVar); - - return new McuPackage(sdkName, - defaultPath, - {}, // detection path - desc.boardSdk.envVar, // settings key - desc.boardSdk.envVar, // env var - {}, // download URL - versionDetector); -} - -static McuPackage *createFreeRTOSSourcesPackage(const QString &envVar, - const FilePath &boardSdkDir, - const QString &freeRTOSBoardSdkSubDir) -{ - const QString envVarPrefix = envVar.chopped(int(strlen("_FREERTOS_DIR"))); - - FilePath defaultPath; - if (qEnvironmentVariableIsSet(envVar.toLatin1())) - defaultPath = FilePath::fromUserInput(qEnvironmentVariable(envVar.toLatin1())); - else if (!boardSdkDir.isEmpty() && !freeRTOSBoardSdkSubDir.isEmpty()) - defaultPath = boardSdkDir / freeRTOSBoardSdkSubDir; - - return new McuPackage(QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix), - defaultPath, - {}, - QString::fromLatin1("FreeRTOSSourcePackage_%1").arg(envVarPrefix), - envVar, - "https://freertos.org"); -} - -struct McuTargetFactory -{ - McuTargetFactory(const QHash<QString, McuToolChainPackage *> &tcPkgs, - const QHash<QString, McuPackage *> &vendorPkgs) - : tcPkgs(tcPkgs) - , vendorPkgs(vendorPkgs) - {} - - QVector<McuTarget *> createTargets(const McuTargetDescription &description) - { - auto qulVersion = QVersionNumber::fromString(description.qulVersion); - if (qulVersion <= QVersionNumber({1, 3})) { - if (description.platform.type == McuTargetDescription::TargetType::Desktop) - return createDesktopTargetsLegacy(description); - - // There was a platform backends related refactoring in Qul 1.4 - // This requires different processing of McuTargetDescriptions - return createMcuTargetsLegacy(description); - } - return createTargetsImpl(description); - } - - QVector<McuAbstractPackage *> getMcuPackages() const - { - QVector<McuAbstractPackage *> packages; - for (auto *package : qAsConst(boardSdkPkgs)) - packages.append(package); - for (auto *package : qAsConst(freeRTOSPkgs)) - packages.append(package); - return packages; - } - -protected: - // Implementation for Qul version <= 1.3 - QVector<McuTarget *> createMcuTargetsLegacy(const McuTargetDescription &desc) - { - QVector<McuTarget *> mcuTargets; - McuToolChainPackage *tcPkg = tcPkgs.value(desc.toolchain.id); - if (!tcPkg) - tcPkg = createUnsupportedToolChainPackage(); - for (auto os : {McuTarget::OS::BareMetal, McuTarget::OS::FreeRTOS}) { - for (int colorDepth : desc.platform.colorDepths) { - QVector<McuAbstractPackage *> required3rdPartyPkgs = {tcPkg}; - if (vendorPkgs.contains(desc.platform.vendor)) - required3rdPartyPkgs.push_back(vendorPkgs.value(desc.platform.vendor)); - - FilePath boardSdkDefaultPath; - if (!desc.boardSdk.envVar.isEmpty()) { - if (!boardSdkPkgs.contains(desc.boardSdk.envVar)) { - auto boardSdkPkg = desc.boardSdk.envVar != "RGL_DIR" - ? createBoardSdkPackage(desc) - : createRGLPackage(); - boardSdkPkgs.insert(desc.boardSdk.envVar, boardSdkPkg); - } - auto boardSdkPkg = boardSdkPkgs.value(desc.boardSdk.envVar); - boardSdkDefaultPath = boardSdkPkg->defaultPath(); - required3rdPartyPkgs.append(boardSdkPkg); - } - if (os == McuTarget::OS::FreeRTOS) { - if (desc.freeRTOS.envVar.isEmpty()) { - continue; - } else { - if (!freeRTOSPkgs.contains(desc.freeRTOS.envVar)) { - freeRTOSPkgs - .insert(desc.freeRTOS.envVar, - createFreeRTOSSourcesPackage(desc.freeRTOS.envVar, - boardSdkDefaultPath, - desc.freeRTOS.boardSdkSubDir)); - } - required3rdPartyPkgs.append(freeRTOSPkgs.value(desc.freeRTOS.envVar)); - } - } - - const auto platform = McuTarget::Platform{desc.platform.id, - desc.platform.name, - desc.platform.vendor}; - auto mcuTarget = new McuTarget(QVersionNumber::fromString(desc.qulVersion), - platform, - os, - required3rdPartyPkgs, - tcPkg, - desc.platform.colorDepths.count() > 1 - ? colorDepth - : McuTarget::UnspecifiedColorDepth); - mcuTargets.append(mcuTarget); - } - } - return mcuTargets; - } - - QVector<McuTarget *> createDesktopTargetsLegacy(const McuTargetDescription &desc) - { - McuToolChainPackage *tcPkg = tcPkgs.value(desc.toolchain.id); - if (!tcPkg) - tcPkg = createUnsupportedToolChainPackage(); - const auto platform = McuTarget::Platform{desc.platform.id, - desc.platform.name, - desc.platform.vendor}; - auto desktopTarget = new McuTarget(QVersionNumber::fromString(desc.qulVersion), - platform, - McuTarget::OS::Desktop, - {}, - tcPkg); - return {desktopTarget}; - } - - QVector<McuTarget *> createTargetsImpl(const McuTargetDescription &desc) - { - // OS deduction - const auto os = [&] { - if (desc.platform.type == McuTargetDescription::TargetType::Desktop) - return McuTarget::OS::Desktop; - else if (!desc.freeRTOS.envVar.isEmpty()) - return McuTarget::OS::FreeRTOS; - return McuTarget::OS::BareMetal; - }(); - - QVector<McuTarget *> mcuTargets; - McuToolChainPackage *tcPkg = tcPkgs.value(desc.toolchain.id); - if (tcPkg) - tcPkg->setVersions(desc.toolchain.versions); - else - tcPkg = createUnsupportedToolChainPackage(); - for (int colorDepth : desc.platform.colorDepths) { - QVector<McuAbstractPackage *> required3rdPartyPkgs; - // Desktop toolchains don't need any additional settings - if (tcPkg && !tcPkg->isDesktopToolchain() - && tcPkg->toolchainType() != McuToolChainPackage::ToolChainType::Unsupported) - required3rdPartyPkgs.append(tcPkg); - - // Add setting specific to platform IDE - if (vendorPkgs.contains(desc.platform.vendor)) - required3rdPartyPkgs.push_back(vendorPkgs.value(desc.platform.vendor)); - - // Board SDK specific settings - FilePath boardSdkDefaultPath; - if (!desc.boardSdk.envVar.isEmpty()) { - if (!boardSdkPkgs.contains(desc.boardSdk.envVar)) { - auto boardSdkPkg = createBoardSdkPackage(desc); - boardSdkPkgs.insert(desc.boardSdk.envVar, boardSdkPkg); - } - auto boardSdkPkg = boardSdkPkgs.value(desc.boardSdk.envVar); - boardSdkPkg->setVersions(desc.boardSdk.versions); - boardSdkDefaultPath = boardSdkPkg->defaultPath(); - required3rdPartyPkgs.append(boardSdkPkg); - } - - // Free RTOS specific settings - if (!desc.freeRTOS.envVar.isEmpty()) { - if (!freeRTOSPkgs.contains(desc.freeRTOS.envVar)) { - freeRTOSPkgs.insert(desc.freeRTOS.envVar, - createFreeRTOSSourcesPackage(desc.freeRTOS.envVar, - boardSdkDefaultPath, - desc.freeRTOS.boardSdkSubDir)); - } - required3rdPartyPkgs.append(freeRTOSPkgs.value(desc.freeRTOS.envVar)); - } - - const McuTarget::Platform platform( - {desc.platform.id, desc.platform.name, desc.platform.vendor}); - auto mcuTarget = new McuTarget(QVersionNumber::fromString(desc.qulVersion), - platform, - os, - required3rdPartyPkgs, - tcPkg, - colorDepth); - mcuTargets.append(mcuTarget); - } - return mcuTargets; - } - -private: - const QHash<QString, McuToolChainPackage *> &tcPkgs; - const QHash<QString, McuPackage *> &vendorPkgs; - - QHash<QString, McuPackage *> boardSdkPkgs; - QHash<QString, McuPackage *> freeRTOSPkgs; -}; // struct McuTargetFactory QVector<McuTarget *> targetsFromDescriptions(const QList<McuTargetDescription> &descriptions, QVector<McuAbstractPackage *> *packages) { const QHash<QString, McuToolChainPackage *> tcPkgs = { - {{"armgcc"}, createArmGccPackage()}, + {{"armgcc"}, createArmGccToolchainPackage()}, {{"greenhills"}, createGhsToolchainPackage()}, {{"iar"}, createIarToolChainPackage()}, {{"msvc"}, createMsvcToolChainPackage()}, @@ -676,31 +487,30 @@ McuTargetDescription parseDescriptionJson(const QByteArray &data) }); const QString platformName = platform.value("platformName").toString(); - return { - qulVersion, - compatVersion, - { - platform.value("id").toString(), - platformName, - platform.value("vendor").toString(), - colorDepthsVector, - platformName == "Desktop" ? McuTargetDescription::TargetType::Desktop : McuTargetDescription::TargetType::MCU, - }, - { - toolchain.value("id").toString(), - toolchainVersionsList, - }, - { - boardSdk.value("name").toString(), - boardSdk.value("defaultPath").toString(), - boardSdk.value("envVar").toString(), - boardSdkVersionsList, - }, - { - freeRTOS.value("envVar").toString(), - freeRTOS.value("boardSdkSubDir").toString(), - } - }; + return {qulVersion, + compatVersion, + { + platform.value("id").toString(), + platformName, + platform.value("vendor").toString(), + colorDepthsVector, + platformName == "Desktop" ? McuTargetDescription::TargetType::Desktop + : McuTargetDescription::TargetType::MCU, + }, + { + toolchain.value("id").toString(), + toolchainVersionsList, + }, + { + boardSdk.value("name").toString(), + boardSdk.value("defaultPath").toString(), + boardSdk.value("envVar").toString(), + boardSdkVersionsList, + }, + { + freeRTOS.value("envVar").toString(), + freeRTOS.value("boardSdkSubDir").toString(), + }}; } // https://doc.qt.io/qtcreator/creator-developing-mcu.html#supported-qt-for-mcus-sdks @@ -720,7 +530,6 @@ static const QString legacySupportVersionFor(const QString &sdkVersion) return QString(); } - bool checkDeprecatedSdkError(const Utils::FilePath &qulDir, QString &message) { const McuPackagePathVersionDetector versionDetector("(?<=\\bQtMCUs.)(\\d+\\.\\d+)"); @@ -799,7 +608,7 @@ void targetsAndPackages(const Utils::FilePath &dir, McuSdkRepository *repo) std::sort(repo->mcuTargets.begin(), repo->mcuTargets.end(), [](const McuTarget *lhs, const McuTarget *rhs) { - return McuKitManager::kitName(lhs) < McuKitManager::kitName(rhs); + return McuKitManager::generateKitNameFromTarget(lhs) < McuKitManager::generateKitNameFromTarget(rhs); }); } diff --git a/src/plugins/mcusupport/mcusupportsdk.h b/src/plugins/mcusupport/mcusupportsdk.h index a79a7d90f10..1f4594d7aff 100644 --- a/src/plugins/mcusupport/mcusupportsdk.h +++ b/src/plugins/mcusupport/mcusupportsdk.h @@ -25,7 +25,7 @@ #pragma once -#include "utils/filepath.h" +#include <utils/filepath.h> #include <QSettings> #include <QVector> @@ -38,6 +38,7 @@ constexpr int MAX_COMPATIBILITY_VERSION{1}; class McuSdkRepository; class McuAbstractPackage; class McuPackage; +class McuToolChainPackage; class McuTarget; namespace Sdk { @@ -59,6 +60,13 @@ Utils::FilePath kitsPath(const Utils::FilePath &dir); Utils::FilePath packagePathFromSettings(const QString &settingsKey, QSettings::Scope scope, const Utils::FilePath &defaultPath); + +McuToolChainPackage *createUnsupportedToolChainPackage(); +McuPackage *createBoardSdkPackage(const McuTargetDescription &desc); +McuPackage *createFreeRTOSSourcesPackage(const QString &envVar, + const Utils::FilePath &boardSdkDir, + const QString &freeRTOSBoardSdkSubDir); + } // namespace Sdk } // namespace Internal } // namespace McuSupport diff --git a/src/plugins/mcusupport/mcusupportversiondetection.cpp b/src/plugins/mcusupport/mcusupportversiondetection.cpp index 12bb7912e72..b0db7638015 100644 --- a/src/plugins/mcusupport/mcusupportversiondetection.cpp +++ b/src/plugins/mcusupport/mcusupportversiondetection.cpp @@ -69,7 +69,7 @@ QString McuPackageExecutableVersionDetector::parseVersion(const QString &package if (!binaryProcess.waitForStarted()) return QString(); binaryProcess.waitForFinished(execTimeout); - if (binaryProcess.exitCode() == QProcess::ExitStatus::NormalExit) { + if (binaryProcess.exitStatus() == QProcess::NormalExit) { const QString processOutput = QString::fromUtf8( binaryProcess.readAllStandardOutput().append(binaryProcess.readAllStandardError())); return matchRegExp(processOutput, m_detectionRegExp); diff --git a/src/plugins/mcusupport/mcutarget.cpp b/src/plugins/mcusupport/mcutarget.cpp index 28628eeca51..2110cd4c1d8 100644 --- a/src/plugins/mcusupport/mcutarget.cpp +++ b/src/plugins/mcusupport/mcutarget.cpp @@ -82,13 +82,13 @@ void McuTarget::printPackageProblems() const package->updateStatus(); if (!package->isValidStatus()) printMessage(tr("Error creating kit for target %1, package %2: %3") - .arg(McuKitManager::kitName(this), + .arg(McuKitManager::generateKitNameFromTarget(this), package->label(), package->statusText()), true); if (package->status() == McuAbstractPackage::Status::ValidPackageMismatchedVersion) printMessage(tr("Warning creating kit for target %1, package %2: %3") - .arg(McuKitManager::kitName(this), + .arg(McuKitManager::generateKitNameFromTarget(this), package->label(), package->statusText()), false); diff --git a/src/plugins/mcusupport/mcutarget.h b/src/plugins/mcusupport/mcutarget.h index 7426f81e152..fbfbd2d6055 100644 --- a/src/plugins/mcusupport/mcutarget.h +++ b/src/plugins/mcusupport/mcutarget.h @@ -37,8 +37,7 @@ class PathChooser; class InfoLabel; } // namespace Utils -namespace McuSupport { -namespace Internal { +namespace McuSupport::Internal { class McuAbstractPackage; class McuToolChainPackage; @@ -85,5 +84,4 @@ private: }; // class McuTarget -} // namespace Internal -} // namespace McuSupport +} // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcutargetfactory.cpp b/src/plugins/mcusupport/mcutargetfactory.cpp new file mode 100644 index 00000000000..d3f171bff30 --- /dev/null +++ b/src/plugins/mcusupport/mcutargetfactory.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "mcutargetfactory.h" +#include "mcupackage.h" +#include "mcusupportsdk.h" +#include "mcusupportversiondetection.h" +#include "mcutarget.h" +#include "mcutargetdescription.h" + +#include <utils/fileutils.h> +#include <QVersionNumber> + +namespace McuSupport::Internal::Sdk { + +using namespace Utils; + +QVector<McuAbstractPackage *> McuTargetFactory::getMcuPackages() const +{ + QVector<McuAbstractPackage *> packages; + for (auto *package : qAsConst(boardSdkPkgs)) + packages.append(package); + for (auto *package : qAsConst(freeRTOSPkgs)) + packages.append(package); + return packages; +} + +QVector<McuTarget *> McuTargetFactory::createTargets(const McuTargetDescription &desc) +{ + // OS deduction + const auto os = [&] { + if (desc.platform.type == McuTargetDescription::TargetType::Desktop) + return McuTarget::OS::Desktop; + else if (!desc.freeRTOS.envVar.isEmpty()) + return McuTarget::OS::FreeRTOS; + return McuTarget::OS::BareMetal; + }(); + + QVector<McuTarget *> mcuTargets; + McuToolChainPackage *tcPkg = tcPkgs.value(desc.toolchain.id); + if (tcPkg) + tcPkg->setVersions(desc.toolchain.versions); + else + tcPkg = createUnsupportedToolChainPackage(); + for (int colorDepth : desc.platform.colorDepths) { + QVector<McuAbstractPackage *> required3rdPartyPkgs; + // Desktop toolchains don't need any additional settings + if (tcPkg && !tcPkg->isDesktopToolchain() + && tcPkg->toolchainType() != McuToolChainPackage::ToolChainType::Unsupported) + required3rdPartyPkgs.append(tcPkg); + + // Add setting specific to platform IDE + if (vendorPkgs.contains(desc.platform.vendor)) + required3rdPartyPkgs.push_back(vendorPkgs.value(desc.platform.vendor)); + + // Board SDK specific settings + FilePath boardSdkDefaultPath; + if (!desc.boardSdk.envVar.isEmpty()) { + if (!boardSdkPkgs.contains(desc.boardSdk.envVar)) { + auto boardSdkPkg = createBoardSdkPackage(desc); + boardSdkPkgs.insert(desc.boardSdk.envVar, boardSdkPkg); + } + auto boardSdkPkg = boardSdkPkgs.value(desc.boardSdk.envVar); + boardSdkPkg->setVersions(desc.boardSdk.versions); + boardSdkDefaultPath = boardSdkPkg->defaultPath(); + required3rdPartyPkgs.append(boardSdkPkg); + } + + // Free RTOS specific settings + if (!desc.freeRTOS.envVar.isEmpty()) { + if (!freeRTOSPkgs.contains(desc.freeRTOS.envVar)) { + freeRTOSPkgs.insert(desc.freeRTOS.envVar, + createFreeRTOSSourcesPackage(desc.freeRTOS.envVar, + boardSdkDefaultPath, + desc.freeRTOS.boardSdkSubDir)); + } + required3rdPartyPkgs.append(freeRTOSPkgs.value(desc.freeRTOS.envVar)); + } + + const McuTarget::Platform platform( + {desc.platform.id, desc.platform.name, desc.platform.vendor}); + auto mcuTarget = new McuTarget(QVersionNumber::fromString(desc.qulVersion), + platform, + os, + required3rdPartyPkgs, + tcPkg, + colorDepth); + mcuTargets.append(mcuTarget); + } + return mcuTargets; +} +} // namespace McuSupport::Internal::Sdk diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocess.h b/src/plugins/mcusupport/mcutargetfactory.h index 215b26879b4..1143a9145e2 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocess.h +++ b/src/plugins/mcusupport/mcutargetfactory.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,38 +25,39 @@ #pragma once -#include "../projectexplorer_export.h" +#include <QHash> +#include <QVector> -#include <utils/qtcprocess.h> +namespace McuSupport::Internal { -#include <QSharedPointer> -#include <QStringList> +class McuAbstractPackage; +class McuPackage; +class McuTarget; +class McuToolChainPackage; -namespace ProjectExplorer { +namespace Sdk { -class IDevice; -class Runnable; +struct McuTargetDescription; -class PROJECTEXPLORER_EXPORT DeviceProcess : public Utils::QtcProcess +class McuTargetFactory { - Q_OBJECT public: - using Utils::QtcProcess::start; - virtual void start(const Runnable &runnable) = 0; + McuTargetFactory(const QHash<QString, McuToolChainPackage *> &tcPkgs, + const QHash<QString, McuPackage *> &vendorPkgs) + : tcPkgs(tcPkgs) + , vendorPkgs(vendorPkgs) + {} - void setRunInTerminal(bool term) { m_runInTerminal = term; } - bool runInTerminal() const { return m_runInTerminal; } - -protected: - explicit DeviceProcess(const QSharedPointer<const IDevice> &device, - const Utils::QtcProcess::Setup &setup, - QObject *parent = nullptr); - - QSharedPointer<const IDevice> device() const; + QVector<McuTarget *> createTargets(const McuTargetDescription &description); + QVector<McuAbstractPackage *> getMcuPackages() const; private: - const QSharedPointer<const IDevice> m_device; - bool m_runInTerminal = false; -}; + const QHash<QString, McuToolChainPackage *> &tcPkgs; + const QHash<QString, McuPackage *> &vendorPkgs; + + QHash<QString, McuPackage *> boardSdkPkgs; + QHash<QString, McuPackage *> freeRTOSPkgs; +}; // struct McuTargetFactory -} // namespace ProjectExplorer +} // namespace Sdk +} // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/test/armgcc_nxp_1050_json.h b/src/plugins/mcusupport/test/armgcc_nxp_1050_json.h new file mode 100644 index 00000000000..81c15d6bdc6 --- /dev/null +++ b/src/plugins/mcusupport/test/armgcc_nxp_1050_json.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +constexpr auto armgcc_nxp_1050_json = R"({ + "qulVersion": "2.0.0", + "compatVersion": "1", + "platform": { + "id": "MIMXRT1050-EVK-FREERTOS", + "vendor": "NXP", + "colorDepths": [ + 16 + ], + "pathEntries": [], + "environmentEntries": [], + "cmakeCacheEntries": [ + { + "id": "Qul_DIR", + "description": "Qt for MCUs SDK", + "type": "path", + "cmakeOptionName": "Qul_ROOT", + "optional": false + }, + { + "id": "MCU_XPRESSO_PATH", + "description": "MCUXpresso IDE", + "type": "path", + "cmakeOptionName": "MCUXPRESSO_IDE_PATH", + "defaultValue": { + "windows": "$ROOT/nxp/MCUXpressoIDE*", + "unix": "/usr/local/mcuxpressoide/" + }, + "optional": false + } + ] + }, + "toolchain": { + "id": "armgcc", + "versions": [ + "9.3.1" + ], + "cmakeCacheEntries": [ + { + "id": "ARMGCC_DIR", + "description": "GNU Arm Embedded Toolchain", + "cmakeOptionName": "QUL_TARGET_TOOLCHAIN_DIR", + "type": "path", + "optional": false + }, + { + "id": "ARMGCC_CMAKE_TOOLCHAIN_FILE", + "description": "CMake Toolchain File", + "cmakeOptionName": "CMAKE_TOOLCHAIN_FILE", + "type": "file", + "defaultValue": "$Qul_ROOT/lib/cmake/Qul/toolchain/armgcc.cmake", + "visible": false, + "optional": false + } + ] + }, + "boardSdk": { + "envVar": "EVKB_IMXRT1050_SDK_PATH", + "versions": [ + "2.10.0" + ], + "cmakeCacheEntries": [ + { + "id": "NXP_SDK_DIR", + "description": "Board SDK for MIMXRT1050-EVK", + "cmakeOptionName": "QUL_BOARD_SDK_DIR", + "type": "path", + "optional": false + } + ] + }, + "freeRTOS": { + "envVar": "IMXRT1050_FREERTOS_DIR", + "cmakeCacheEntries": [ + { + "id": "NXP_FREERTOS_DIR", + "description": "FreeRTOS SDK for MIMXRT1050-EVK", + "cmakeOptionName": "FREERTOS_DIR", + "defaultValue": "$QUL_BOARD_SDK_DIR/rtos/freertos/freertos_kernel", + "type": "path", + "optional": false + } + ] + } +})"; diff --git a/src/plugins/mcusupport/test/nxp_1064_json.h b/src/plugins/mcusupport/test/armgcc_nxp_1064_json.h index 4e8f53afa96..f03911f405c 100644 --- a/src/plugins/mcusupport/test/nxp_1064_json.h +++ b/src/plugins/mcusupport/test/armgcc_nxp_1064_json.h @@ -25,7 +25,9 @@ #pragma once -constexpr auto nxp_1064_json = R"({ +constexpr auto armgcc_nxp_1064_json = R"({ + "compatVersion": "1", + "qulVersion": "2.0.0", "boardSdk": { "cmakeCacheEntries": [ { @@ -35,9 +37,9 @@ constexpr auto nxp_1064_json = R"({ "optional": false, "type": "path" } - ], - "envVar": "EVK_MIMXRT1064_SDK_PATH", - "versions": ["2.10.0"] + ], + "envVar": "EVK_MIMXRT1064_SDK_PATH", + "versions": ["2.10.0"] }, "compatVersion": "1", "freeRTOS": { @@ -49,7 +51,7 @@ constexpr auto nxp_1064_json = R"({ "id": "NXP_FREERTOS_DIR", "optional": false, "type": "path" - } + } ], "envVar": "IMXRT1064_FREERTOS_DIR" }, diff --git a/src/plugins/mcusupport/test/armgcc_stm32f769i_freertos_json.h b/src/plugins/mcusupport/test/armgcc_stm32f769i_freertos_json.h new file mode 100644 index 00000000000..1695d83244e --- /dev/null +++ b/src/plugins/mcusupport/test/armgcc_stm32f769i_freertos_json.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +constexpr auto armgcc_stm32f769i_freertos_json = R"({ + "qulVersion": "@CMAKE_PROJECT_VERSION@", + "compatVersion": "@COMPATIBILITY_VERSION@", + "platform": { + "id": "STM32F769I-DISCOVERY-FREERTOS", + "vendor": "ST", + "colorDepths": [ + 32 + ], + "pathEntries": [ + { + "id": "STM32CubeProgrammer_PATH", + "id": "STM32CubeProgrammer_PATH", + "description": "STM32CubeProgrammer", + "type": "path", + "defaultValue": { + "windows": "$PROGRAMSANDFILES/STMicroelectronics/STM32Cube/STM32CubeProgrammer/", + "unix": "$HOME/STMicroelectronics/STM32Cube/STM32CubeProgrammer/" + }, + "optional": false + } + ], + "environmentEntries": [], + "cmakeCacheEntries": [ + { + "id": "Qul_DIR", + "description": "Qt for MCUs SDK", + "type": "path", + "cmakeOptionName": "Qul_ROOT", + "optional": false + } + ] + }, + "toolchain": { + "id": "armgcc", + "versions": [ + "9.3.1" + ], + "cmakeCacheEntries": [ + { + "id": "ARMGCC_DIR", + "description": "GNU Arm Embedded Toolchain", + "cmakeOptionName": "QUL_TARGET_TOOLCHAIN_DIR", + "type": "path", + "optional": false + }, + { + "id": "ARMGCC_CMAKE_TOOLCHAIN_FILE", + "description": "CMake Toolchain File", + "cmakeOptionName": "CMAKE_TOOLCHAIN_FILE", + "type": "file", + "defaultValue": "$Qul_ROOT/lib/cmake/Qul/toolchain/armgcc.cmake", + "visible": false, + "optional": false + } + ] + }, + "boardSdk": { + "envVar": "STM32Cube_FW_F7_SDK_PATH", + "versions": [ + "1.16.0" + ], + "cmakeCacheEntries": [ + { + "id": "ST_SDK_DIR", + "description": "Board SDK for STM32F769I-Discovery", + "cmakeOptionName": "QUL_BOARD_SDK_DIR", + "type": "path", + "optional": false + } + ] + }, + "freeRTOS": { + "envVar": "STM32F7_FREERTOS_DIR", + "cmakeCacheEntries": [ + { + "id": "ST_FREERTOS_DIR", + "description": "FreeRTOS SDK for STM32F769I-Discovery", + "cmakeOptionName": "FREERTOS_DIR", + "defaultValue": "$QUL_BOARD_SDK_DIR/Middlewares/Third_Party/FreeRTOS/Source", + "type": "path", + "optional": false + } + ] + } +})"; diff --git a/src/plugins/mcusupport/test/armgcc_stm32h750b_metal_json.h b/src/plugins/mcusupport/test/armgcc_stm32h750b_metal_json.h new file mode 100644 index 00000000000..51484538e6b --- /dev/null +++ b/src/plugins/mcusupport/test/armgcc_stm32h750b_metal_json.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +constexpr auto armgcc_stm32h750b_metal_json = R"({ + "qulVersion": "2.0.0", + "compatVersion": "1", + "platform": { + "id": "STM32H750B-DISCOVERY-BAREMETAL", + "vendor": "ST", + "colorDepths": [ + 32 + ], + "pathEntries": [ + { + "id": "STM32CubeProgrammer_PATH", + "description": "STM32CubeProgrammer", + "type": "path", + "defaultValue": { + "windows": "$PROGRAMSANDFILES/STMicroelectronics/STM32Cube/STM32CubeProgrammer/", + "unix": "$HOME/STMicroelectronics/STM32Cube/STM32CubeProgrammer/" + }, + "optional": false + } + ], + "environmentEntries": [], + "cmakeCacheEntries": [ + { + "id": "Qul_DIR", + "description": "Qt for MCUs SDK", + "type": "path", + "cmakeOptionName": "Qul_ROOT", + "optional": false + } + ] + }, + "toolchain": { + "id": "armgcc", + "versions": [ + "9.3.1" + ], + "cmakeCacheEntries": [ + { + "id": "ARMGCC_DIR", + "description": "GNU Arm Embedded Toolchain", + "cmakeOptionName": "QUL_TARGET_TOOLCHAIN_DIR", + "type": "path", + "optional": false + }, + { + "id": "ARMGCC_CMAKE_TOOLCHAIN_FILE", + "description": "CMake Toolchain File", + "cmakeOptionName": "CMAKE_TOOLCHAIN_FILE", + "type": "file", + "defaultValue": "$Qul_ROOT/lib/cmake/Qul/toolchain/armgcc.cmake", + "visible": false, + "optional": false + } + ] + }, + "boardSdk": { + "envVar": "STM32Cube_FW_H7_SDK_PATH", + "versions": [ + "1.5.0" + ], + "cmakeCacheEntries": [ + { + "id": "ST_SDK_DIR", + "description": "Board SDK for STM32H750B-Discovery", + "cmakeOptionName": "QUL_BOARD_SDK_DIR", + "type": "path", + "optional": false + } + ] + } +})"; diff --git a/src/plugins/mcusupport/test/iar_stm32f469i_metal_json.h b/src/plugins/mcusupport/test/iar_stm32f469i_metal_json.h new file mode 100644 index 00000000000..6a738729cae --- /dev/null +++ b/src/plugins/mcusupport/test/iar_stm32f469i_metal_json.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +constexpr auto iar_stm32f469i_metal_json = R"({ + "qulVersion": "@CMAKE_PROJECT_VERSION@", + "compatVersion": "@COMPATIBILITY_VERSION@", + "platform": { + "id": "STM32F469I-DISCOVERY-BAREMETAL", + "vendor": "ST", + "colorDepths": [ + 24 + ], + "pathEntries": [ + { + "id": "STM32CubeProgrammer_PATH", + "description": "STM32CubeProgrammer", + "type": "path", + "defaultValue": { + "windows": "$PROGRAMSANDFILES/STMicroelectronics/STM32Cube/STM32CubeProgrammer/", + "unix": "$HOME/STMicroelectronics/STM32Cube/STM32CubeProgrammer/" + }, + "optional": false + } + ], + "environmentEntries": [], + "cmakeCacheEntries": [ + { + "id": "Qul_DIR", + "description": "Qt for MCUs SDK", + "type": "path", + "cmakeOptionName": "Qul_ROOT", + "optional": false + } + ] + }, + "toolchain": { + "id": "iar", + "versions": [ + "8.50.9" + ], + "cmakeCacheEntries": [ + { + "id": "IARToolchain", + "envVar": "IAR_ARM_COMPILER_DIR", + "description": "IAR ARM Compiler", + "cmakeOptionName": "QUL_TARGET_TOOLCHAIN_DIR", + "type": "path", + "optional": false + }, + { + "id": "IAR_CMAKE_TOOLCHAIN_FILE", + "description": "CMake Toolchain File", + "cmakeOptionName": "CMAKE_TOOLCHAIN_FILE", + "type": "file", + "defaultValue": "$Qul_ROOT/lib/cmake/Qul/toolchain/iar.cmake", + "visible": false, + "optional": false + } + ] + }, + "boardSdk": { + "envVar": "STM32Cube_FW_F4_SDK_PATH", + "versions": [ + "1.25.0" + ], + "cmakeCacheEntries": [ + { + "id": "ST_SDK_DIR", + "description": "Board SDK for STM32F469I-Discovery", + "cmakeOptionName": "QUL_BOARD_SDK_DIR", + "type": "path", + "optional": false + } + ] + } +})"; diff --git a/src/plugins/mcusupport/test/packagemock.h b/src/plugins/mcusupport/test/packagemock.h index 2e41ef6550b..93108856c7b 100644 --- a/src/plugins/mcusupport/test/packagemock.h +++ b/src/plugins/mcusupport/test/packagemock.h @@ -42,9 +42,11 @@ public: MOCK_METHOD(Utils::FilePath, detectionPath, (), (const)); MOCK_METHOD(QString, statusText, (), (const)); MOCK_METHOD(void, updateStatus, ()); + MOCK_METHOD(QString, settingsKey, (), (const)); MOCK_METHOD(Status, status, (), (const)); MOCK_METHOD(bool, isValidStatus, (), (const)); + MOCK_METHOD(const QString &, cmakeVariableName, (), (const)); MOCK_METHOD(const QString &, environmentVariableName, (), (const)); MOCK_METHOD(bool, isAddToSystemPath, (), (const)); MOCK_METHOD(bool, writeToSettings, (), (const)); diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp index 62b8fd9c278..3c5816df3a6 100644 --- a/src/plugins/mcusupport/test/unittest.cpp +++ b/src/plugins/mcusupport/test/unittest.cpp @@ -24,18 +24,42 @@ ****************************************************************************/ #include "unittest.h" -#include "mcutargetdescription.h" +#include "armgcc_nxp_1050_json.h" +#include "armgcc_nxp_1064_json.h" +#include "armgcc_stm32f769i_freertos_json.h" +#include "armgcc_stm32h750b_metal_json.h" +#include "iar_stm32f469i_metal_json.h" #include "mcukitmanager.h" -#include "nxp_1064_json.h" -#include "utils/filepath.h" +#include "mcusupportconstants.h" +#include "mcusupportsdk.h" +#include "mcutargetdescription.h" +#include <utils/algorithm.h> +#include <utils/filepath.h> + #include <cmakeprojectmanager/cmakeconfigitem.h> #include <cmakeprojectmanager/cmakekitinformation.h> #include <gmock/gmock.h> #include <QJsonArray> #include <QJsonDocument> +#include <qtestcase.h> +#include <algorithm> +#include <ciso646> namespace McuSupport::Internal::Test { +// clazy:excludeall=non-pod-global-static +static const QString nxp1050FreeRtosEnvVar{"IMXRT1050_FREERTOS_DIR"}; +static const QString nxp1064FreeRtosEnvVar{"IMXRT1064_FREERTOS_DIR"}; +static const QString nxp1170FreeRtosEnvVar{"EVK_MIMXRT1170_FREERTOS_PATH"}; +static const QString stm32f7FreeRtosEnvVar{"STM32F7_FREERTOS_DIR"}; +static const QString stm32f7{"STM32F7"}; +static const QString nxp1170{"EVK_MIMXRT1170"}; +static const QString nxp1050{"IMXRT1050"}; +static const QString nxp1064{"IMXRT1064"}; + +static const QStringList jsonFiles{QString::fromUtf8(armgcc_nxp_1050_json), + QString::fromUtf8(armgcc_nxp_1064_json)}; + using CMakeProjectManager::CMakeConfigItem; using CMakeProjectManager::CMakeConfigurationKitAspect; using ProjectExplorer::EnvironmentKitAspect; @@ -46,15 +70,11 @@ using Utils::FilePath; void McuSupportTest::initTestCase() { - EXPECT_CALL(freeRtosPackage, environmentVariableName()).WillRepeatedly(ReturnRef(freeRtosEnvVar)); - EXPECT_CALL(freeRtosPackage, isValidStatus()).WillRepeatedly(Return(true)); - EXPECT_CALL(freeRtosPackage, path()) - .WillRepeatedly(Return(FilePath::fromString(defaultfreeRtosPath))); } void McuSupportTest::test_parseBasicInfoFromJson() { - const auto description = Sdk::parseDescriptionJson(nxp_1064_json); + const auto description = Sdk::parseDescriptionJson(armgcc_nxp_1064_json); QVERIFY(!description.freeRTOS.envVar.isEmpty()); QVERIFY(description.freeRTOS.boardSdkSubDir.isEmpty()); @@ -62,11 +82,35 @@ void McuSupportTest::test_parseBasicInfoFromJson() void McuSupportTest::test_addNewKit() { + const QString cmakeVar = "CMAKE_SDK"; + McuPackage sdkPackage{"sdk", // label + {}, // defaultPath + {}, // detectionPath + "sdk", // settingsKey + cmakeVar, // cmake var + {}}; // env var + Kit kit; + + McuToolChainPackage toolchainPackage{ + {}, // label + {}, // defaultPath + {}, // detectionPath + {}, // settingsKey + McuToolChainPackage::ToolChainType::Unsupported, // toolchain type + {}, // cmake var name + {}}; // env var name + const McuTarget::Platform platform{id, name, vendor}; + McuTarget mcuTarget{currentQulVersion, // version + platform, // platform + McuTarget::OS::FreeRTOS, // os + {&sdkPackage}, // packages + &toolchainPackage}; // toolchain packages + auto &kitManager{*KitManager::instance()}; QSignalSpy kitAddedSpy(&kitManager, &KitManager::kitAdded); - auto *newKit{McuKitManager::newKit(&mcuTarget, &freeRtosPackage)}; + auto *newKit{McuKitManager::newKit(&mcuTarget, &sdkPackage)}; QVERIFY(newKit != nullptr); QCOMPARE(kitAddedSpy.count(), 1); @@ -75,25 +119,61 @@ void McuSupportTest::test_addNewKit() QVERIFY(createdKit != nullptr); QCOMPARE(createdKit, newKit); - auto cmakeAspect{CMakeConfigurationKitAspect{}}; - QVERIFY(createdKit->hasValue(cmakeAspect.id())); - QVERIFY(createdKit->value(cmakeAspect.id(), freeRtosCmakeVar).isValid()); + const auto config = CMakeConfigurationKitAspect::configuration(newKit); + QVERIFY(config.size() > 0); + QVERIFY(Utils::indexOf(config.toVector(), [&cmakeVar](const CMakeConfigItem &item) { + return item.key == cmakeVar.toUtf8(); + }) != -1); } -void McuSupportTest::test_addFreeRtosCmakeVarToKit() +void McuSupportTest::test_createPackagesWithCorrespondingSettings_data() { - McuSupportOptions::updateKitEnvironment(&kit, &mcuTarget); - - QVERIFY(kit.hasValue(EnvironmentKitAspect::id())); - QVERIFY(kit.isValid()); - QVERIFY(!kit.allKeys().empty()); - - const auto &cmakeConfig{CMakeConfigurationKitAspect::configuration(&kit)}; - QCOMPARE(cmakeConfig.size(), 1); + QTest::addColumn<QString>("json"); + QTest::addColumn<QSet<QString>>("expectedSettings"); + + QSet<QString> commonSettings{{"CypressAutoFlashUtil"}, + {"GHSArmToolchain"}, + {"GHSToolchain"}, + {"GNUArmEmbeddedToolchain"}, + {"IARToolchain"}, + {"MCUXpressoIDE"}, + {"RenesasFlashProgrammer"}, + {"Stm32CubeProgrammer"}}; + + QTest::newRow("nxp1064") << armgcc_nxp_1064_json + << QSet<QString>{{"EVK_MIMXRT1064_SDK_PATH"}, + {QString{Constants::SETTINGS_KEY_FREERTOS_PREFIX} + .append("IMXRT1064")}} + .unite(commonSettings); + QTest::newRow("nxp1050") << armgcc_nxp_1050_json + << QSet<QString>{{"EVKB_IMXRT1050_SDK_PATH"}, + {QString{Constants::SETTINGS_KEY_FREERTOS_PREFIX} + .append("IMXRT1050")}} + .unite(commonSettings); + + QTest::newRow("stm32h750b") << armgcc_stm32h750b_metal_json + << QSet<QString>{{"STM32Cube_FW_H7_SDK_PATH"}}.unite(commonSettings); + + QTest::newRow("stm32f769i") << armgcc_stm32f769i_freertos_json + << QSet<QString>{{"STM32Cube_FW_F7_SDK_PATH"}}.unite(commonSettings); + + QTest::newRow("stm32f469i") << iar_stm32f469i_metal_json + << QSet<QString>{{"STM32Cube_FW_F4_SDK_PATH"}}.unite(commonSettings); +} - CMakeConfigItem expectedCmakeVar{freeRtosCmakeVar.toLocal8Bit(), - FilePath::fromString(defaultfreeRtosPath).toUserOutput().toLocal8Bit()}; - QVERIFY(cmakeConfig.contains(expectedCmakeVar)); +void McuSupportTest::test_createPackagesWithCorrespondingSettings() +{ + QFETCH(QString, json); + const auto description = Sdk::parseDescriptionJson(json.toLocal8Bit()); + QVector<McuAbstractPackage *> packages; + const auto targets = Sdk::targetsFromDescriptions({description}, &packages); + Q_UNUSED(targets); + + QSet<QString> settings = Utils::transform<QSet<QString>>(packages, [](const auto &package) { + return package->settingsKey(); + }); + QFETCH(QSet<QString>, expectedSettings); + QVERIFY(settings.contains(expectedSettings)); } } // namespace McuSupport::Internal::Test diff --git a/src/plugins/mcusupport/test/unittest.h b/src/plugins/mcusupport/test/unittest.h index dcdb5370b2d..86463b3ad1b 100644 --- a/src/plugins/mcusupport/test/unittest.h +++ b/src/plugins/mcusupport/test/unittest.h @@ -26,10 +26,10 @@ #pragma once #include "mcupackage.h" -#include "mcutarget.h" #include "mcusupportoptions.h" #include "mcusupportplugin.h" #include "mcusupportsdk.h" +#include "mcutarget.h" #include "packagemock.h" #include <projectexplorer/kit.h> @@ -52,9 +52,10 @@ class McuSupportTest : public QObject private slots: void initTestCase(); - void test_addFreeRtosCmakeVarToKit(); void test_addNewKit(); void test_parseBasicInfoFromJson(); + void test_createPackagesWithCorrespondingSettings(); + void test_createPackagesWithCorrespondingSettings_data(); private: QVersionNumber currentQulVersion{2, 0}; @@ -67,17 +68,6 @@ private: const QString freeRtosCmakeVar{"FREERTOS_DIR"}; const QString defaultfreeRtosPath{"/opt/freertos/default"}; - PackageMock freeRtosPackage; - Kit kit; - - McuToolChainPackage toolchainPackage{{}, {}, {}, {}, {}}; - const McuTarget::Platform platform{id, name, vendor}; - McuTarget mcuTarget{currentQulVersion, - platform, - McuTarget::OS::FreeRTOS, - {&freeRtosPackage}, - &toolchainPackage}; - }; // class McuSupportTest } // namespace McuSupport::Internal::Test diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index b7093f4196f..120ce4a938b 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -128,7 +128,7 @@ bool MercurialClient::synchronousClone(const FilePath &workingDirectory, QStringList arguments(QLatin1String("init")); QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, arguments); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return false; // Then pull remote repository @@ -136,7 +136,7 @@ bool MercurialClient::synchronousClone(const FilePath &workingDirectory, arguments << QLatin1String("pull") << dstLocation; QtcProcess proc1; vcsSynchronousExec(proc1, workingDirectory, arguments, flags); - if (proc1.result() != QtcProcess::FinishedWithSuccess) + if (proc1.result() != ProcessResult::FinishedWithSuccess) return false; // By now, there is no hgrc file -> create it @@ -153,13 +153,13 @@ bool MercurialClient::synchronousClone(const FilePath &workingDirectory, arguments << QLatin1String("update"); QtcProcess proc2; vcsSynchronousExec(proc2, workingDirectory, arguments, flags); - return proc2.result() == QtcProcess::FinishedWithSuccess; + return proc2.result() == ProcessResult::FinishedWithSuccess; } else { QStringList arguments(QLatin1String("clone")); arguments << dstLocation << workingDirectory.parentDir().toString(); QtcProcess proc; vcsSynchronousExec(proc, workingDirectory.parentDir(), arguments, flags); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } } @@ -183,7 +183,7 @@ bool MercurialClient::synchronousPull(const FilePath &workingDir, const QString command.addFlags(flags); command.runCommand(proc, {vcsBinary(), args}); - const bool ok = proc.result() == QtcProcess::FinishedWithSuccess; + const bool ok = proc.result() == ProcessResult::FinishedWithSuccess; parsePullOutput(proc.stdOut().trimmed()); return ok; @@ -224,7 +224,7 @@ QStringList MercurialClient::parentRevisionsSync(const FilePath &workingDirector args << file; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, args); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return QStringList(); /* Looks like: \code changeset: 0:031a48610fba @@ -267,7 +267,7 @@ QString MercurialClient::shortDescriptionSync(const FilePath &workingDirectory, QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, args); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return revision; return stripLastNewline(proc.stdOut()); } diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index 12b8cb6a186..a177d410284 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -36,32 +36,32 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/command.h> -#include <coreplugin/documentmanager.h> -#include <coreplugin/vcsmanager.h> #include <coreplugin/coreconstants.h> +#include <coreplugin/documentmanager.h> +#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> #include <coreplugin/idocument.h> -#include <coreplugin/editormanager/editormanager.h> - #include <coreplugin/locator/commandlocator.h> +#include <coreplugin/vcsmanager.h> +#include <utils/commandline.h> +#include <utils/environment.h> #include <utils/parameteraction.h> #include <utils/qtcassert.h> #include <vcsbase/basevcseditorfactory.h> #include <vcsbase/basevcssubmiteditorfactory.h> -#include <vcsbase/vcsbaseeditor.h> #include <vcsbase/vcsbaseconstants.h> -#include <vcsbase/vcsoutputwindow.h> +#include <vcsbase/vcsbaseeditor.h> #include <vcsbase/vcscommand.h> +#include <vcsbase/vcsoutputwindow.h> #include <QAction> -#include <QMenu> #include <QDebug> -#include <QtGlobal> #include <QDir> -#include <QDialog> #include <QFileDialog> +#include <QMenu> +#include <QtGlobal> #ifdef WITH_TESTS #include <QTest> diff --git a/src/plugins/mesonprojectmanager/project/mesonprocess.cpp b/src/plugins/mesonprojectmanager/project/mesonprocess.cpp index 25b859ec730..e457a9c379b 100644 --- a/src/plugins/mesonprojectmanager/project/mesonprocess.cpp +++ b/src/plugins/mesonprojectmanager/project/mesonprocess.cpp @@ -33,6 +33,7 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/taskhub.h> +#include <utils/qtcprocess.h> #include <utils/stringutils.h> #include <QLoggingCategory> @@ -118,29 +119,29 @@ void MesonProcess::handleProcessError(QProcess::ProcessError error) QString message; QString commandStr = m_currentCommand.toUserOutput(); switch (error) { - case QProcess::ProcessError::FailedToStart: + case QProcess::FailedToStart: message = tr("The process failed to start.") + tr("Either the " "invoked program \"%1\" is missing, or you may have insufficient " "permissions to invoke the program.") .arg(m_currentCommand.executable().toUserOutput()); break; - case QProcess::ProcessError::Crashed: + case QProcess::Crashed: message = tr("The process was ended forcefully."); break; - case QProcess::ProcessError::Timedout: + case QProcess::Timedout: message = tr("Process timed out."); break; - case QProcess::ProcessError::WriteError: + case QProcess::WriteError: message = tr("An error occurred when attempting to write " "to the process. For example, the process may not be running, " "or it may have closed its input channel."); break; - case QProcess::ProcessError::ReadError: + case QProcess::ReadError: message = tr("An error occurred when attempting to read from " "the process. For example, the process may not be running."); break; - case QProcess::ProcessError::UnknownError: + case QProcess::UnknownError: message = tr("An unknown error in the process occurred."); break; } diff --git a/src/plugins/mesonprojectmanager/project/mesonprocess.h b/src/plugins/mesonprojectmanager/project/mesonprocess.h index 9c5c6db9a97..f4c191af933 100644 --- a/src/plugins/mesonprojectmanager/project/mesonprocess.h +++ b/src/plugins/mesonprojectmanager/project/mesonprocess.h @@ -27,9 +27,6 @@ #include "exewrappers/mesonwrapper.h" -#include <utils/qtcprocess.h> - -#include <QBuffer> #include <QByteArray> #include <QElapsedTimer> #include <QFutureInterface> @@ -39,6 +36,8 @@ #include <memory> +namespace Utils { class QtcProcess; } + namespace MesonProjectManager { namespace Internal { diff --git a/src/plugins/mesonprojectmanager/project/ninjabuildstep.h b/src/plugins/mesonprojectmanager/project/ninjabuildstep.h index c4a6a81875b..2ddc872ec1c 100644 --- a/src/plugins/mesonprojectmanager/project/ninjabuildstep.h +++ b/src/plugins/mesonprojectmanager/project/ninjabuildstep.h @@ -30,8 +30,6 @@ #include <projectexplorer/abstractprocessstep.h> #include <projectexplorer/buildstep.h> -#include <utils/qtcprocess.h> - namespace MesonProjectManager { namespace Internal { diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp index d05cf9ecb3d..622e7204cb3 100644 --- a/src/plugins/modeleditor/modelindexer.cpp +++ b/src/plugins/modeleditor/modelindexer.cpp @@ -43,10 +43,9 @@ #include <projectexplorer/session.h> #include <projectexplorer/projectnodes.h> -#include <utils/mimetypes/mimetype.h> -#include <utils/mimetypes/mimedatabase.h> -#include <utils/qtcassert.h> +#include <utils/mimeutils.h> #include <utils/porting.h> +#include <utils/qtcassert.h> #include <QQueue> #include <QMutex> diff --git a/src/plugins/nim/project/nimbuildconfiguration.cpp b/src/plugins/nim/project/nimbuildconfiguration.cpp index e27b1374f24..48d91b40a4a 100644 --- a/src/plugins/nim/project/nimbuildconfiguration.cpp +++ b/src/plugins/nim/project/nimbuildconfiguration.cpp @@ -39,7 +39,6 @@ #include <projectexplorer/target.h> #include <utils/aspects.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/qtcassert.h> using namespace ProjectExplorer; diff --git a/src/plugins/nim/suggest/server.cpp b/src/plugins/nim/suggest/server.cpp index d1eb036f022..48e29d1ceb7 100644 --- a/src/plugins/nim/suggest/server.cpp +++ b/src/plugins/nim/suggest/server.cpp @@ -108,7 +108,7 @@ void NimSuggestServer::onFinished() { clearState(); - if (m_process.exitCode() == QProcess::ExitStatus::CrashExit) + if (m_process.exitStatus() == QProcess::CrashExit) emit crashed(); else emit finished(); diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index cd340be879c..2644d24afb3 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -1270,7 +1270,7 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const FilePath &worki } process.setTimeOutMessageBoxEnabled(true); process.setCommand({m_settings.p4BinaryPath.filePath(), args}); - process.runBlocking(QtcProcess::WithEventLoop); + process.runBlocking(EventLoopMode::On); PerforceResponse response; response.error = true; @@ -1278,20 +1278,20 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const FilePath &worki response.stdErr = process.stdErr(); response.stdOut = process.stdOut(); switch (process.result()) { - case QtcProcess::FinishedWithSuccess: + case ProcessResult::FinishedWithSuccess: response.error = false; break; - case QtcProcess::FinishedWithError: + case ProcessResult::FinishedWithError: response.message = msgExitCode(process.exitCode()); response.error = !(flags & IgnoreExitCode); break; - case QtcProcess::TerminatedAbnormally: + case ProcessResult::TerminatedAbnormally: response.message = msgCrash(); break; - case QtcProcess::StartFailed: + case ProcessResult::StartFailed: response.message = msgNotStarted(m_settings.p4BinaryPath.value()); break; - case QtcProcess::Hang: + case ProcessResult::Hang: response.message = msgCrash(); break; } diff --git a/src/plugins/perfprofiler/perfconfigwidget.cpp b/src/plugins/perfprofiler/perfconfigwidget.cpp index b58900c5d60..97d5ed752de 100644 --- a/src/plugins/perfprofiler/perfconfigwidget.cpp +++ b/src/plugins/perfprofiler/perfconfigwidget.cpp @@ -29,7 +29,6 @@ #include <coreplugin/messagebox.h> -#include <projectexplorer/devicesupport/deviceprocess.h> #include <projectexplorer/kit.h> #include <projectexplorer/kitinformation.h> #include <projectexplorer/runcontrol.h> @@ -43,7 +42,9 @@ #include <QHeaderView> #include <QMessageBox> #include <QMetaEnum> +#include <QPushButton> #include <QStyledItemDelegate> +#include <QTableView> using namespace Utils; @@ -124,6 +125,8 @@ PerfConfigWidget::PerfConfigWidget(PerfSettings *settings, QWidget *parent) }.attachTo(this); } +PerfConfigWidget::~PerfConfigWidget() = default; + void PerfConfigWidget::setTarget(ProjectExplorer::Target *target) { ProjectExplorer::IDevice::ConstPtr device; @@ -146,10 +149,10 @@ void PerfConfigWidget::setTarget(ProjectExplorer::Target *target) return; } - connect(m_process.get(), &ProjectExplorer::DeviceProcess::finished, + connect(m_process.get(), &QtcProcess::finished, this, &PerfConfigWidget::handleProcessFinished); - connect(m_process.get(), &ProjectExplorer::DeviceProcess::errorOccurred, + connect(m_process.get(), &QtcProcess::errorOccurred, this, &PerfConfigWidget::handleProcessError); useTracePointsButton->setEnabled(true); @@ -173,9 +176,8 @@ void PerfConfigWidget::readTracePoints() messageBox.setText(tr("Replace events with trace points read from the device?")); messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); if (messageBox.exec() == QMessageBox::Yes) { - ProjectExplorer::Runnable runnable; - runnable.command = {"perf", {"probe", "-l"}}; - m_process->start(runnable); + m_process->setCommand({"perf", {"probe", "-l"}}); + m_process->start(); useTracePointsButton->setEnabled(false); } } diff --git a/src/plugins/perfprofiler/perfconfigwidget.h b/src/plugins/perfprofiler/perfconfigwidget.h index bb4795a7910..4f6c5c5602b 100644 --- a/src/plugins/perfprofiler/perfconfigwidget.h +++ b/src/plugins/perfprofiler/perfconfigwidget.h @@ -29,10 +29,14 @@ #include <coreplugin/dialogs/ioptionspage.h> -#include <projectexplorer/devicesupport/deviceprocess.h> +#include <QProcess> -#include <QPushButton> -#include <QTableView> +QT_BEGIN_NAMESPACE +class QPushButton; +class QTableView; +QT_END_NAMESPACE + +namespace Utils { class QtcProcess; } namespace PerfProfiler { namespace Internal { @@ -42,6 +46,7 @@ class PerfConfigWidget : public Core::IOptionsPageWidget Q_OBJECT public: explicit PerfConfigWidget(PerfSettings *settings, QWidget *parent = nullptr); + ~PerfConfigWidget(); void updateUi(); void setTarget(ProjectExplorer::Target *target); @@ -55,7 +60,7 @@ private: void handleProcessError(QProcess::ProcessError error); PerfSettings *m_settings; - std::unique_ptr<ProjectExplorer::DeviceProcess> m_process; + std::unique_ptr<Utils::QtcProcess> m_process; QTableView *eventsView; QPushButton *useTracePointsButton; diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp index 684918bb6a8..f638d14c903 100644 --- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp +++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp @@ -32,7 +32,6 @@ #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> -#include <projectexplorer/devicesupport/deviceprocess.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/kitinformation.h> #include <projectexplorer/target.h> @@ -131,9 +130,9 @@ public: return; } - connect(m_process, &DeviceProcess::started, this, &RunWorker::reportStarted); - connect(m_process, &DeviceProcess::finished, this, &RunWorker::reportStopped); - connect(m_process, &DeviceProcess::errorOccurred, [this](QProcess::ProcessError e) { + connect(m_process, &QtcProcess::started, this, &RunWorker::reportStarted); + connect(m_process, &QtcProcess::finished, this, &RunWorker::reportStopped); + connect(m_process, &QtcProcess::errorOccurred, [this](QProcess::ProcessError e) { // The terminate() below will frequently lead to QProcess::Crashed. We're not interested // in that. FailedToStart is the only actual failure. if (e == QProcess::FailedToStart) { @@ -147,15 +146,13 @@ public: Runnable perfRunnable = runnable(); - QStringList arguments; - arguments << "record"; - arguments += m_perfRecordArguments; - arguments << "-o" << "-" << "--" << perfRunnable.command.executable().toString() - << ProcessArgs::splitArgs(perfRunnable.command.arguments(), OsTypeLinux); + CommandLine cmd({"perf", {"record"}}); + cmd.addArgs(m_perfRecordArguments); + cmd.addArgs({"-o", "-", "--"}); + cmd.addCommandLineAsArgs(perfRunnable.command, CommandLine::Raw); - perfRunnable.command.setExecutable("perf"); - perfRunnable.command.setArguments(ProcessArgs::joinArgs(arguments, OsTypeLinux)); - m_process->start(perfRunnable); + m_process->setCommand(cmd); + m_process->start(); } void stop() override @@ -164,10 +161,10 @@ public: m_process->terminate(); } - DeviceProcess *recorder() { return m_process; } + QtcProcess *recorder() { return m_process; } private: - QPointer<DeviceProcess> m_process; + QPointer<QtcProcess> m_process; QStringList m_perfRecordArguments; }; @@ -216,12 +213,12 @@ void PerfProfilerRunner::start() PerfDataReader *reader = m_perfParserWorker->reader(); if (auto prw = qobject_cast<LocalPerfRecordWorker *>(m_perfRecordWorker)) { // That's the local case. - DeviceProcess *recorder = prw->recorder(); - connect(recorder, &DeviceProcess::readyReadStandardError, this, [this, recorder] { + QtcProcess *recorder = prw->recorder(); + connect(recorder, &QtcProcess::readyReadStandardError, this, [this, recorder] { appendMessage(QString::fromLocal8Bit(recorder->readAllStandardError()), Utils::StdErrFormat); }); - connect(recorder, &DeviceProcess::readyReadStandardOutput, this, [this, reader, recorder] { + connect(recorder, &QtcProcess::readyReadStandardOutput, this, [this, reader, recorder] { if (!reader->feedParser(recorder->readAllStandardOutput())) reportFailure(tr("Failed to transfer Perf data to perfparser.")); }); diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp index 3bb2197ea58..3a360c5c974 100644 --- a/src/plugins/perfprofiler/perftracepointdialog.cpp +++ b/src/plugins/perfprofiler/perftracepointdialog.cpp @@ -35,9 +35,8 @@ #include <projectexplorer/target.h> #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> -#include <QVBoxLayout> -#include <QHBoxLayout> #include <QPushButton> #include <QTimer> @@ -84,8 +83,8 @@ PerfTracePointDialog::PerfTracePointDialog() : PerfTracePointDialog::~PerfTracePointDialog() { if (m_process && m_process->state() != QProcess::NotRunning) { - DeviceProcess *process = m_process.release(); - connect(process, &DeviceProcess::finished, process, &QObject::deleteLater); + QtcProcess *process = m_process.release(); + connect(process, &QtcProcess::finished, process, &QObject::deleteLater); process->kill(); QTimer::singleShot(10000, process, &QObject::deleteLater); } @@ -99,30 +98,22 @@ void PerfTracePointDialog::runScript() m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); m_process.reset(m_device->createProcess(this)); + m_process->setWriteData(m_ui->textEdit->toPlainText().toUtf8()); + m_ui->textEdit->clear(); - Runnable runnable; const QString elevate = m_ui->privilegesChooser->currentText(); if (elevate != QLatin1String("n.a.")) - runnable.command = {FilePath::fromString(elevate), {"sh"}}; + m_process->setCommand({FilePath::fromString(elevate), {"sh"}}); else - runnable.command = {"sh", {}}; - - connect(m_process.get(), &DeviceProcess::started, - this, &PerfTracePointDialog::feedScriptToProcess); + m_process->setCommand({"sh", {}}); - connect(m_process.get(), &DeviceProcess::finished, + connect(m_process.get(), &QtcProcess::finished, this, &PerfTracePointDialog::handleProcessFinished); - connect(m_process.get(), &DeviceProcess::errorOccurred, + connect(m_process.get(), &QtcProcess::errorOccurred, this, &PerfTracePointDialog::handleProcessError); - m_process->start(runnable); -} - -void PerfTracePointDialog::feedScriptToProcess() -{ - m_process->write(m_ui->textEdit->toPlainText().toUtf8()); - m_ui->textEdit->clear(); + m_process->start(); } void PerfTracePointDialog::handleProcessFinished() diff --git a/src/plugins/perfprofiler/perftracepointdialog.h b/src/plugins/perfprofiler/perftracepointdialog.h index ca5d4e2ae52..94838d59172 100644 --- a/src/plugins/perfprofiler/perftracepointdialog.h +++ b/src/plugins/perfprofiler/perftracepointdialog.h @@ -25,14 +25,10 @@ #pragma once -#include <projectexplorer/devicesupport/deviceprocess.h> #include <projectexplorer/devicesupport/idevice.h> #include <QDialog> -#include <QTextEdit> -#include <QLabel> -#include <QDialogButtonBox> -#include <QComboBox> +#include <QProcess> namespace PerfProfiler { namespace Internal { @@ -49,14 +45,13 @@ public: private: void runScript(); - void feedScriptToProcess(); void handleProcessFinished(); void handleProcessError(QProcess::ProcessError error); void finish(); Ui::PerfTracePointDialog *m_ui; ProjectExplorer::IDevice::ConstPtr m_device; - std::unique_ptr<ProjectExplorer::DeviceProcess> m_process; + std::unique_ptr<Utils::QtcProcess> m_process; void accept() final; void reject() final; diff --git a/src/plugins/perfprofiler/tracepoints.sh b/src/plugins/perfprofiler/tracepoints.sh index 1352c4dd324..989e5004bf5 100644 --- a/src/plugins/perfprofiler/tracepoints.sh +++ b/src/plugins/perfprofiler/tracepoints.sh @@ -49,8 +49,20 @@ match_tracepoints() { BASE="perfprofiler_${MACHINE}_${NAME}" RETURN=`perf probe -l "${BASE}_ret" | awk '{print $3}'` + if [ -z "$RETURN" ]; then + RETURN=`perf probe -l "${BASE}_ret__return" | awk '{print $3}'` + fi + + CHECK= + for RET in $RETURN; do + if [ -n "$CHECK" ]; then + CHECK="$CHECK && " + fi + CHECK="$CHECK\$3 != \"$RET\"" + done + ENTRY=`echo ${RETURN} | awk '{sub(/%return/, ""); print $1}'` - BAD=`perf probe -l "${BASE}*" | awk '{ if ($3 != "'$RETURN'" && $3 != "'$ENTRY'") { print $1 } }'` + BAD=`perf probe -l "${BASE}*" | awk '{ if ('"$CHECK"' && $3 != "'$ENTRY'") { print $1 } }'` for PROBE in $BAD; do perf probe -d $PROBE done @@ -121,7 +133,7 @@ set_tracepoint() { } HOST_MACHINE=`uname -m` -find /lib -name libc.so.6 | while read LIBC; do +find /lib/ -name libc.so.6 | while read LIBC; do echo $LIBC | awk -F '/' '{print $(NF-1)}' | while IFS='-' read MACHINE KERNEL SYSTEM; do if [ "$MACHINE" = "lib" ]; then MACHINE=$HOST_MACHINE; fi >&2 echo "</pre><h3>Removing old trace points for $MACHINE</h3><pre>" diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs index 504a47a1669..efd1f689731 100644 --- a/src/plugins/plugins.qbs +++ b/src/plugins/plugins.qbs @@ -80,7 +80,6 @@ Project { "valgrind/valgrind.qbs", "vcsbase/vcsbase.qbs", "webassembly/webassembly.qbs", - "welcome/welcome.qbs", - "winrt/winrt.qbs" + "welcome/welcome.qbs" ].concat(project.additionalPlugins) } diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index a123b7a1170..a48c08452b4 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -49,13 +49,11 @@ add_qtc_plugin(ProjectExplorer desktoprunconfiguration.cpp desktoprunconfiguration.h devicesupport/desktopdevice.cpp devicesupport/desktopdevice.h devicesupport/desktopdevicefactory.cpp devicesupport/desktopdevicefactory.h - devicesupport/desktopdeviceprocess.cpp devicesupport/desktopdeviceprocess.h devicesupport/desktopprocesssignaloperation.cpp devicesupport/desktopprocesssignaloperation.h devicesupport/devicecheckbuildstep.cpp devicesupport/devicecheckbuildstep.h devicesupport/devicefactoryselectiondialog.cpp devicesupport/devicefactoryselectiondialog.h devicesupport/devicefactoryselectiondialog.ui devicesupport/devicemanager.cpp devicesupport/devicemanager.h devicesupport/devicemanagermodel.cpp devicesupport/devicemanagermodel.h - devicesupport/deviceprocess.cpp devicesupport/deviceprocess.h devicesupport/deviceprocessesdialog.cpp devicesupport/deviceprocessesdialog.h devicesupport/deviceprocesslist.cpp devicesupport/deviceprocesslist.h devicesupport/devicesettingspage.cpp devicesupport/devicesettingspage.h diff --git a/src/plugins/projectexplorer/applicationlauncher.cpp b/src/plugins/projectexplorer/applicationlauncher.cpp index ab685ae8cbd..acd8d31ce10 100644 --- a/src/plugins/projectexplorer/applicationlauncher.cpp +++ b/src/plugins/projectexplorer/applicationlauncher.cpp @@ -35,7 +35,7 @@ #include <utils/qtcassert.h> #include <utils/qtcprocess.h> -#include "devicesupport/deviceprocess.h" +#include "devicesupport/desktopdevice.h" #include "projectexplorer.h" #include "projectexplorersettings.h" #include "runcontrol.h" @@ -70,25 +70,22 @@ public: explicit ApplicationLauncherPrivate(ApplicationLauncher *parent); ~ApplicationLauncherPrivate() override { setFinished(); } - void start(const Runnable &runnable, const IDevice::ConstPtr &device, bool local); + void start(); void stop(); + void handleStandardOutput(); + void handleStandardError(); + // Local - void handleProcessStarted(); void localProcessError(QProcess::ProcessError error); - void readLocalStandardOutput(); - void readLocalStandardError(); void cannotRetrieveLocalDebugOutput(); void checkLocalDebugOutput(qint64 pid, const QString &message); - void localProcessDone(int, QProcess::ExitStatus); qint64 applicationPID() const; - bool isLocalRunning() const; + bool isRunning() const; // Remote void doReportError(const QString &message, QProcess::ProcessError error = QProcess::FailedToStart); - void handleRemoteStderr(); - void handleRemoteStdout(); void handleApplicationFinished(); void setFinished(); void handleApplicationError(QProcess::ProcessError error); @@ -99,26 +96,30 @@ public: bool m_isLocal = true; bool m_runAsRoot = false; + std::unique_ptr<QtcProcess> m_process; + + QTextCodec *m_outputCodec = nullptr; + QTextCodec::ConverterState m_outputCodecState; + QTextCodec::ConverterState m_errorCodecState; + // Local - std::unique_ptr<QtcProcess> m_localProcess; bool m_useTerminal = false; QProcess::ProcessChannelMode m_processChannelMode; // Keep track whether we need to emit a finished signal bool m_processRunning = false; - QTextCodec *m_outputCodec; - QTextCodec::ConverterState m_outputCodecState; - QTextCodec::ConverterState m_errorCodecState; - qint64 m_listeningPid = 0; // Remote - DeviceProcess *m_deviceProcess = nullptr; QString m_remoteErrorString; QProcess::ProcessError m_remoteError = QProcess::UnknownError; - QProcess::ExitStatus m_remoteExitStatus = QProcess::CrashExit; State m_state = Inactive; bool m_stopRequested = false; + + Runnable m_runnable; + + int m_exitCode = 0; + QProcess::ExitStatus m_exitStatus = QProcess::NormalExit; }; } // Internal @@ -132,7 +133,6 @@ static QProcess::ProcessChannelMode defaultProcessChannelMode() ApplicationLauncherPrivate::ApplicationLauncherPrivate(ApplicationLauncher *parent) : q(parent) , m_processChannelMode(defaultProcessChannelMode()) - , m_outputCodec(QTextCodec::codecForLocale()) { #ifdef Q_OS_WIN connect(WinDebugInterface::instance(), &WinDebugInterface::cannotRetrieveDebugOutput, @@ -164,6 +164,11 @@ void ApplicationLauncher::setRunAsRoot(bool on) d->m_runAsRoot = on; } +void ApplicationLauncher::setRunnable(const Runnable &runnable) +{ + d->m_runnable = runnable; +} + void ApplicationLauncher::stop() { d->stop(); @@ -171,22 +176,23 @@ void ApplicationLauncher::stop() void ApplicationLauncherPrivate::stop() { + m_exitStatus = QProcess::CrashExit; if (m_isLocal) { - if (!isLocalRunning()) + if (!isRunning()) return; - QTC_ASSERT(m_localProcess, return); - m_localProcess->stopProcess(); - localProcessDone(0, QProcess::CrashExit); + QTC_ASSERT(m_process, return); + m_listeningPid = 0; + m_process->stopProcess(); + QTimer::singleShot(100, this, [this] { emit q->finished(); }); } else { if (m_stopRequested) return; m_stopRequested = true; - m_remoteExitStatus = QProcess::CrashExit; emit q->appendMessage(ApplicationLauncher::tr("User requested stop. Shutting down..."), - Utils::NormalMessageFormat); + NormalMessageFormat); switch (m_state) { case Run: - m_deviceProcess->terminate(); + m_process->terminate(); break; case Inactive: break; @@ -196,7 +202,7 @@ void ApplicationLauncherPrivate::stop() bool ApplicationLauncher::isRunning() const { - return d->isLocalRunning(); + return d->isRunning(); } bool ApplicationLauncher::isLocal() const @@ -204,11 +210,11 @@ bool ApplicationLauncher::isLocal() const return d->m_isLocal; } -bool ApplicationLauncherPrivate::isLocalRunning() const +bool ApplicationLauncherPrivate::isRunning() const { - if (!m_localProcess) + if (!m_process) return false; - return m_localProcess->state() != QProcess::NotRunning; + return m_process->state() != QProcess::NotRunning; } ProcessHandle ApplicationLauncher::applicationPID() const @@ -218,23 +224,23 @@ ProcessHandle ApplicationLauncher::applicationPID() const qint64 ApplicationLauncherPrivate::applicationPID() const { - if (!isLocalRunning()) + if (!isRunning()) return 0; - return m_localProcess->processId(); + return m_process->processId(); } QString ApplicationLauncher::errorString() const { if (d->m_isLocal) - return d->m_localProcess ? d->m_localProcess->errorString() : QString(); + return d->m_process ? d->m_process->errorString() : QString(); return d->m_remoteErrorString; } -QProcess::ProcessError ApplicationLauncher::processError() const +QProcess::ProcessError ApplicationLauncher::error() const { if (d->m_isLocal) - return d->m_localProcess ? d->m_localProcess->error() : QProcess::UnknownError; + return d->m_process ? d->m_process->error() : QProcess::UnknownError; return d->m_remoteError; } @@ -242,45 +248,46 @@ void ApplicationLauncherPrivate::localProcessError(QProcess::ProcessError error) { // TODO: why below handlings are different? if (m_useTerminal) { - emit q->appendMessage(m_localProcess->errorString(), ErrorMessageFormat); - if (m_processRunning && m_localProcess->processId() == 0) { + emit q->appendMessage(m_process->errorString(), ErrorMessageFormat); + if (m_processRunning && m_process->processId() == 0) { m_processRunning = false; - emit q->processExited(-1, QProcess::NormalExit); + m_exitCode = -1; + emit q->finished(); } } else { QString error; - QProcess::ExitStatus status = QProcess::NormalExit; - switch (m_localProcess->error()) { + switch (m_process->error()) { case QProcess::FailedToStart: error = ApplicationLauncher::tr("Failed to start program. Path or permissions wrong?"); break; case QProcess::Crashed: - status = QProcess::CrashExit; + m_exitStatus = QProcess::CrashExit; break; default: error = ApplicationLauncher::tr("Some error has occurred while running the program."); } if (!error.isEmpty()) emit q->appendMessage(error, ErrorMessageFormat); - if (m_processRunning && !isLocalRunning()) { + if (m_processRunning && !isRunning()) { m_processRunning = false; - emit q->processExited(-1, status); + m_exitCode = -1; + emit q->finished(); } } - emit q->error(error); + emit q->errorOccurred(error); } -void ApplicationLauncherPrivate::readLocalStandardOutput() +void ApplicationLauncherPrivate::handleStandardOutput() { - const QByteArray data = m_localProcess->readAllStandardOutput(); + const QByteArray data = m_process->readAllStandardOutput(); const QString msg = m_outputCodec->toUnicode( data.constData(), data.length(), &m_outputCodecState); emit q->appendMessage(msg, StdOutFormat, false); } -void ApplicationLauncherPrivate::readLocalStandardError() +void ApplicationLauncherPrivate::handleStandardError() { - const QByteArray data = m_localProcess->readAllStandardError(); + const QByteArray data = m_process->readAllStandardError(); const QString msg = m_outputCodec->toUnicode( data.constData(), data.length(), &m_errorCodecState); emit q->appendMessage(msg, StdErrFormat, false); @@ -300,72 +307,54 @@ void ApplicationLauncherPrivate::checkLocalDebugOutput(qint64 pid, const QString emit q->appendMessage(message, DebugFormat); } -void ApplicationLauncherPrivate::localProcessDone(int exitCode, QProcess::ExitStatus status) -{ - QTimer::singleShot(100, this, [this, exitCode, status]() { - m_listeningPid = 0; - emit q->processExited(exitCode, status); - }); -} - QString ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput() { return tr("Cannot retrieve debugging output.") + QLatin1Char('\n'); } -void ApplicationLauncherPrivate::handleProcessStarted() +int ApplicationLauncher::exitCode() const { - m_listeningPid = applicationPID(); - emit q->processStarted(); + return d->m_exitCode; } -void ApplicationLauncher::start(const Runnable &runnable) +QProcess::ExitStatus ApplicationLauncher::exitStatus() const { - d->start(runnable, IDevice::ConstPtr(), true); + return d->m_exitStatus; } -void ApplicationLauncher::start(const Runnable &runnable, const IDevice::ConstPtr &device) +void ApplicationLauncher::start() { - d->start(runnable, device, false); + d->start(); } -void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice::ConstPtr &device, bool local) +void ApplicationLauncherPrivate::start() { - m_isLocal = local; + m_isLocal = m_runnable.device.isNull() || m_runnable.device.dynamicCast<const DesktopDevice>(); + + m_exitCode = 0; + m_exitStatus = QProcess::NormalExit; if (m_isLocal) { - const QtcProcess::TerminalMode terminalMode = m_useTerminal - ? QtcProcess::TerminalOn : QtcProcess::TerminalOff; - m_localProcess.reset(new QtcProcess(terminalMode, this)); - m_localProcess->setProcessChannelMode(m_processChannelMode); - - if (m_processChannelMode == QProcess::SeparateChannels) { - connect(m_localProcess.get(), &QtcProcess::readyReadStandardError, - this, &ApplicationLauncherPrivate::readLocalStandardError); - } - if (!m_useTerminal) { - connect(m_localProcess.get(), &QtcProcess::readyReadStandardOutput, - this, &ApplicationLauncherPrivate::readLocalStandardOutput); - } + m_process.reset(new QtcProcess(this)); - connect(m_localProcess.get(), &QtcProcess::started, - this, &ApplicationLauncherPrivate::handleProcessStarted); - connect(m_localProcess.get(), &QtcProcess::finished, this, [this] { - localProcessDone(m_localProcess->exitCode(), m_localProcess->exitStatus()); + connect(m_process.get(), &QtcProcess::finished, this, [this] { + m_exitCode = m_process->exitCode(); + m_exitStatus = m_process->exitStatus(); + m_listeningPid = 0; + emit q->finished(); }); - connect(m_localProcess.get(), &QtcProcess::errorOccurred, + connect(m_process.get(), &QtcProcess::errorOccurred, this, &ApplicationLauncherPrivate::localProcessError); - // Work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch' ...) - const FilePath fixedPath = runnable.workingDirectory.normalizedPathName(); - m_localProcess->setWorkingDirectory(fixedPath); + const FilePath fixedPath = m_runnable.workingDirectory.normalizedPathName(); + m_process->setWorkingDirectory(fixedPath); - Environment env = runnable.environment; + Environment env = m_runnable.environment; if (m_runAsRoot) RunControl::provideAskPassEntry(env); - m_localProcess->setEnvironment(env); + m_process->setEnvironment(env); m_processRunning = true; #ifdef Q_OS_WIN @@ -373,7 +362,7 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice:: WinDebugInterface::instance()->start(); // Try to start listener again... #endif - CommandLine cmdLine = runnable.command; + CommandLine cmdLine = m_runnable.command; if (HostOsInfo::isMacHost()) { CommandLine disclaim(Core::ICore::libexecPath("disclaim")); @@ -381,55 +370,74 @@ void ApplicationLauncherPrivate::start(const Runnable &runnable, const IDevice:: cmdLine = disclaim; } - m_localProcess->setRunAsRoot(m_runAsRoot); - m_localProcess->setCommand(cmdLine); - m_localProcess->start(); + m_process->setRunAsRoot(m_runAsRoot); + m_process->setCommand(cmdLine); } else { QTC_ASSERT(m_state == Inactive, return); m_state = Run; - if (!device) { + if (!m_runnable.device) { doReportError(ApplicationLauncher::tr("Cannot run: No device.")); setFinished(); return; } - if (!device->canCreateProcess()) { + if (!m_runnable.device->canCreateProcess()) { doReportError(ApplicationLauncher::tr("Cannot run: Device is not able to create processes.")); setFinished(); return; } - if (!device->isEmptyCommandAllowed() && runnable.command.isEmpty()) { + if (!m_runnable.device->isEmptyCommandAllowed() && m_runnable.command.isEmpty()) { doReportError(ApplicationLauncher::tr("Cannot run: No command given.")); setFinished(); return; } m_stopRequested = false; - m_remoteExitStatus = QProcess::NormalExit; - - m_deviceProcess = device->createProcess(this); - m_deviceProcess->setRunInTerminal(m_useTerminal); - connect(m_deviceProcess, &DeviceProcess::started, - q, &ApplicationLauncher::processStarted); - connect(m_deviceProcess, &DeviceProcess::readyReadStandardOutput, - this, &ApplicationLauncherPrivate::handleRemoteStdout); - connect(m_deviceProcess, &DeviceProcess::readyReadStandardError, - this, &ApplicationLauncherPrivate::handleRemoteStderr); - connect(m_deviceProcess, &DeviceProcess::errorOccurred, + + m_process.reset(m_runnable.device->createProcess(this)); + connect(m_process.get(), &QtcProcess::errorOccurred, this, &ApplicationLauncherPrivate::handleApplicationError); - connect(m_deviceProcess, &DeviceProcess::finished, + connect(m_process.get(), &QtcProcess::finished, this, &ApplicationLauncherPrivate::handleApplicationFinished); - m_deviceProcess->start(runnable); + m_process->setCommand(m_runnable.command); + m_process->setWorkingDirectory(m_runnable.workingDirectory); + m_process->setRemoteEnvironment(m_runnable.environment); + m_process->setExtraData(m_runnable.extraData); + } + + if (m_isLocal) + m_outputCodec = QTextCodec::codecForLocale(); + else + m_outputCodec = QTextCodec::codecForName("utf8"); + + connect(m_process.get(), &QtcProcess::started, this, [this] { + // The local bit affects only WinDebugInterface. + if (m_isLocal) + m_listeningPid = applicationPID(); + emit q->started(); + }); + + m_process->setProcessChannelMode(m_processChannelMode); + if (m_processChannelMode == QProcess::SeparateChannels) { + connect(m_process.get(), &QtcProcess::readyReadStandardError, + this, &ApplicationLauncherPrivate::handleStandardError); + } + if (!m_useTerminal) { + connect(m_process.get(), &QtcProcess::readyReadStandardOutput, + this, &ApplicationLauncherPrivate::handleStandardOutput); } + + m_process->setTerminalMode(m_useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off); + m_process->start(); } void ApplicationLauncherPrivate::handleApplicationError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { doReportError(ApplicationLauncher::tr("Application failed to start: %1") - .arg(m_deviceProcess->errorString())); + .arg(m_process->errorString())); setFinished(); } } @@ -439,43 +447,27 @@ void ApplicationLauncherPrivate::setFinished() if (m_state == Inactive) return; - int exitCode = 0; - if (m_deviceProcess) - exitCode = m_deviceProcess->exitCode(); + m_exitCode = m_process ? m_process->exitCode() : 0; m_state = Inactive; - emit q->processExited(exitCode, m_remoteExitStatus); + emit q->finished(); } void ApplicationLauncherPrivate::handleApplicationFinished() { QTC_ASSERT(m_state == Run, return); - if (m_deviceProcess->exitStatus() == QProcess::CrashExit) - doReportError(m_deviceProcess->errorString(), QProcess::Crashed); + if (m_process->exitStatus() == QProcess::CrashExit) + doReportError(m_process->errorString(), QProcess::Crashed); setFinished(); } -void ApplicationLauncherPrivate::handleRemoteStdout() -{ - QTC_ASSERT(m_state == Run, return); - const QByteArray output = m_deviceProcess->readAllStandardOutput(); - emit q->appendMessage(QString::fromUtf8(output), Utils::StdOutFormat, false); -} - -void ApplicationLauncherPrivate::handleRemoteStderr() -{ - QTC_ASSERT(m_state == Run, return); - const QByteArray output = m_deviceProcess->readAllStandardError(); - emit q->appendMessage(QString::fromUtf8(output), Utils::StdErrFormat, false); -} - void ApplicationLauncherPrivate::doReportError(const QString &message, QProcess::ProcessError error) { m_remoteErrorString = message; m_remoteError = error; - m_remoteExitStatus = QProcess::CrashExit; - emit q->error(error); + m_exitStatus = QProcess::CrashExit; + emit q->errorOccurred(error); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/applicationlauncher.h b/src/plugins/projectexplorer/applicationlauncher.h index 86b9c6f3cf3..5d339fd2711 100644 --- a/src/plugins/projectexplorer/applicationlauncher.h +++ b/src/plugins/projectexplorer/applicationlauncher.h @@ -55,23 +55,27 @@ public: void setProcessChannelMode(QProcess::ProcessChannelMode mode); void setUseTerminal(bool on); void setRunAsRoot(bool on); - void start(const Runnable &runnable); - void start(const Runnable &runnable, const IDevice::ConstPtr &device); + void setRunnable(const Runnable &runnable); + + void start(); void stop(); bool isRunning() const; Utils::ProcessHandle applicationPID() const; bool isLocal() const; QString errorString() const; - QProcess::ProcessError processError() const; + QProcess::ProcessError error() const; static QString msgWinCannotRetrieveDebuggingOutput(); + int exitCode() const; + QProcess::ExitStatus exitStatus() const; + signals: void appendMessage(const QString &message, Utils::OutputFormat format, bool appendNewLine = true); - void processStarted(); - void processExited(int exitCode, QProcess::ExitStatus exitStatus); - void error(QProcess::ProcessError error); + void started(); + void finished(); + void errorOccurred(QProcess::ProcessError error); private: std::unique_ptr<Internal::ApplicationLauncherPrivate> d; diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 9fc9562355e..c8ee2003def 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -174,7 +174,7 @@ AppOutputPane::AppOutputPane() : m_formatterWidget(new QWidget), m_handler(new ShowOutputTaskHandler(this, tr("Show &App Output"), - tr("Show the output that generated this issue in the Application Output pane."), + tr("Show the output that generated this issue in Application Output."), tr("A"))) { ExtensionSystem::PluginManager::addObject(m_handler); @@ -874,8 +874,8 @@ public: maxCharsLayout->addWidget(new QLabel(parts.at(1).trimmed())); maxCharsLayout->addStretch(1); const auto outputModeLayout = new QFormLayout; - outputModeLayout->addRow(tr("Open pane on output when running:"), &m_runOutputModeComboBox); - outputModeLayout->addRow(tr("Open pane on output when debugging:"), + outputModeLayout->addRow(tr("Open Application Output when running:"), &m_runOutputModeComboBox); + outputModeLayout->addRow(tr("Open Application Output when debugging:"), &m_debugOutputModeComboBox); layout->addLayout(outputModeLayout); layout->addLayout(maxCharsLayout); diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 630925db048..481c16be811 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -49,10 +49,9 @@ #include <utils/algorithm.h> #include <utils/detailswidget.h> -#include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> -#include <utils/mimetypes/mimetype.h> #include <utils/layoutbuilder.h> +#include <utils/macroexpander.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/variablechooser.h> diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index 94bdf78accc..d66a3a4e80e 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -113,7 +113,7 @@ \fn void ProjectExplorer::BuildStep::addOutput(const QString &string, ProjectExplorer::BuildStep::OutputFormat format, ProjectExplorer::BuildStep::OutputNewlineSetting newlineSetting = DoAppendNewline) const - The \a string is added to the generated output, usually in the output pane. + The \a string is added to the generated output, usually in the output. It should be in plain text, with the format in the parameter. */ diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index abb6736a955..151f69ff3db 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -118,7 +118,7 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_handler = new ShowOutputTaskHandler(this, tr("Show Compile &Output"), - tr("Show the output that generated this issue in the Compile Output pane."), + tr("Show the output that generated this issue in Compile Output."), tr("O")); ExtensionSystem::PluginManager::addObject(m_handler); setupContext(C_COMPILE_OUTPUT, m_outputWindow); @@ -283,7 +283,7 @@ public: const CompileOutputSettings &settings = BuildManager::compileOutputSettings(); m_wrapOutputCheckBox.setText(tr("Word-wrap output")); m_wrapOutputCheckBox.setChecked(settings.wrapOutput); - m_popUpCheckBox.setText(tr("Open pane when building")); + m_popUpCheckBox.setText(tr("Open Compile Output when building")); m_popUpCheckBox.setChecked(settings.popUp); m_maxCharsBox.setMaximum(100000000); m_maxCharsBox.setValue(settings.maxCharCount); diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp index b3006f4ba04..0229a5287ce 100644 --- a/src/plugins/projectexplorer/customparser.cpp +++ b/src/plugins/projectexplorer/customparser.cpp @@ -288,7 +288,7 @@ public: const auto layout = new QVBoxLayout(this); const auto explanatoryLabel = new QLabel(tr( "Custom output parsers scan command line output for user-provided error patterns<br>" - "in order to create entries in the issues pane.<br>" + "to create entries in Issues.<br>" "The parsers can be configured <a href=\"dummy\">here</a>.")); layout->addWidget(explanatoryLabel); connect(explanatoryLabel, &QLabel::linkActivated, [] { diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp index 5edaf059198..93b38e0584a 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp @@ -30,7 +30,7 @@ #include <cppeditor/cppeditorconstants.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> #include <utils/templateengine.h> diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp index d2170775b6c..8a3327a8aa0 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp @@ -115,8 +115,8 @@ static bool qDebug("In %s, running:\n%s\n", qPrintable(workingDirectory.toUserOutput()), qPrintable(cmd.toUserOutput())); process.setCommand(cmd); - process.runBlocking(QtcProcess::WithEventLoop); - if (process.result() != Utils::QtcProcess::FinishedWithSuccess) { + process.runBlocking(EventLoopMode::On); + if (process.result() != Utils::ProcessResult::FinishedWithSuccess) { *errorMessage = QString("Generator script failed: %1").arg(process.exitMessage()); const QString stdErr = process.stdErr(); if (!stdErr.isEmpty()) { diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index 87efde505e9..17d2d52e70a 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -24,7 +24,6 @@ ****************************************************************************/ #include "desktopdevice.h" -#include "desktopdeviceprocess.h" #include "deviceprocesslist.h" #include "localprocesslist.h" #include "desktopprocesssignaloperation.h" @@ -39,6 +38,7 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/portlist.h> +#include <utils/qtcprocess.h> #include <utils/stringutils.h> #include <utils/url.h> @@ -97,9 +97,9 @@ DeviceProcessList *DesktopDevice::createProcessListModel(QObject *parent) const return new Internal::LocalProcessList(sharedFromThis(), parent); } -DeviceProcess *DesktopDevice::createProcess(QObject *parent) const +QtcProcess *DesktopDevice::createProcess(QObject *parent) const { - return new Internal::DesktopDeviceProcess(sharedFromThis(), parent); + return new QtcProcess(parent); } DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index 06502c559d6..0454c9c13cb 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -50,7 +50,7 @@ public: DeviceProcessList *createProcessListModel(QObject *parent) const override; bool canCreateProcess() const override { return true; } ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override; - DeviceProcess *createProcess(QObject *parent) const override; + Utils::QtcProcess *createProcess(QObject *parent) const override; DeviceProcessSignalOperation::Ptr signalOperation() const override; DeviceEnvironmentFetcher::Ptr environmentFetcher() const override; QUrl toolControlChannel(const ControlChannelHint &) const override; diff --git a/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.cpp b/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.cpp deleted file mode 100644 index c2106967f77..00000000000 --- a/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "desktopdeviceprocess.h" - -#include "idevice.h" -#include "../runcontrol.h" - -#include <utils/environment.h> -#include <utils/qtcassert.h> -#include <utils/qtcprocess.h> - -using namespace Utils; - -namespace ProjectExplorer { -namespace Internal { - -DesktopDeviceProcess::DesktopDeviceProcess(const QSharedPointer<const IDevice> &device, - QObject *parent) - : DeviceProcess(device, ProcessMode::Writer, parent) -{ -} - -void DesktopDeviceProcess::start(const Runnable &runnable) -{ - QTC_ASSERT(state() == QProcess::NotRunning, return); - if (runnable.environment.size()) - setEnvironment(runnable.environment); - setWorkingDirectory(runnable.workingDirectory); - setCommand(runnable.command); - QtcProcess::start(); -} - -void DesktopDeviceProcess::interrupt() -{ - device()->signalOperation()->interruptProcess(processId()); -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.h b/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.h deleted file mode 100644 index ed926112a2d..00000000000 --- a/src/plugins/projectexplorer/devicesupport/desktopdeviceprocess.h +++ /dev/null @@ -1,47 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "deviceprocess.h" - -#include <utils/qtcprocess.h> - -namespace ProjectExplorer { -namespace Internal { - -class DesktopDeviceProcess : public DeviceProcess -{ - Q_OBJECT - -public: - DesktopDeviceProcess(const QSharedPointer<const IDevice> &device, QObject *parent = nullptr); - - void start(const Runnable &runnable) override; - void interrupt() override; -}; - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp b/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp index 5a1d0f94277..7057987e896 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp @@ -25,12 +25,11 @@ #include "desktopprocesssignaloperation.h" -#include "localprocesslist.h" - #include <app/app_version.h> #include <utils/winutils.h> #include <utils/fileutils.h> +#include <utils/processinfo.h> #include <QCoreApplication> #include <QDir> @@ -46,6 +45,8 @@ #include <signal.h> #endif // else Q_OS_WIN +using namespace Utils; + namespace ProjectExplorer { void DesktopProcessSignalOperation::killProcess(qint64 pid) @@ -57,9 +58,10 @@ void DesktopProcessSignalOperation::killProcess(qint64 pid) void DesktopProcessSignalOperation::killProcess(const QString &filePath) { m_errorMessage.clear(); - foreach (const DeviceProcessItem &process, Internal::LocalProcessList::getLocalProcesses()) { - if (process.cmdLine == filePath) - killProcessSilently(process.pid); + const QList<ProcessInfo> processInfoList = ProcessInfo::processInfoList(); + for (const ProcessInfo &processInfo : processInfoList) { + if (processInfo.commandLine == filePath) + killProcessSilently(processInfo.processId); } emit finished(m_errorMessage); } @@ -74,9 +76,10 @@ void DesktopProcessSignalOperation::interruptProcess(qint64 pid) void DesktopProcessSignalOperation::interruptProcess(const QString &filePath) { m_errorMessage.clear(); - foreach (const DeviceProcessItem &process, Internal::LocalProcessList::getLocalProcesses()) { - if (process.cmdLine == filePath) - interruptProcessSilently(process.pid); + const QList<ProcessInfo> processInfoList = ProcessInfo::processInfoList(); + for (const ProcessInfo &processInfo : processInfoList) { + if (processInfo.commandLine == filePath) + interruptProcessSilently(processInfo.processId); } emit finished(m_errorMessage); } @@ -105,7 +108,7 @@ void DesktopProcessSignalOperation::killProcessSilently(qint64 pid) |PROCESS_DUP_HANDLE|PROCESS_TERMINATE|PROCESS_CREATE_THREAD|PROCESS_SUSPEND_RESUME; if (const HANDLE handle = OpenProcess(rights, FALSE, DWORD(pid))) { if (!TerminateProcess(handle, UINT(-1))) - appendMsgCannotKill(pid, Utils::winErrorMessage(GetLastError())); + appendMsgCannotKill(pid, winErrorMessage(GetLastError())); CloseHandle(handle); } else { appendMsgCannotKill(pid, tr("Cannot open process.")); @@ -123,10 +126,10 @@ void DesktopProcessSignalOperation::interruptProcessSilently(qint64 pid) #ifdef Q_OS_WIN enum SpecialInterrupt { NoSpecialInterrupt, Win32Interrupt, Win64Interrupt }; - bool is64BitSystem = Utils::is64BitWindowsSystem(); + bool is64BitSystem = is64BitWindowsSystem(); SpecialInterrupt si = NoSpecialInterrupt; if (is64BitSystem) - si = Utils::is64BitWindowsBinary(m_debuggerCommand) ? Win64Interrupt : Win32Interrupt; + si = is64BitWindowsBinary(m_debuggerCommand) ? Win64Interrupt : Win32Interrupt; /* Windows 64 bit has a 32 bit subsystem (WOW64) which makes it possible to run a 32 bit application inside a 64 bit environment. @@ -164,18 +167,18 @@ GDB 32bit | Api | Api | N/A | Win32 inferior = OpenProcess(rights, FALSE, pid); if (inferior == NULL) { appendMsgCannotInterrupt(pid, tr("Cannot open process: %1") - + Utils::winErrorMessage(GetLastError())); + + winErrorMessage(GetLastError())); break; } - bool creatorIs64Bit = Utils::is64BitWindowsBinary( - Utils::FilePath::fromUserInput(QCoreApplication::applicationFilePath())); + bool creatorIs64Bit = is64BitWindowsBinary( + FilePath::fromUserInput(QCoreApplication::applicationFilePath())); if (!is64BitSystem || si == NoSpecialInterrupt || (si == Win64Interrupt && creatorIs64Bit) || (si == Win32Interrupt && !creatorIs64Bit)) { if (!DebugBreakProcess(inferior)) { appendMsgCannotInterrupt(pid, tr("DebugBreakProcess failed:") - + QLatin1Char(' ') + Utils::winErrorMessage(GetLastError())); + + QLatin1Char(' ') + winErrorMessage(GetLastError())); } } else if (si == Win32Interrupt || si == Win64Interrupt) { QString executable = QCoreApplication::applicationDirPath(); diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 2f33c106d7e..50b2e876e63 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -604,6 +604,7 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager DeviceProcessHooks processHooks; + // TODO: remove this hook processHooks.startProcessHook = [](QtcProcess &process) { FilePath filePath = process.commandLine().executable(); auto device = DeviceManager::deviceForPath(filePath); @@ -611,6 +612,12 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager device->runProcess(process); }; + processHooks.processImplHook = [](const FilePath &filePath) -> ProcessInterface * { + auto device = DeviceManager::deviceForPath(filePath); + QTC_ASSERT(device, return nullptr); + return device->createProcessInterface(); + }; + processHooks.systemEnvironmentForBinary = [](const FilePath &filePath) { auto device = DeviceManager::deviceForPath(filePath); QTC_ASSERT(device, return Environment()); diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocess.cpp b/src/plugins/projectexplorer/devicesupport/deviceprocess.cpp deleted file mode 100644 index 84c692e5a47..00000000000 --- a/src/plugins/projectexplorer/devicesupport/deviceprocess.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "deviceprocess.h" - -#include "idevice.h" - -#include <utils/qtcassert.h> -#include <utils/fileutils.h> - -using namespace Utils; - -namespace ProjectExplorer { - -DeviceProcess::DeviceProcess(const IDevice::ConstPtr &device, - const QtcProcess::Setup &setup, - QObject *parent) - : QtcProcess(setup, parent), m_device(device) -{ -} - -IDevice::ConstPtr DeviceProcess::device() const -{ - return m_device; -} - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp index bef4f16974c..82ce1981a6f 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp @@ -30,6 +30,7 @@ #include <utils/fancylineedit.h> #include <utils/itemviews.h> +#include <utils/processinfo.h> #include <utils/qtcassert.h> #include <QDialogButtonBox> @@ -99,7 +100,7 @@ public: void handleProcessListUpdated(); void handleProcessKilled(); void updateButtons(); - DeviceProcessItem selectedProcess() const; + ProcessInfo selectedProcess() const; QDialog *q; DeviceProcessList *processList; @@ -279,11 +280,11 @@ void DeviceProcessesDialogPrivate::updateButtons() errorText->setVisible(!errorText->document()->isEmpty()); } -DeviceProcessItem DeviceProcessesDialogPrivate::selectedProcess() const +ProcessInfo DeviceProcessesDialogPrivate::selectedProcess() const { const QModelIndexList indexes = procView->selectionModel()->selectedIndexes(); if (indexes.empty() || !processList) - return DeviceProcessItem(); + return ProcessInfo(); return processList->at(proxyModel.mapToSource(indexes.first()).row()); } @@ -352,7 +353,7 @@ void DeviceProcessesDialog::showAllDevices() d->updateDevice(); } -DeviceProcessItem DeviceProcessesDialog::currentProcess() const +ProcessInfo DeviceProcessesDialog::currentProcess() const { return d->selectedProcess(); } diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.h b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.h index 7876bf6edd3..d4cbd0c120f 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.h +++ b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.h @@ -33,9 +33,10 @@ #include <memory> +namespace Utils { class ProcessInfo; } + namespace ProjectExplorer { -class DeviceProcessItem; class KitChooser; namespace Internal { class DeviceProcessesDialogPrivate; } @@ -52,7 +53,7 @@ public: void setDevice(const IDevice::ConstPtr &device); void showAllDevices(); - DeviceProcessItem currentProcess() const; + Utils::ProcessInfo currentProcess() const; KitChooser *kitChooser() const; void logMessage(const QString &line); DeviceProcessesDialog(KitChooser *chooser, QWidget *parent); diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp index b54b0baddb0..4187793df9d 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp @@ -24,11 +24,11 @@ ****************************************************************************/ #include "deviceprocesslist.h" -#include "localprocesslist.h" +#include <utils/fileutils.h> +#include <utils/processinfo.h> #include <utils/qtcassert.h> #include <utils/treemodel.h> -#include <utils/fileutils.h> using namespace Utils; @@ -40,12 +40,12 @@ enum State { Inactive, Listing, Killing }; class DeviceProcessTreeItem : public TreeItem { public: - DeviceProcessTreeItem(const DeviceProcessItem &p, Qt::ItemFlags f) : process(p), fl(f) {} + DeviceProcessTreeItem(const ProcessInfo &p, Qt::ItemFlags f) : process(p), fl(f) {} QVariant data(int column, int role) const final; Qt::ItemFlags flags(int) const final { return fl; } - DeviceProcessItem process; + ProcessInfo process; Qt::ItemFlags fl; }; @@ -88,14 +88,14 @@ void DeviceProcessList::update() doUpdate(); } -void DeviceProcessList::reportProcessListUpdated(const QList<DeviceProcessItem> &processes) +void DeviceProcessList::reportProcessListUpdated(const QList<ProcessInfo> &processes) { QTC_ASSERT(d->state == Listing, return); setFinished(); d->model.clear(); - for (const DeviceProcessItem &process : processes) { + for (const ProcessInfo &process : processes) { Qt::ItemFlags fl; - if (process.pid != d->ownPid) + if (process.processId != d->ownPid) fl = Qt::ItemIsEnabled | Qt::ItemIsSelectable; d->model.rootItem()->appendChild(new DeviceProcessTreeItem(process, fl)); } @@ -125,7 +125,7 @@ void DeviceProcessList::reportProcessKilled() emit processKilled(); } -DeviceProcessItem DeviceProcessList::at(int row) const +ProcessInfo DeviceProcessList::at(int row) const { return d->model.rootItem()->childAt(row)->process; } @@ -139,9 +139,9 @@ QVariant DeviceProcessTreeItem::data(int column, int role) const { if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { if (column == 0) - return process.pid ? process.pid : QVariant(); + return process.processId ? process.processId : QVariant(); else - return process.cmdLine; + return process.commandLine; } return QVariant(); } @@ -163,18 +163,4 @@ void DeviceProcessList::reportError(const QString &message) emit error(message); } -QList<DeviceProcessItem> DeviceProcessList::localProcesses() -{ - return LocalProcessList::getLocalProcesses(); -} - -bool DeviceProcessItem::operator <(const DeviceProcessItem &other) const -{ - if (pid != other.pid) - return pid < other.pid; - if (exe != other.exe) - return exe < other.exe; - return cmdLine < other.cmdLine; -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h index bd39744e36c..f6b0ecb89bb 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h +++ b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h @@ -32,20 +32,12 @@ #include <memory> +namespace Utils { class ProcessInfo; } + namespace ProjectExplorer { namespace Internal { class DeviceProcessListPrivate; } -class PROJECTEXPLORER_EXPORT DeviceProcessItem -{ -public: - bool operator<(const DeviceProcessItem &other) const; - - qint64 pid = 0; - QString cmdLine; - QString exe; -}; - class PROJECTEXPLORER_EXPORT DeviceProcessList : public QObject { Q_OBJECT @@ -58,11 +50,9 @@ public: void killProcess(int row); void setOwnPid(qint64 pid); - DeviceProcessItem at(int row) const; + Utils::ProcessInfo at(int row) const; QAbstractItemModel *model() const; - static QList<DeviceProcessItem> localProcesses(); - signals: void processListUpdated(); void error(const QString &errorMsg); @@ -71,13 +61,13 @@ signals: protected: void reportError(const QString &message); void reportProcessKilled(); - void reportProcessListUpdated(const QList<DeviceProcessItem> &processes); + void reportProcessListUpdated(const QList<Utils::ProcessInfo> &processes); IDevice::ConstPtr device() const; private: virtual void doUpdate() = 0; - virtual void doKillProcess(const DeviceProcessItem &process) = 0; + virtual void doKillProcess(const Utils::ProcessInfo &process) = 0; void setFinished(); diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp index 567cf5d41f5..94e97494478 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.cpp @@ -23,7 +23,6 @@ ** ****************************************************************************/ -#include "deviceprocess.h" #include "deviceusedportsgatherer.h" #include <ssh/sshconnection.h> @@ -31,6 +30,7 @@ #include <utils/port.h> #include <utils/portlist.h> #include <utils/qtcassert.h> +#include <utils/qtcprocess.h> #include <utils/url.h> #include <QPointer> @@ -44,7 +44,7 @@ namespace Internal { class DeviceUsedPortsGathererPrivate { public: - QPointer<DeviceProcess> process; + QPointer<QtcProcess> process; QList<Port> usedPorts; QByteArray remoteStdout; QByteArray remoteStderr; @@ -77,18 +77,17 @@ void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device) const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol; d->process = d->device->createProcess(this); - connect(d->process.data(), &DeviceProcess::finished, + connect(d->process.data(), &QtcProcess::finished, this, &DeviceUsedPortsGatherer::handleProcessFinished); - connect(d->process.data(), &DeviceProcess::errorOccurred, + connect(d->process.data(), &QtcProcess::errorOccurred, this, &DeviceUsedPortsGatherer::handleProcessError); - connect(d->process.data(), &DeviceProcess::readyReadStandardOutput, + connect(d->process.data(), &QtcProcess::readyReadStandardOutput, this, &DeviceUsedPortsGatherer::handleRemoteStdOut); - connect(d->process.data(), &DeviceProcess::readyReadStandardError, + connect(d->process.data(), &QtcProcess::readyReadStandardError, this, &DeviceUsedPortsGatherer::handleRemoteStdErr); - Runnable runnable; - runnable.command = d->portsGatheringMethod->commandLine(protocol); - d->process->start(runnable); + d->process->setCommand(d->portsGatheringMethod->commandLine(protocol)); + d->process->start(); } void DeviceUsedPortsGatherer::stop() diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 4977cafac41..70b61107635 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -45,6 +45,7 @@ #include <QStandardPaths> #include <QDateTime> +#include <QReadWriteLock> #include <QString> #include <QUuid> @@ -151,6 +152,7 @@ public: OsType osType = OsTypeOther; int version = 0; // This is used by devices that have been added by the SDK. + QReadWriteLock lock; // Currently used to protect sshParameters only QSsh::SshConnectionParameters sshParameters; PortList freePorts; FilePath debugServerPath; @@ -422,6 +424,13 @@ bool IDevice::setPermissions(const FilePath &filePath, QFile::Permissions) const return false; } +ProcessInterface *IDevice::createProcessInterface() const +{ +// TODO: uncomment below assert when docker device implements this method +// QTC_ASSERT(false, return nullptr); + return nullptr; +} + void IDevice::runProcess(QtcProcess &process) const { Q_UNUSED(process); @@ -581,7 +590,7 @@ OsType IDevice::osType() const return d->osType; } -DeviceProcess *IDevice::createProcess(QObject * /* parent */) const +QtcProcess *IDevice::createProcess(QObject * /* parent */) const { QTC_CHECK(false); return nullptr; @@ -629,6 +638,7 @@ void IDevice::fromMap(const QVariantMap &map) d->id = newId(); d->origin = static_cast<Origin>(map.value(QLatin1String(OriginKey), ManuallyAdded).toInt()); + QWriteLocker locker(&d->lock); d->sshParameters.setHost(map.value(QLatin1String(HostKey)).toString()); d->sshParameters.setPort(map.value(QLatin1String(SshPortKey), 22).toInt()); d->sshParameters.setUserName(map.value(QLatin1String(UserNameKey)).toString()); @@ -673,6 +683,7 @@ QVariantMap IDevice::toMap() const map.insert(QLatin1String(IdKey), d->id.toSetting()); map.insert(QLatin1String(OriginKey), d->origin); + QReadLocker locker(&d->lock); map.insert(QLatin1String(MachineTypeKey), d->machineType); map.insert(QLatin1String(HostKey), d->sshParameters.host()); map.insert(QLatin1String(SshPortKey), d->sshParameters.port()); @@ -723,11 +734,13 @@ QString IDevice::deviceStateToString() const QSsh::SshConnectionParameters IDevice::sshParameters() const { + QReadLocker locker(&d->lock); return d->sshParameters; } void IDevice::setSshParameters(const QSsh::SshConnectionParameters &sshParameters) { + QWriteLocker locker(&d->lock); d->sshParameters = sshParameters; } @@ -735,6 +748,7 @@ QUrl IDevice::toolControlChannel(const ControlChannelHint &) const { QUrl url; url.setScheme(Utils::urlTcpScheme()); + QReadLocker locker(&d->lock); url.setHost(d->sshParameters.host()); return url; } diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 9090500f493..9d724fbd150 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -54,12 +54,12 @@ class Environment; class Icon; class PortList; class Port; +class ProcessInterface; class QtcProcess; } // Utils namespace ProjectExplorer { -class DeviceProcess; class DeviceProcessList; class Kit; class Task; @@ -181,7 +181,7 @@ public: virtual DeviceTester *createDeviceTester() const; virtual bool canCreateProcess() const { return false; } - virtual DeviceProcess *createProcess(QObject *parent) const; + virtual Utils::QtcProcess *createProcess(QObject *parent) const; virtual DeviceProcessSignalOperation::Ptr signalOperation() const = 0; virtual DeviceEnvironmentFetcher::Ptr environmentFetcher() const; @@ -266,6 +266,7 @@ public: virtual QDateTime lastModified(const Utils::FilePath &filePath) const; virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const; virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const; + virtual Utils::ProcessInterface *createProcessInterface() const; virtual void runProcess(Utils::QtcProcess &process) const; virtual Utils::Environment systemEnvironment() const; virtual qint64 fileSize(const Utils::FilePath &filePath) const; diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp index 1579b2cc9fe..b6362cb01e2 100644 --- a/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp +++ b/src/plugins/projectexplorer/devicesupport/localprocesslist.cpp @@ -25,204 +25,42 @@ #include "localprocesslist.h" -#include <utils/qtcprocess.h> +#include <utils/processinfo.h> -#include <QLibrary> #include <QTimer> -#ifdef Q_OS_UNIX -#include <QDir> -#include <signal.h> -#include <errno.h> -#include <string.h> +#if defined(Q_OS_UNIX) #include <unistd.h> -#endif - -#ifdef Q_OS_WIN +#elif defined(Q_OS_WIN) #include <windows.h> -#include <utils/winutils.h> -#include <tlhelp32.h> -#include <psapi.h> #endif +using namespace Utils; + namespace ProjectExplorer { namespace Internal { -#ifdef Q_OS_WIN - -LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent) - : DeviceProcessList(device, parent) -{ - setOwnPid(GetCurrentProcessId()); -} - -QList<DeviceProcessItem> LocalProcessList::getLocalProcesses() -{ - QList<DeviceProcessItem> processes; - - PROCESSENTRY32 pe; - pe.dwSize = sizeof(PROCESSENTRY32); - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (snapshot == INVALID_HANDLE_VALUE) - return processes; - - for (bool hasNext = Process32First(snapshot, &pe); hasNext; hasNext = Process32Next(snapshot, &pe)) { - DeviceProcessItem p; - p.pid = pe.th32ProcessID; - // Image has the absolute path, but can fail. - const QString image = Utils::imageName(pe.th32ProcessID); - p.exe = p.cmdLine = image.isEmpty() ? - QString::fromWCharArray(pe.szExeFile) : - image; - processes << p; - } - CloseHandle(snapshot); - return processes; -} - -#endif //Q_OS_WIN - -#ifdef Q_OS_UNIX LocalProcessList::LocalProcessList(const IDevice::ConstPtr &device, QObject *parent) : DeviceProcessList(device, parent) { +#if defined(Q_OS_UNIX) setOwnPid(getpid()); +#elif defined(Q_OS_WIN) + setOwnPid(GetCurrentProcessId()); +#endif } -static bool isUnixProcessId(const QString &procname) -{ - for (int i = 0; i != procname.size(); ++i) - if (!procname.at(i).isDigit()) - return false; - return true; -} - -// Determine UNIX processes by reading "/proc". Default to ps if -// it does not exist - -static const char procDirC[] = "/proc/"; - -static QList<DeviceProcessItem> getLocalProcessesUsingProc(const QDir &procDir) -{ - QList<DeviceProcessItem> processes; - const QString procDirPath = QLatin1String(procDirC); - const QStringList procIds = procDir.entryList(); - foreach (const QString &procId, procIds) { - if (!isUnixProcessId(procId)) - continue; - DeviceProcessItem proc; - proc.pid = procId.toInt(); - const QString root = procDirPath + procId; - - QFile exeFile(root + QLatin1String("/exe")); - proc.exe = exeFile.symLinkTarget(); - - QFile cmdLineFile(root + QLatin1String("/cmdline")); - if (cmdLineFile.open(QIODevice::ReadOnly)) { // process may have exited - QList<QByteArray> tokens = cmdLineFile.readAll().split('\0'); - if (!tokens.isEmpty()) { - if (proc.exe.isEmpty()) - proc.exe = QString::fromLocal8Bit(tokens.front()); - foreach (const QByteArray &t, tokens) { - if (!proc.cmdLine.isEmpty()) - proc.cmdLine.append(QLatin1Char(' ')); - proc.cmdLine.append(QString::fromLocal8Bit(t)); - } - } - } - - if (proc.exe.isEmpty()) { - QFile statFile(root + QLatin1String("/stat")); - if (!statFile.open(QIODevice::ReadOnly)) { - const QStringList data = QString::fromLocal8Bit(statFile.readAll()).split(QLatin1Char(' ')); - if (data.size() < 2) - continue; - proc.exe = data.at(1); - proc.cmdLine = data.at(1); // PPID is element 3 - if (proc.exe.startsWith(QLatin1Char('(')) && proc.exe.endsWith(QLatin1Char(')'))) { - proc.exe.truncate(proc.exe.size() - 1); - proc.exe.remove(0, 1); - } - } - } - if (!proc.exe.isEmpty()) - processes.push_back(proc); - } - return processes; -} - -// Determine UNIX processes by running ps -static QMap<qint64, QString> getLocalProcessDataUsingPs(const QString &column) -{ - QMap<qint64, QString> result; - Utils::QtcProcess psProcess; - psProcess.setCommand({"ps", {"-e", "-o", "pid," + column}}); - psProcess.start(); - if (psProcess.waitForStarted()) { - QByteArray output; - if (psProcess.readDataFromProcess(30, &output, nullptr, false)) { - // Split "457 /Users/foo.app arg1 arg2" - const QStringList lines = QString::fromLocal8Bit(output).split(QLatin1Char('\n')); - const int lineCount = lines.size(); - const QChar blank = QLatin1Char(' '); - for (int l = 1; l < lineCount; l++) { // Skip header - const QString line = lines.at(l).trimmed(); - const int pidSep = line.indexOf(blank); - const qint64 pid = line.left(pidSep).toLongLong(); - result[pid] = line.mid(pidSep + 1); - } - } - } - return result; -} - -static QList<DeviceProcessItem> getLocalProcessesUsingPs() -{ - QList<DeviceProcessItem> processes; - - // cmdLines are full command lines, usually with absolute path, - // exeNames only the file part of the executable's path. - const QMap<qint64, QString> exeNames = getLocalProcessDataUsingPs("comm"); - const QMap<qint64, QString> cmdLines = getLocalProcessDataUsingPs("args"); - - for (auto it = exeNames.begin(), end = exeNames.end(); it != end; ++it) { - const qint64 pid = it.key(); - if (pid <= 0) - continue; - const QString cmdLine = cmdLines.value(pid); - if (cmdLines.isEmpty()) - continue; - const QString exeName = it.value(); - if (exeName.isEmpty()) - continue; - const int pos = cmdLine.indexOf(exeName); - if (pos == -1) - continue; - processes.append({pid, cmdLine, cmdLine.left(pos + exeName.size())}); - } - - return processes; -} - -QList<DeviceProcessItem> LocalProcessList::getLocalProcesses() -{ - const QDir procDir = QDir(QLatin1String(procDirC)); - return procDir.exists() ? getLocalProcessesUsingProc(procDir) : getLocalProcessesUsingPs(); -} - -#endif // QT_OS_UNIX - -void LocalProcessList::doKillProcess(const DeviceProcessItem &process) +void LocalProcessList::doKillProcess(const ProcessInfo &processInfo) { DeviceProcessSignalOperation::Ptr signalOperation = device()->signalOperation(); connect(signalOperation.data(), &DeviceProcessSignalOperation::finished, this, &LocalProcessList::reportDelayedKillStatus); - signalOperation->killProcess(process.pid); + signalOperation->killProcess(processInfo.processId); } void LocalProcessList::handleUpdate() { - reportProcessListUpdated(getLocalProcesses()); + reportProcessListUpdated(ProcessInfo::processInfoList()); } void LocalProcessList::doUpdate() diff --git a/src/plugins/projectexplorer/devicesupport/localprocesslist.h b/src/plugins/projectexplorer/devicesupport/localprocesslist.h index 8e100868495..01f06e06e74 100644 --- a/src/plugins/projectexplorer/devicesupport/localprocesslist.h +++ b/src/plugins/projectexplorer/devicesupport/localprocesslist.h @@ -37,11 +37,9 @@ class LocalProcessList : public DeviceProcessList public: explicit LocalProcessList(const IDevice::ConstPtr &device, QObject *parent = nullptr); - static QList<DeviceProcessItem> getLocalProcesses(); - private: void doUpdate() override; - void doKillProcess(const DeviceProcessItem &process) override; + void doKillProcess(const Utils::ProcessInfo &process) override; private: void handleUpdate(); diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp index bf265b0258f..689ee1b398c 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.cpp @@ -26,7 +26,6 @@ #include "sshdeviceprocess.h" #include "idevice.h" -#include "../runcontrol.h" #include <coreplugin/icore.h> #include <ssh/sshconnection.h> @@ -51,10 +50,11 @@ public: SshDeviceProcessPrivate(SshDeviceProcess *q) : q(q) {} SshDeviceProcess * const q; - bool ignoreSelfSignals = true; + QSharedPointer<const IDevice> m_device; QSsh::SshConnection *connection = nullptr; QSsh::SshRemoteProcessPtr remoteProcess; - Runnable runnable; + QString processName; + QString displayName; QString errorMessage; QProcess::ExitStatus exitStatus = QProcess::NormalExit; DeviceProcessSignalOperation::Ptr killOperation; @@ -63,47 +63,49 @@ public: void setState(State newState); void doSignal(Signal signal); - - QString displayName() const - { - return runnable.extraData.value("Ssh.X11ForwardToDisplay").toString(); - } }; SshDeviceProcess::SshDeviceProcess(const IDevice::ConstPtr &device, QObject *parent) - : DeviceProcess(device, QtcProcess::TerminalOn, parent), + : QtcProcess(parent), d(std::make_unique<SshDeviceProcessPrivate>(this)) { - // Hack: we rely on fact that below slots were called before any other external slots connected - // to this instance signals. That's why we don't re-emit them from inside our handlers since - // these signal will reach all other external slots anyway after our handlers are done. - connect(this, &QtcProcess::started, this, [this] { - if (!d->ignoreSelfSignals) - handleProcessStarted(); - }); - connect(this, &QtcProcess::finished, this, [this] { - if (!d->ignoreSelfSignals) - handleProcessFinished(QtcProcess::errorString()); - }); + d->m_device = device; connect(&d->killTimer, &QTimer::timeout, this, &SshDeviceProcess::handleKillOperationTimeout); } +void SshDeviceProcess::emitStarted() +{ + handleProcessStarted(); +} + +void SshDeviceProcess::emitFinished() +{ + handleProcessFinished(QtcProcess::errorString()); +} + +const QSharedPointer<const IDevice> &SshDeviceProcess::device() const +{ + return d->m_device; +} + SshDeviceProcess::~SshDeviceProcess() { d->setState(SshDeviceProcessPrivate::Inactive); } -void SshDeviceProcess::start(const Runnable &runnable) +void SshDeviceProcess::start() { QTC_ASSERT(d->state == SshDeviceProcessPrivate::Inactive, return); - QTC_ASSERT(runInTerminal() || !runnable.command.isEmpty(), return); + QTC_ASSERT(usesTerminal() || !commandLine().isEmpty(), return); d->setState(SshDeviceProcessPrivate::Connecting); d->errorMessage.clear(); d->exitStatus = QProcess::NormalExit; - d->runnable = runnable; - QSsh::SshConnectionParameters params = device()->sshParameters(); - params.x11DisplayName = d->displayName(); + d->processName = commandLine().executable().toString(); + d->displayName = extraData("Ssh.X11ForwardToDisplay").toString(); + + QSsh::SshConnectionParameters params = d->m_device->sshParameters(); + params.x11DisplayName = d->displayName; d->connection = QSsh::SshConnectionManager::acquireConnection(params); connect(d->connection, &QSsh::SshConnection::errorOccurred, this, &SshDeviceProcess::handleConnectionError); @@ -161,7 +163,7 @@ QProcess::ExitStatus SshDeviceProcess::exitStatus() const int SshDeviceProcess::exitCode() const { - return runInTerminal() ? QtcProcess::exitCode() : d->remoteProcess->exitCode(); + return usesTerminal() ? QtcProcess::exitCode() : d->remoteProcess->exitCode(); } QString SshDeviceProcess::errorString() const @@ -179,32 +181,27 @@ QByteArray SshDeviceProcess::readAllStandardError() return d->remoteProcess.get() ? d->remoteProcess->readAllStandardError() : QByteArray(); } -qint64 SshDeviceProcess::processId() const -{ - return 0; -} - void SshDeviceProcess::handleConnected() { QTC_ASSERT(d->state == SshDeviceProcessPrivate::Connecting, return); d->setState(SshDeviceProcessPrivate::Connected); - d->remoteProcess = runInTerminal() && d->runnable.command.isEmpty() + d->remoteProcess = usesTerminal() && d->processName.isEmpty() ? d->connection->createRemoteShell() - : d->connection->createRemoteProcess(fullCommandLine(d->runnable)); - const QString display = d->displayName(); + : d->connection->createRemoteProcess(fullCommandLine()); + const QString display = d->displayName; if (!display.isEmpty()) d->remoteProcess->requestX11Forwarding(display); - d->ignoreSelfSignals = !runInTerminal(); - if (runInTerminal()) { + if (usesTerminal()) { setAbortOnMetaChars(false); setCommand(d->remoteProcess->fullLocalCommandLine(true)); QtcProcess::start(); } else { connect(d->remoteProcess.get(), &QSsh::SshRemoteProcess::started, this, &SshDeviceProcess::handleProcessStarted); - connect(d->remoteProcess.get(), &QSsh::SshRemoteProcess::done, - this, &SshDeviceProcess::handleProcessFinished); + connect(d->remoteProcess.get(), &QSsh::SshRemoteProcess::finished, this, [this] { + handleProcessFinished(d->remoteProcess->errorString()); + }); connect(d->remoteProcess.get(), &QSsh::SshRemoteProcess::readyReadStandardOutput, this, &QtcProcess::readyReadStandardOutput); connect(d->remoteProcess.get(), &QSsh::SshRemoteProcess::readyReadStandardError, @@ -244,8 +241,7 @@ void SshDeviceProcess::handleProcessStarted() QTC_ASSERT(d->state == SshDeviceProcessPrivate::Connected, return); d->setState(SshDeviceProcessPrivate::ProcessRunning); - if (d->ignoreSelfSignals) - emit started(); + emit started(); } void SshDeviceProcess::handleProcessFinished(const QString &error) @@ -254,8 +250,7 @@ void SshDeviceProcess::handleProcessFinished(const QString &error) if (d->killOperation && error.isEmpty()) d->errorMessage = tr("The process was ended forcefully."); d->setState(SshDeviceProcessPrivate::Inactive); - if (d->ignoreSelfSignals) - emit finished(); + emit finished(); } void SshDeviceProcess::handleKillOperationFinished(const QString &errorMessage) @@ -278,18 +273,14 @@ void SshDeviceProcess::handleKillOperationTimeout() emit finished(); } -QString SshDeviceProcess::fullCommandLine(const Runnable &runnable) const +QString SshDeviceProcess::fullCommandLine() const { - QString cmdLine = runnable.command.executable().toString(); - // FIXME: That quotes wrongly. - if (!runnable.command.arguments().isEmpty()) - cmdLine.append(QLatin1Char(' ')).append(runnable.command.arguments()); - return cmdLine; + return commandLine().toUserOutput(); } void SshDeviceProcess::SshDeviceProcessPrivate::doSignal(Signal signal) { - if (runnable.command.isEmpty()) + if (processName.isEmpty()) return; switch (state) { case SshDeviceProcessPrivate::Inactive: @@ -302,13 +293,13 @@ void SshDeviceProcess::SshDeviceProcessPrivate::doSignal(Signal signal) break; case SshDeviceProcessPrivate::Connected: case SshDeviceProcessPrivate::ProcessRunning: - DeviceProcessSignalOperation::Ptr signalOperation = q->device()->signalOperation(); + DeviceProcessSignalOperation::Ptr signalOperation = m_device->signalOperation(); const qint64 processId = q->processId(); if (signal == Signal::Interrupt) { if (processId != 0) signalOperation->interruptProcess(processId); else - signalOperation->interruptProcess(runnable.command.executable().toString()); + signalOperation->interruptProcess(processName); } else { if (killOperation) // We are already in the process of killing the app. return; @@ -319,7 +310,7 @@ void SshDeviceProcess::SshDeviceProcessPrivate::doSignal(Signal signal) if (processId != 0) signalOperation->killProcess(processId); else - signalOperation->killProcess(runnable.command.executable().toString()); + signalOperation->killProcess(processName); } break; } @@ -337,7 +328,7 @@ void SshDeviceProcess::SshDeviceProcessPrivate::setState(SshDeviceProcess::SshDe if (killOperation) { killOperation->disconnect(q); killOperation.clear(); - if (q->runInTerminal()) + if (q->usesTerminal()) QMetaObject::invokeMethod(q, &QtcProcess::stopProcess, Qt::QueuedConnection); } killTimer.stop(); @@ -352,7 +343,7 @@ void SshDeviceProcess::SshDeviceProcessPrivate::setState(SshDeviceProcess::SshDe qint64 SshDeviceProcess::write(const QByteArray &data) { - QTC_ASSERT(!runInTerminal(), return -1); + QTC_ASSERT(!usesTerminal(), return -1); return d->remoteProcess->write(data); } diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h index 0980d530bcf..ebf6b53397d 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocess.h @@ -25,22 +25,24 @@ #pragma once -#include "deviceprocess.h" +#include "../projectexplorer_export.h" + +#include <utils/qtcprocess.h> #include <memory> namespace ProjectExplorer { -class Runnable; +class IDevice; -class PROJECTEXPLORER_EXPORT SshDeviceProcess : public DeviceProcess +class PROJECTEXPLORER_EXPORT SshDeviceProcess : public Utils::QtcProcess { Q_OBJECT public: explicit SshDeviceProcess(const QSharedPointer<const IDevice> &device, QObject *parent = nullptr); ~SshDeviceProcess() override; - void start(const Runnable &runnable) override; + void start() override; void interrupt() override; void terminate() override; void kill() override; @@ -55,6 +57,12 @@ public: qint64 write(const QByteArray &data) override; +protected: + void emitStarted() override; + void emitFinished() override; + + const QSharedPointer<const IDevice> &device() const; + private: void handleConnected(); void handleConnectionError(); @@ -64,8 +72,7 @@ private: void handleKillOperationFinished(const QString &errorMessage); void handleKillOperationTimeout(); - virtual QString fullCommandLine(const Runnable &runnable) const; - virtual qint64 processId() const; + virtual QString fullCommandLine() const; class SshDeviceProcessPrivate; friend class SshDeviceProcessPrivate; diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp index 32d58dc5dce..5e5084aa172 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.cpp @@ -28,10 +28,12 @@ #include "idevice.h" #include <ssh/sshremoteprocessrunner.h> -#include <utils/qtcassert.h> #include <utils/fileutils.h> +#include <utils/processinfo.h> +#include <utils/qtcassert.h> using namespace QSsh; +using namespace Utils; namespace ProjectExplorer { @@ -53,18 +55,18 @@ void SshDeviceProcessList::doUpdate() { connect(&d->process, &SshRemoteProcessRunner::connectionError, this, &SshDeviceProcessList::handleConnectionError); - connect(&d->process, &SshRemoteProcessRunner::processClosed, + connect(&d->process, &SshRemoteProcessRunner::finished, this, &SshDeviceProcessList::handleListProcessFinished); d->process.run(listProcessesCommandLine(), device()->sshParameters()); } -void SshDeviceProcessList::doKillProcess(const DeviceProcessItem &process) +void SshDeviceProcessList::doKillProcess(const ProcessInfo &process) { d->signalOperation = device()->signalOperation(); QTC_ASSERT(d->signalOperation, return); connect(d->signalOperation.data(), &DeviceProcessSignalOperation::finished, this, &SshDeviceProcessList::handleKillProcessFinished); - d->signalOperation->killProcess(process.pid); + d->signalOperation->killProcess(process.processId); } void SshDeviceProcessList::handleConnectionError() @@ -73,21 +75,22 @@ void SshDeviceProcessList::handleConnectionError() reportError(tr("Connection failure: %1").arg(d->process.lastConnectionErrorString())); } -void SshDeviceProcessList::handleListProcessFinished(const QString &error) +void SshDeviceProcessList::handleListProcessFinished() { + const QString error = d->process.errorString(); setFinished(); if (!error.isEmpty()) { handleProcessError(error); return; } - if (d->process.processExitCode() == 0) { + if (d->process.exitCode() == 0) { const QByteArray remoteStdout = d->process.readAllStandardOutput(); const QString stdoutString = QString::fromUtf8(remoteStdout.data(), remoteStdout.count()); reportProcessListUpdated(buildProcessList(stdoutString)); } else { handleProcessError(tr("Process listing command failed with exit code %1.") - .arg(d->process.processExitCode())); + .arg(d->process.exitCode())); } } diff --git a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h index 60937e65cc0..513583f0e7f 100644 --- a/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h +++ b/src/plugins/projectexplorer/devicesupport/sshdeviceprocesslist.h @@ -40,14 +40,14 @@ public: private: void handleConnectionError(); - void handleListProcessFinished(const QString &error); + void handleListProcessFinished(); void handleKillProcessFinished(const QString &errorString); virtual QString listProcessesCommandLine() const = 0; - virtual QList<DeviceProcessItem> buildProcessList(const QString &listProcessesReply) const = 0; + virtual QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const = 0; void doUpdate() override; - void doKillProcess(const DeviceProcessItem &process) override; + void doKillProcess(const Utils::ProcessInfo &process) override; void handleProcessError(const QString &errorMessage); void setFinished(); diff --git a/src/plugins/projectexplorer/editorconfiguration.cpp b/src/plugins/projectexplorer/editorconfiguration.cpp index 286e8cf201d..760e08b6b91 100644 --- a/src/plugins/projectexplorer/editorconfiguration.cpp +++ b/src/plugins/projectexplorer/editorconfiguration.cpp @@ -407,12 +407,12 @@ void EditorConfiguration::slotAboutToRemoveProject(Project *project) deconfigureEditor(editor); } -TabSettings actualTabSettings(const QString &fileName, +TabSettings actualTabSettings(const Utils::FilePath &file, const TextDocument *baseTextdocument) { if (baseTextdocument) return baseTextdocument->tabSettings(); - if (Project *project = SessionManager::projectForFile(Utils::FilePath::fromString(fileName))) + if (Project *project = SessionManager::projectForFile(file)) return project->editorConfiguration()->codeStyle()->tabSettings(); return TextEditorSettings::codeStyle()->tabSettings(); } diff --git a/src/plugins/projectexplorer/editorconfiguration.h b/src/plugins/projectexplorer/editorconfiguration.h index fb9561d90a4..fef5e68466c 100644 --- a/src/plugins/projectexplorer/editorconfiguration.h +++ b/src/plugins/projectexplorer/editorconfiguration.h @@ -49,7 +49,9 @@ class StorageSettings; class BehaviorSettings; class ExtraEncodingSettings; class MarginSettings; -} +} // namespace TextEditor + +namespace Utils { class FilePath; } namespace ProjectExplorer { @@ -118,6 +120,6 @@ private: // the file belongs to and return the project settings. If the file doesn't belong to any // project return the global settings. PROJECTEXPLORER_EXPORT TextEditor::TabSettings actualTabSettings( - const QString &fileName, const TextEditor::TextDocument *baseTextDocument); + const Utils::FilePath &file, const TextEditor::TextDocument *baseTextDocument); } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index ad3ab0133f1..f7313eaad14 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -146,7 +146,7 @@ static QByteArray runGcc(const FilePath &gcc, const QStringList &arguments, cons cpp.setTimeoutS(10); cpp.setCommand({gcc, arguments}); cpp.runBlocking(); - if (cpp.result() != QtcProcess::FinishedWithSuccess || cpp.exitCode() != 0) { + if (cpp.result() != ProcessResult::FinishedWithSuccess || cpp.exitCode() != 0) { Core::MessageManager::writeFlashing({"Compiler feature detection failure!", cpp.exitMessage(), QString::fromUtf8(cpp.allRawOutput())}); @@ -1194,10 +1194,16 @@ Toolchains GccToolChainFactory::autoDetectToolchains( || compilerPath.toString().contains("ccache")) { existingTcMatches = existingCommand == compilerPath; } else { - existingTcMatches = Environment::systemEnvironment().isSameExecutable( - existingCommand.toString(), compilerPath.toString()) - || (HostOsInfo::isWindowsHost() && existingCommand.toFileInfo().size() - == compilerPath.toFileInfo().size()); + existingTcMatches = Environment::systemEnvironment() + .isSameExecutable(existingCommand.toString(), + compilerPath.toString()); + if (!existingTcMatches + && HostOsInfo::isWindowsHost() + && !existingCommand.needsDevice() + && !compilerPath.needsDevice()) { + existingTcMatches = existingCommand.toFileInfo().size() + == compilerPath.toFileInfo().size(); + } } if (existingTcMatches) { if (existingTc->typeId() == requiredTypeId && (!checker || checker(existingTc)) diff --git a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp index 75c2daf7164..8a34a1bb221 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonkitspage.cpp @@ -35,7 +35,7 @@ #include <utils/algorithm.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> using namespace Core; diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp index 705e51aa66d..9ed80f77c27 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp @@ -247,18 +247,8 @@ QVariant JsonWizardFactory::mergeDataValueMaps(const QVariant &valueMap, const Q { QVariantMap retVal; -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - const QVariantMap &map = defaultValueMap.toMap(); - for (auto it = map.begin(), end = map.end(); it != end; ++it) - retVal.insert(it.key(), it.value()); - - const QVariantMap &map2 = valueMap.toMap(); - for (auto it = map2.begin(), end = map2.end(); it != end; ++it) - retVal.insert(it.key(), it.value()); -#else retVal.insert(defaultValueMap.toMap()); retVal.insert(valueMap.toMap()); -#endif return retVal; } diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp index a49b01fd48a..999ba78a169 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp @@ -42,9 +42,9 @@ #include <texteditor/textindenter.h> #include <utils/algorithm.h> -#include <utils/mimetypes/mimedatabase.h> -#include <utils/stringutils.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> +#include <utils/stringutils.h> #include <QCoreApplication> #include <QDebug> diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp index 51ffd37b91c..6fa6e42298a 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp @@ -34,9 +34,9 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> -#include <utils/qtcassert.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> +#include <utils/qtcassert.h> #include <QCoreApplication> #include <QDir> diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp index df9a21660fb..a6912b3c484 100644 --- a/src/plugins/projectexplorer/msvcparser.cpp +++ b/src/plugins/projectexplorer/msvcparser.cpp @@ -78,7 +78,9 @@ static Task handleNmakeJomMessage(const QString &line) if (!matchLength) return {}; - return CompileTask(Task::Error, line.mid(matchLength).trimmed()); + CompileTask task(Task::Error, line.mid(matchLength).trimmed()); + task.details << line; + return task; } static Task::TaskType taskType(const QString &category) @@ -139,6 +141,7 @@ OutputLineParser::Result MsvcParser::handleLine(const QString &line, OutputForma LinkSpecs linkSpecs; addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match, 2); m_lastTask = CompileTask(Task::Unknown, description, filePath, lineNo); + m_lastTask.details << line; m_lines = 1; return {Status::InProgress, linkSpecs}; } @@ -173,17 +176,16 @@ MsvcParser::Result MsvcParser::processCompileLine(const QString &line) [](int total, const QString &line) { return total + line.length() + 1;}); for (LinkSpec &ls : linkSpecs) ls.startPos += offset; - m_linkSpecs << linkSpecs; - m_lastTask.details.append(line); ++m_lines; } else { flush(); m_lastTask = CompileTask(taskType(match.captured(2)), match.captured(3) + match.captured(4).trimmed(), // description filePath, position.second); - m_linkSpecs << linkSpecs; m_lines = 1; } + m_linkSpecs << linkSpecs; + m_lastTask.details.append(line); return {Status::InProgress, linkSpecs}; } @@ -196,6 +198,8 @@ void MsvcParser::flush() if (m_lastTask.isNull()) return; + if (m_lastTask.details.count() == 1) + m_lastTask.details.clear(); setDetailsFormat(m_lastTask, m_linkSpecs); Task t = m_lastTask; m_lastTask.clear(); @@ -476,6 +480,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << (Tasks() << compileTask(Task::Error, "C2440: 'initializing' : cannot convert from 'int' to 'std::_Tree<_Traits>::iterator'\n" + "..\\untitled\\main.cpp(19) : error C2440: 'initializing' : cannot convert from 'int' to 'std::_Tree<_Traits>::iterator'\n" " with\n" " [\n" " _Traits=std::_Tmap_traits<int,double,std::less<int>,std::allocator<std::pair<const int,double>>,false>\n" @@ -484,7 +489,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() FilePath::fromUserInput("..\\untitled\\main.cpp"), 19, QVector<QTextLayout::FormatRange>() - << formatRange(85, 247))) + << formatRange(85, 365))) << ""; QTest::newRow("Linker error 1") @@ -545,6 +550,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() FilePath::fromUserInput("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility"), 2212) << compileTask(Task::Unknown, "see reference to function template instantiation '_OutIt std::copy<const unsigned char*,unsigned short*>(_InIt,_InIt,_OutIt)' being compiled\n" + " symbolgroupvalue.cpp(2314) : see reference to function template instantiation '_OutIt std::copy<const unsigned char*,unsigned short*>(_InIt,_InIt,_OutIt)' being compiled\n" " with\n" " [\n" " _OutIt=unsigned short *,\n" @@ -553,7 +559,7 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() FilePath::fromUserInput("symbolgroupvalue.cpp"), 2314, QVector<QTextLayout::FormatRange>() - << formatRange(141, 109))) + << formatRange(141, 287))) << ""; QTest::newRow("Ambiguous symbol") @@ -588,11 +594,12 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() << "" << "" << Tasks{compileTask(Task::Error, "C2733: 'func': second C linkage of overloaded function not allowed\n" + "main.cpp(7): error C2733: 'func': second C linkage of overloaded function not allowed\n" "main.cpp(6): note: see declaration of 'func'", FilePath::fromUserInput("main.cpp"), 7, QVector<QTextLayout::FormatRange>() - << formatRange(67, 44))} + << formatRange(67, 130))} << ""; QTest::newRow("cyrillic warning") // QTCREATORBUG-20297 diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 13a945a2807..3f0bfb54daf 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -265,21 +265,21 @@ static QVector<VisualStudioInstallation> detectVisualStudioFromVsWhere(const QSt {"-products", "*", "-prerelease", "-legacy", "-format", "json", "-utf8"}}); vsWhereProcess.runBlocking(); switch (vsWhereProcess.result()) { - case QtcProcess::FinishedWithSuccess: + case ProcessResult::FinishedWithSuccess: break; - case QtcProcess::StartFailed: + case ProcessResult::StartFailed: qWarning().noquote() << QDir::toNativeSeparators(vswhere) << "could not be started."; return installations; - case QtcProcess::FinishedWithError: + case ProcessResult::FinishedWithError: qWarning().noquote().nospace() << QDir::toNativeSeparators(vswhere) << " finished with exit code " << vsWhereProcess.exitCode() << "."; return installations; - case QtcProcess::TerminatedAbnormally: + case ProcessResult::TerminatedAbnormally: qWarning().noquote().nospace() << QDir::toNativeSeparators(vswhere) << " crashed. Exit code: " << vsWhereProcess.exitCode(); return installations; - case QtcProcess::Hang: + case ProcessResult::Hang: qWarning().noquote() << QDir::toNativeSeparators(vswhere) << "did not finish in" << timeoutS << "seconds."; return installations; @@ -652,7 +652,7 @@ Macros MsvcToolChain::msvcPredefinedMacros(const QStringList &cxxflags, arguments << toProcess << QLatin1String("/EP") << saver.filePath().toUserOutput(); cpp.setCommand({binary, arguments}); cpp.runBlocking(); - if (cpp.result() != QtcProcess::FinishedWithSuccess) + if (cpp.result() != ProcessResult::FinishedWithSuccess) return predefinedMacros; const QStringList output = Utils::filtered(cpp.stdOut().split('\n'), @@ -901,34 +901,18 @@ QStringList MsvcToolChain::suggestedMkspecList() const case Abi::WindowsMsvc2013Flavor: return {"win32-msvc", "win32-msvc2013", - "winphone-arm-msvc2013", - "winphone-x86-msvc2013", - "winrt-arm-msvc2013", - "winrt-x86-msvc2013", - "winrt-x64-msvc2013", "win32-msvc2012", "win32-msvc2010"}; case Abi::WindowsMsvc2015Flavor: return {"win32-msvc", - "win32-msvc2015", - "winphone-arm-msvc2015", - "winphone-x86-msvc2015", - "winrt-arm-msvc2015", - "winrt-x86-msvc2015", - "winrt-x64-msvc2015"}; + "win32-msvc2015"}; case Abi::WindowsMsvc2017Flavor: return {"win32-msvc", - "win32-msvc2017", - "winrt-arm-msvc2017", - "winrt-x86-msvc2017", - "winrt-x64-msvc2017"}; + "win32-msvc2017",}; case Abi::WindowsMsvc2019Flavor: return {"win32-msvc", "win32-msvc2019", - "win32-arm64-msvc", - "winrt-arm-msvc2019", - "winrt-x86-msvc2019", - "winrt-x64-msvc2019"}; + "win32-arm64-msvc"}; case Abi::WindowsMsvc2022Flavor: return {"win32-msvc", "win32-msvc2022", @@ -1555,7 +1539,7 @@ static QVersionNumber clangClVersion(const FilePath &clangClPath) QtcProcess clangClProcess; clangClProcess.setCommand({clangClPath, {"--version"}}); clangClProcess.runBlocking(); - if (clangClProcess.result() != QtcProcess::FinishedWithSuccess) + if (clangClProcess.result() != ProcessResult::FinishedWithSuccess) return {}; const QRegularExpressionMatch match = QRegularExpression( QStringLiteral("clang version (\\d+(\\.\\d+)+)")) @@ -1780,7 +1764,7 @@ Macros ClangClToolChain::msvcPredefinedMacros(const QStringList &cxxflags, arguments.append("-"); cpp.setCommand({compilerCommand(), arguments}); cpp.runBlocking(); - if (cpp.result() != Utils::QtcProcess::FinishedWithSuccess) { + if (cpp.result() != ProcessResult::FinishedWithSuccess) { // Show the warning but still parse the output. QTC_CHECK(false && "clang-cl exited with non-zero code."); } @@ -2128,7 +2112,7 @@ Utils::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils: run.setCommand(cmd); run.runBlocking(); - if (run.result() != QtcProcess::FinishedWithSuccess) { + if (run.result() != ProcessResult::FinishedWithSuccess) { const QString message = !run.stdErr().isEmpty() ? run.stdErr() : run.exitMessage(); qWarning().noquote() << message; QString command = QDir::toNativeSeparators(batchFile); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 00c2ba035f3..791de917d59 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -139,13 +139,14 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/macroexpander.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/parameteraction.h> #include <utils/processhandle.h> #include <utils/proxyaction.h> #include <utils/qtcassert.h> #include <utils/removefiledialog.h> #include <utils/stringutils.h> +#include <utils/tooltip/tooltip.h> #include <utils/utilsicons.h> #include <QAction> @@ -418,6 +419,42 @@ protected: void restoreState(const QJsonObject &object) override; }; +class RunConfigurationLocatorFilter : public Core::ILocatorFilter +{ +public: + RunConfigurationLocatorFilter(); + + void prepareSearch(const QString &entry) override; + QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, + const QString &entry) override; + +private: + void targetListUpdated(); + QList<Core::LocatorFilterEntry> m_result; +}; + +class RunRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter +{ +public: + RunRunConfigurationLocatorFilter(); + + void accept(const Core::LocatorFilterEntry &selection, + QString *newText, + int *selectionStart, + int *selectionLength) const final; +}; + +class SwitchToRunConfigurationLocatorFilter final : public RunConfigurationLocatorFilter +{ +public: + SwitchToRunConfigurationLocatorFilter(); + + void accept(const Core::LocatorFilterEntry &selection, + QString *newText, + int *selectionStart, + int *selectionLength) const final; +}; + class ProjectExplorerPluginPrivate : public QObject { Q_DECLARE_TR_FUNCTIONS(ProjectExplorer::ProjectExplorerPlugin) @@ -652,6 +689,8 @@ public: AllProjectsFilter m_allProjectsFilter; CurrentProjectFilter m_currentProjectFilter; AllProjectFilesFilter m_allProjectDirectoriesFilter; + RunRunConfigurationLocatorFilter m_runConfigurationLocatorFilter; + SwitchToRunConfigurationLocatorFilter m_switchRunConfigurationLocatorFilter; ProcessStepFactory m_processStepFactory; @@ -1680,7 +1719,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er if (tmp < 0 || tmp > int(StopBeforeBuild::SameApp)) tmp = int(defaultSettings.stopBeforeBuild); dd->m_projectExplorerSettings.stopBeforeBuild = StopBeforeBuild(tmp); - dd->m_projectExplorerSettings.terminalMode = static_cast<TerminalMode>( + dd->m_projectExplorerSettings.terminalMode = static_cast<Internal::TerminalMode>( s->value(Constants::TERMINAL_MODE_SETTINGS_KEY, int(defaultSettings.terminalMode)).toInt()); dd->m_projectExplorerSettings.closeSourceFilesWithProject = s->value(Constants::CLOSE_FILES_WITH_PROJECT_SETTINGS_KEY, @@ -4370,4 +4409,106 @@ void AllProjectFilesFilter::restoreState(const QJsonObject &object) DirectoryFilter::restoreState(withoutDirectories); } +RunConfigurationLocatorFilter::RunConfigurationLocatorFilter() +{ + connect(SessionManager::instance(), &SessionManager::startupProjectChanged, + this, &RunConfigurationLocatorFilter::targetListUpdated); + + targetListUpdated(); +} + +void RunConfigurationLocatorFilter::prepareSearch(const QString &entry) +{ + m_result.clear(); + const Target *target = SessionManager::startupTarget(); + if (!target) + return; + for (auto rc : target->runConfigurations()) { + if (rc->displayName().contains(entry, Qt::CaseInsensitive)) { + Core::LocatorFilterEntry filterEntry(this, rc->displayName(), {}); + m_result.append(filterEntry); + } + } +} + +QList<Core::LocatorFilterEntry> RunConfigurationLocatorFilter::matchesFor( + QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) +{ + Q_UNUSED(future) + Q_UNUSED(entry) + return m_result; +} + +void RunConfigurationLocatorFilter::targetListUpdated() +{ + setEnabled(SessionManager::startupProject()); // at least one project opened +} + +static RunConfiguration *runConfigurationForDisplayName(const QString &displayName) +{ + const Project *project = SessionManager::instance()->startupProject(); + if (!project) + return nullptr; + const QList<RunConfiguration *> runconfigs = project->activeTarget()->runConfigurations(); + return Utils::findOrDefault(runconfigs, [displayName](RunConfiguration *rc) { + return rc->displayName() == displayName; + }); +} + +RunRunConfigurationLocatorFilter::RunRunConfigurationLocatorFilter() +{ + setId("Run run configuration"); + setDisplayName(ProjectExplorerPluginPrivate::tr("Run run configuration")); + setDescription(ProjectExplorerPluginPrivate::tr("Run a run configuration of the current " + "active project")); + setDefaultShortcutString("rr"); + setPriority(Medium); +} + +void RunRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection, QString *newText, + int *selectionStart, int *selectionLength) const +{ + Q_UNUSED(newText) + Q_UNUSED(selectionStart) + Q_UNUSED(selectionLength) + + RunConfiguration *toStart = runConfigurationForDisplayName(selection.displayName); + if (!toStart) + return; + if (!BuildManager::isBuilding(toStart->project())) + ProjectExplorerPlugin::runRunConfiguration(toStart, Constants::NORMAL_RUN_MODE, true); +} + +SwitchToRunConfigurationLocatorFilter::SwitchToRunConfigurationLocatorFilter() +{ + setId("Switch run configuration"); + setDisplayName(ProjectExplorerPluginPrivate::tr("Switch run configuration")); + setDescription(ProjectExplorerPluginPrivate::tr("Switch active run configuration")); + setDefaultShortcutString("sr"); + setPriority(Medium); +} + +void SwitchToRunConfigurationLocatorFilter::accept(const LocatorFilterEntry &selection, + QString *newText, int *selectionStart, + int *selectionLength) const +{ + Q_UNUSED(newText) + Q_UNUSED(selectionStart) + Q_UNUSED(selectionLength) + + RunConfiguration *toSwitchTo = runConfigurationForDisplayName(selection.displayName); + if (!toSwitchTo) + return; + + SessionManager::startupTarget()->setActiveRunConfiguration(toSwitchTo); + QTimer::singleShot(200, this, [displayName = selection.displayName](){ + if (auto ks = ICore::mainWindow()->findChild<QWidget *>("KitSelector.Button")) { + Utils::ToolTip::show(ks->mapToGlobal(QPoint{25, 25}), + ProjectExplorerPluginPrivate::tr( + "Switched run configuration to\n%1").arg(displayName), + ICore::dialogParent()); + } + }); +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 69baa7acc75..1afca928404 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -211,7 +211,6 @@ Project { "devicefactoryselectiondialog.cpp", "devicefactoryselectiondialog.h", "devicefactoryselectiondialog.ui", "devicemanager.cpp", "devicemanager.h", "devicemanagermodel.cpp", "devicemanagermodel.h", - "deviceprocess.cpp", "deviceprocess.h", "deviceprocessesdialog.cpp", "deviceprocessesdialog.h", "deviceprocesslist.cpp", "deviceprocesslist.h", "devicesettingspage.cpp", "devicesettingspage.h", @@ -221,7 +220,6 @@ Project { "idevice.cpp", "idevice.h", "idevicefactory.cpp", "idevicefactory.h", "idevicewidget.h", - "desktopdeviceprocess.cpp", "desktopdeviceprocess.h", "localprocesslist.cpp", "localprocesslist.h", "sshdeviceprocess.cpp", "sshdeviceprocess.h", "sshdeviceprocesslist.cpp", "sshdeviceprocesslist.h", diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp index 52b20b3ad08..fc3fb6ec5f8 100644 --- a/src/plugins/projectexplorer/projectfilewizardextension.cpp +++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp @@ -44,7 +44,7 @@ #include <texteditor/tabsettings.h> #include <texteditor/texteditorsettings.h> #include <texteditor/textindenter.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QPointer> #include <QDebug> diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 7b6866f92a6..3915a733ec1 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -39,8 +39,7 @@ #include <utils/fileutils.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> -#include <utils/mimetypes/mimetype.h> +#include <utils/mimeutils.h> #include <utils/pointeralgorithm.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 883760dfda0..1ea65831037 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1188,13 +1188,16 @@ SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl) void SimpleTargetRunner::start() { - if (m_starter) + if (m_starter) { m_starter(); - else - doStart(runControl()->runnable(), runControl()->device()); + } else { + Runnable runnable = runControl()->runnable(); + runnable.device = runControl()->device(); + doStart(runnable); + } } -void SimpleTargetRunner::doStart(const Runnable &runnable, const IDevice::ConstPtr &device) +void SimpleTargetRunner::doStart(const Runnable &runnable) { m_stopForced = false; m_stopReported = false; @@ -1202,23 +1205,21 @@ void SimpleTargetRunner::doStart(const Runnable &runnable, const IDevice::ConstP m_launcher.setUseTerminal(m_useTerminal); m_launcher.setRunAsRoot(m_runAsRoot); - const bool isDesktop = device.isNull() || device.dynamicCast<const DesktopDevice>(); const QString msg = RunControl::tr("Starting %1...").arg(runnable.command.toUserOutput()); appendMessage(msg, Utils::NormalMessageFormat); - connect(&m_launcher, &ApplicationLauncher::processExited, - this, [this, runnable](int exitCode, QProcess::ExitStatus status) { + connect(&m_launcher, &ApplicationLauncher::finished, this, [this, runnable]() { if (m_stopReported) return; - const QString msg = (status == QProcess::CrashExit) - ? tr("%1 crashed.") : tr("%2 exited with code %1").arg(exitCode); + const QString msg = (m_launcher.exitStatus() == QProcess::CrashExit) + ? tr("%1 crashed.") : tr("%2 exited with code %1").arg(m_launcher.exitCode()); const QString displayName = runnable.command.executable().toUserOutput(); appendMessage(msg.arg(displayName), Utils::NormalMessageFormat); m_stopReported = true; reportStopped(); }); - connect(&m_launcher, &ApplicationLauncher::error, + connect(&m_launcher, &ApplicationLauncher::errorOccurred, this, [this, runnable](QProcess::ProcessError error) { if (m_stopReported) return; @@ -1233,8 +1234,10 @@ void SimpleTargetRunner::doStart(const Runnable &runnable, const IDevice::ConstP connect(&m_launcher, &ApplicationLauncher::appendMessage, this, &RunWorker::appendMessage); + const bool isDesktop = runnable.device.isNull() + || runnable.device.dynamicCast<const DesktopDevice>(); if (isDesktop) { - connect(&m_launcher, &ApplicationLauncher::processStarted, this, [this] { + connect(&m_launcher, &ApplicationLauncher::started, this, [this] { // Console processes only know their pid after being started ProcessHandle pid = m_launcher.applicationPID(); runControl()->setApplicationProcessHandle(pid); @@ -1244,14 +1247,13 @@ void SimpleTargetRunner::doStart(const Runnable &runnable, const IDevice::ConstP if (runnable.command.isEmpty()) { reportFailure(RunControl::tr("No executable specified.")); - } else { - m_launcher.start(runnable); + return; } - } else { - connect(&m_launcher, &ApplicationLauncher::processStarted, this, &RunWorker::reportStarted); - m_launcher.start(runnable, device); + connect(&m_launcher, &ApplicationLauncher::started, this, &RunWorker::reportStarted); } + m_launcher.setRunnable(runnable); + m_launcher.start(); } void SimpleTargetRunner::stop() diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index c1ab5d67813..7a6ca088478 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -31,10 +31,10 @@ #include "projectexplorerconstants.h" #include "runconfiguration.h" +#include <utils/commandline.h> #include <utils/environment.h> #include <utils/processhandle.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> #include <utils/icon.h> #include <QHash> @@ -71,7 +71,7 @@ public: Utils::FilePath workingDirectory; Utils::Environment environment; IDevice::ConstPtr device; // Override the kit's device. Keep unset by default. - QHash<Utils::Id, QVariant> extraData; + QVariantHash extraData; // FIXME: Not necessarily a display name QString displayName() const; @@ -294,7 +294,7 @@ public: protected: void setStarter(const std::function<void()> &starter); - void doStart(const Runnable &runnable, const IDevice::ConstPtr &device); + void doStart(const Runnable &runnable); private: void start() final; diff --git a/src/plugins/projectexplorer/simpleprojectwizard.cpp b/src/plugins/projectexplorer/simpleprojectwizard.cpp index 82a30658aa3..6d7b2e2884f 100644 --- a/src/plugins/projectexplorer/simpleprojectwizard.cpp +++ b/src/plugins/projectexplorer/simpleprojectwizard.cpp @@ -42,7 +42,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/filewizardpage.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/wizard.h> #include <QApplication> diff --git a/src/plugins/projectexplorer/treescanner.h b/src/plugins/projectexplorer/treescanner.h index 26992448928..ff73e8e563e 100644 --- a/src/plugins/projectexplorer/treescanner.h +++ b/src/plugins/projectexplorer/treescanner.h @@ -28,8 +28,8 @@ #include "projectexplorer_export.h" #include "projectnodes.h" -#include <utils/mimetypes/mimedatabase.h> #include <utils/fileutils.h> +#include <utils/mimeutils.h> #include <QObject> #include <QFuture> diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp index 86906317ea9..bef1aea80e1 100644 --- a/src/plugins/python/pythonlanguageclient.cpp +++ b/src/plugins/python/pythonlanguageclient.cpp @@ -31,20 +31,26 @@ #include "pythonutils.h" #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> #include <coreplugin/progressmanager/progressmanager.h> #include <languageclient/languageclientmanager.h> #include <texteditor/textdocument.h> +#include <texteditor/texteditor.h> #include <utils/infobar.h> #include <utils/qtcprocess.h> #include <utils/runextensions.h> #include <utils/variablechooser.h> +#include <QCheckBox> #include <QComboBox> +#include <QDialogButtonBox> #include <QFutureWatcher> #include <QGridLayout> +#include <QGroupBox> +#include <QPushButton> #include <QRegularExpression> #include <QTimer> @@ -82,7 +88,7 @@ static QString pythonName(const FilePath &pythonPath) pythonProcess.setTimeoutS(2); pythonProcess.setCommand({pythonPath, {"--version"}}); pythonProcess.runBlocking(); - if (pythonProcess.result() != QtcProcess::FinishedWithSuccess) + if (pythonProcess.result() != ProcessResult::FinishedWithSuccess) return {}; name = pythonProcess.allOutput().trimmed(); nameForPython[pythonPath] = name; @@ -167,6 +173,154 @@ static PythonLanguageServerState checkPythonLanguageServer(const FilePath &pytho return {PythonLanguageServerState::CanNotBeInstalled, FilePath()}; } +static const QStringList &plugins() +{ + static const QStringList plugins{"flake8", + "jedi_completion", + "jedi_definition", + "jedi_hover", + "jedi_references", + "jedi_signature_help", + "jedi_symbols", + "mccabe", + "pycodestyle", + "pydocstyle", + "pyflakes", + "pylint", + "rope_completion", + "yapf"}; + return plugins; +} + +class PylsConfigureDialog : public QDialog +{ + Q_DECLARE_TR_FUNCTIONS(PylsConfigureDialog) +public: + PylsConfigureDialog() + : QDialog(Core::ICore::dialogParent()) + , m_editor(jsonEditor()) + , m_advancedLabel(new QLabel) + , m_pluginsGroup(new QGroupBox(tr("Plugins:"))) + { + auto mainLayout = new QVBoxLayout; + + auto pluginsLayout = new QGridLayout; + m_pluginsGroup->setLayout(pluginsLayout); + int i = 0; + for (const QString &plugin : plugins()) { + auto checkBox = new QCheckBox(plugin, this); + connect(checkBox, &QCheckBox::toggled, this, [this, plugin](bool enabled) { + updatePluginEnabled(enabled, plugin); + }); + m_checkBoxes[plugin] = checkBox; + pluginsLayout->addWidget(checkBox, i / 4, i % 4); + ++i; + } + mainLayout->addWidget(m_pluginsGroup); + + const QString labelText = tr( + "For a complete list of avilable options, consult the <a " + "href=\"https://github.com/python-lsp/python-lsp-server/blob/develop/" + "CONFIGURATION.md\">Python LSP Server configuration documentation</a>."); + + m_advancedLabel->setText(labelText); + m_advancedLabel->setOpenExternalLinks(true); + mainLayout->addWidget(m_advancedLabel); + mainLayout->addWidget(m_editor->editorWidget(), 1); + + setAdvanced(false); + + mainLayout->addStretch(); + + auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + + auto advanced = new QPushButton(tr("Advanced")); + advanced->setCheckable(true); + advanced->setChecked(false); + buttons->addButton(advanced, QDialogButtonBox::ActionRole); + + connect(advanced, + &QPushButton::toggled, + this, + &PylsConfigureDialog::setAdvanced); + + connect(buttons->button(QDialogButtonBox::Cancel), + &QPushButton::clicked, + this, + &QDialog::reject); + + connect(buttons->button(QDialogButtonBox::Ok), + &QPushButton::clicked, + this, + &QDialog::accept); + + mainLayout->addWidget(buttons); + setLayout(mainLayout); + + resize(640, 480); + } + + void setConfiguration(const QString &configuration) + { + m_editor->textDocument()->setPlainText(configuration); + updateCheckboxes(); + } + + QString configuration() const { return m_editor->textDocument()->plainText(); } + +private: + void setAdvanced(bool advanced) + { + m_editor->editorWidget()->setVisible(advanced); + m_advancedLabel->setVisible(advanced); + m_pluginsGroup->setVisible(!advanced); + updateCheckboxes(); + } + + void updateCheckboxes() + { + const QJsonDocument document = QJsonDocument::fromJson( + m_editor->textDocument()->plainText().toUtf8()); + if (document.isObject()) { + const QJsonObject pluginsObject + = document.object()["pylsp"].toObject()["plugins"].toObject(); + for (const QString &plugin : plugins()) { + auto checkBox = m_checkBoxes[plugin]; + if (!checkBox) + continue; + const QJsonValue enabled = pluginsObject[plugin].toObject()["enabled"]; + if (!enabled.isBool()) + checkBox->setCheckState(Qt::PartiallyChecked); + else + checkBox->setCheckState(enabled.toBool(false) ? Qt::Checked : Qt::Unchecked); + } + } + } + + void updatePluginEnabled(bool enabled, const QString &plugin) + { + QJsonDocument document = QJsonDocument::fromJson( + m_editor->textDocument()->plainText().toUtf8()); + if (document.isNull()) + return; + QJsonObject config = document.object(); + QJsonObject pylsp = config["pylsp"].toObject(); + QJsonObject plugins = pylsp["plugins"].toObject(); + QJsonObject pluginValue = plugins[plugin].toObject(); + pluginValue.insert("enabled", enabled); + plugins.insert(plugin, pluginValue); + pylsp.insert("plugins", plugins); + config.insert("pylsp", pylsp); + document.setObject(config); + m_editor->textDocument()->setPlainText(QString::fromUtf8(document.toJson())); + } + + QMap<QString, QCheckBox *> m_checkBoxes; + TextEditor::BaseTextEditor *m_editor = nullptr; + QLabel *m_advancedLabel = nullptr; + QGroupBox *m_pluginsGroup = nullptr; +}; + class PyLSSettingsWidget : public QWidget { Q_DECLARE_TR_FUNCTIONS(PyLSSettingsWidget) @@ -175,6 +329,8 @@ public: : QWidget(parent) , m_name(new QLineEdit(settings->m_name, this)) , m_interpreter(new QComboBox(this)) + , m_configure(new QPushButton(tr("Configure..."), this)) + , m_configuration(settings->m_configuration) { int row = 0; auto *mainLayout = new QGridLayout; @@ -191,10 +347,14 @@ public: mainLayout->addWidget(m_interpreter, row, 1); setLayout(mainLayout); + mainLayout->addWidget(m_configure, ++row, 0); + connect(PythonSettings::instance(), &PythonSettings::interpretersChanged, this, &PyLSSettingsWidget::updateInterpreters); + + connect(m_configure, &QPushButton::clicked, this, &PyLSSettingsWidget::showConfigureDialog); } void updateInterpreters(const QList<Interpreter> &interpreters, const QString &defaultId) @@ -216,10 +376,21 @@ public: QString name() const { return m_name->text(); } QString interpreterId() const { return m_interpreter->currentData().toString(); } + QString configuration() const { return m_configuration; } private: + void showConfigureDialog() + { + PylsConfigureDialog dialog; + dialog.setConfiguration(m_configuration); + if (dialog.exec() == QDialog::Accepted) + m_configuration = dialog.configuration(); + } + QLineEdit *m_name = nullptr; QComboBox *m_interpreter = nullptr; + QPushButton *m_configure = nullptr; + QString m_configuration; }; PyLSSettings::PyLSSettings() @@ -229,6 +400,8 @@ PyLSSettings::PyLSSettings() m_startBehavior = RequiresFile; m_languageFilter.mimeTypes = QStringList(Constants::C_PY_MIMETYPE); m_arguments = "-m pylsp"; + const QJsonDocument config(defaultConfiguration()); + m_configuration = QString::fromUtf8(config.toJson()); } bool PyLSSettings::isValid() const @@ -248,6 +421,10 @@ QVariantMap PyLSSettings::toMap() const void PyLSSettings::fromMap(const QVariantMap &map) { StdIOSettings::fromMap(map); + if (m_configuration.isEmpty()) { + const QJsonDocument config(defaultConfiguration()); + m_configuration = QString::fromUtf8(config.toJson()); + } setInterpreter(map[interpreterKey].toString()); } @@ -262,6 +439,15 @@ bool PyLSSettings::applyFromSettingsWidget(QWidget *widget) changed |= m_interpreterId != pylswidget->interpreterId(); setInterpreter(pylswidget->interpreterId()); + if (m_configuration != pylswidget->configuration()) { + m_configuration = pylswidget->configuration(); + if (!changed) { // if only the settings configuration changed just send an update + const QVector<Client *> clients = LanguageClientManager::clientForSetting(this); + for (Client *client : clients) + client->updateConfiguration(configuration()); + } + } + return changed; } @@ -290,6 +476,36 @@ Client *PyLSSettings::createClient(BaseClientInterface *interface) const return new Client(interface); } +QJsonObject PyLSSettings::defaultConfiguration() +{ + static QJsonObject configuration; + if (configuration.isEmpty()) { + QJsonObject enabled; + enabled.insert("enabled", true); + QJsonObject disabled; + disabled.insert("enabled", false); + QJsonObject plugins; + plugins.insert("flake8", disabled); + plugins.insert("jedi_completion", enabled); + plugins.insert("jedi_definition", enabled); + plugins.insert("jedi_hover", enabled); + plugins.insert("jedi_references", enabled); + plugins.insert("jedi_signature_help", enabled); + plugins.insert("jedi_symbols", enabled); + plugins.insert("mccabe", disabled); + plugins.insert("pycodestyle", disabled); + plugins.insert("pydocstyle", disabled); + plugins.insert("pyflakes", enabled); + plugins.insert("pylint", disabled); + plugins.insert("rope_completion", enabled); + plugins.insert("yapf", enabled); + QJsonObject pylsp; + pylsp.insert("plugins", plugins); + configuration.insert("pylsp", pylsp); + } + return configuration; +} + PyLSConfigureAssistant *PyLSConfigureAssistant::instance() { static auto *instance = new PyLSConfigureAssistant(PythonPlugin::instance()); @@ -385,7 +601,7 @@ private: void installFinished() { m_future.reportFinished(); - if (m_process.result() == QtcProcess::FinishedWithSuccess) { + if (m_process.result() == ProcessResult::FinishedWithSuccess) { if (Client *client = registerLanguageServer(m_python)) LanguageClientManager::openDocumentWithClient(m_document, client); } else { diff --git a/src/plugins/python/pythonlanguageclient.h b/src/plugins/python/pythonlanguageclient.h index 67e0e5f104d..97cb00c311d 100644 --- a/src/plugins/python/pythonlanguageclient.h +++ b/src/plugins/python/pythonlanguageclient.h @@ -57,6 +57,8 @@ public: LanguageClient::Client *createClient(LanguageClient::BaseClientInterface *interface) const final; private: + static QJsonObject defaultConfiguration(); + QString m_interpreterId; PyLSSettings(const PyLSSettings &other) = default; diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index 314b3dc3ad5..5f4957d17a3 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -283,7 +283,7 @@ Interpreter::Interpreter(const FilePath &python, const QString &defaultName, boo pythonProcess.setTimeoutS(1); pythonProcess.setCommand({python, {"--version"}}); pythonProcess.runBlocking(); - if (pythonProcess.result() == QtcProcess::FinishedWithSuccess) + if (pythonProcess.result() == ProcessResult::FinishedWithSuccess) name = pythonProcess.stdOut().trimmed(); if (name.isEmpty()) name = defaultName; diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp index 8902ec48ed0..aff91c70986 100644 --- a/src/plugins/python/pythonutils.cpp +++ b/src/plugins/python/pythonutils.cpp @@ -36,7 +36,7 @@ #include <projectexplorer/target.h> #include <utils/algorithm.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcprocess.h> using namespace Utils; @@ -107,7 +107,8 @@ void openPythonRepl(QObject *parent, const FilePath &file, ReplType type) }; const auto args = QStringList{"-i"} + replImportArgs(file, type); - auto process = new QtcProcess(QtcProcess::TerminalOn, parent); + auto process = new QtcProcess(parent); + process->setTerminalMode(TerminalMode::On); const FilePath pythonCommand = detectPython(file); process->setCommand({pythonCommand, args}); process->setWorkingDirectory(workingDir(file)); diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp index 19bfdc27b0a..e6f266ed191 100644 --- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp +++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp @@ -45,7 +45,6 @@ #include <ios/iosconstants.h> #include <qtsupport/baseqtversion.h> #include <qtsupport/qtkitinformation.h> -#include <winrt/winrtconstants.h> #include <QDir> #include <QFileInfo> @@ -60,7 +59,6 @@ using namespace Constants; namespace Internal { using namespace ProjectExplorer::Constants; using namespace Ios::Constants; -using namespace WinRt::Internal::Constants; static QString extractToolchainPrefix(QString *compilerName) { diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp index be80dfcb7f1..2e242fa126f 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp @@ -46,7 +46,6 @@ #include <qtsupport/qtkitinformation.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp index 8739bed7b3b..5a8e813d3f1 100644 --- a/src/plugins/qbsprojectmanager/qbssession.cpp +++ b/src/plugins/qbsprojectmanager/qbssession.cpp @@ -169,8 +169,11 @@ void QbsSession::initialize() { Environment env = Environment::systemEnvironment(); env.set("QT_FORCE_STDERR_LOGGING", "1"); + d->packetReader = new PacketReader(this); - d->qbsProcess = new QtcProcess(ProcessMode::Writer, this); + + d->qbsProcess = new QtcProcess(this); + d->qbsProcess->setProcessMode(ProcessMode::Writer); d->qbsProcess->setEnvironment(env); connect(d->qbsProcess, &QtcProcess::readyReadStandardOutput, this, [this] { d->packetReader->handleData(d->qbsProcess->readAllStandardOutput()); diff --git a/src/plugins/qmakeprojectmanager/externaleditors.cpp b/src/plugins/qmakeprojectmanager/externaleditors.cpp index 6d5893e6701..3d43bd45418 100644 --- a/src/plugins/qmakeprojectmanager/externaleditors.cpp +++ b/src/plugins/qmakeprojectmanager/externaleditors.cpp @@ -272,12 +272,7 @@ bool DesignerExternalEditor::startEditor(const Utils::FilePath &filePath, QStrin m_processCache.insert(binary, socket); auto mapSlot = [this, binary] { processTerminated(binary); }; connect(socket, &QAbstractSocket::disconnected, this, mapSlot); -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - const auto errorOccurred = QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error); -#else - const auto errorOccurred = &QAbstractSocket::errorOccurred; -#endif - connect(socket, errorOccurred, this, mapSlot); + connect(socket, &QAbstractSocket::errorOccurred, this, mapSlot); } return true; } diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp index 018b4c339f1..d3683127ad9 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp @@ -56,10 +56,8 @@ #include <qtsupport/qtkitinformation.h> #include <qtsupport/qtversionmanager.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> -#include <utils/qtcassert.h> #include <QDebug> #include <QInputDialog> diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp index 0931743f9b4..de37451d5d3 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp @@ -46,13 +46,13 @@ #include <texteditor/tabsettings.h> #include <texteditor/texteditorsettings.h> +#include <utils/QtConcurrentTools> #include <utils/algorithm.h> #include <utils/filesystemwatcher.h> +#include <utils/mimeutils.h> #include <utils/qtcprocess.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/stringutils.h> #include <utils/temporarydirectory.h> -#include <utils/QtConcurrentTools> #include <QLoggingCategory> #include <QMessageBox> diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp index 8b0a2cda8a2..3bb1f3cb21a 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryview.cpp @@ -99,12 +99,7 @@ WidgetInfo AssetsLibraryView::widgetInfo() imageCacheData()->synchronousFontImageCache}; } - return createWidgetInfo(m_widget.data(), - new WidgetInfo::ToolBarWidgetDefaultFactory<AssetsLibraryWidget>(m_widget.data()), - "Assets", - WidgetInfo::LeftPane, - 0, - tr("Assets")); + return createWidgetInfo(m_widget.data(), "Assets", WidgetInfo::LeftPane, 0, tr("Assets")); } void AssetsLibraryView::modelAttached(Model *model) diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 6737d66d18d..9b5716da111 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -207,7 +207,6 @@ void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, cons WidgetInfo ConnectionView::widgetInfo() { return createWidgetInfo(m_connectionViewWidget.data(), - new WidgetInfo::ToolBarWidgetDefaultFactory<ConnectionViewWidget>(connectionViewWidget()), QLatin1String("ConnectionView"), WidgetInfo::LeftPane, 0, diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp index 0c535ec48d7..e7bf42aa4ff 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp @@ -69,8 +69,7 @@ bool CurveEditorView::hasWidget() const WidgetInfo CurveEditorView::widgetInfo() { - return createWidgetInfo( - m_editor, nullptr, "CurveEditorId", WidgetInfo::BottomPane, 0, tr("Curve Editor")); + return createWidgetInfo(m_editor, "CurveEditorId", WidgetInfo::BottomPane, 0, tr("Curve Editor")); } void CurveEditorView::modelAttached(Model *model) diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index be09b3864a4..06c2078f537 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -360,7 +360,7 @@ void DebugView::rewriterEndTransaction() WidgetInfo DebugView::widgetInfo() { - return createWidgetInfo(m_debugViewWidget.data(), nullptr, QStringLiteral("DebugView"), WidgetInfo::LeftPane, 0, tr("Debug View")); + return createWidgetInfo(m_debugViewWidget.data(), QStringLiteral("DebugView"), WidgetInfo::LeftPane, 0, tr("Debug View")); } bool DebugView::hasWidget() const diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index dc4af0255fd..f664d793d80 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -83,7 +83,7 @@ WidgetInfo Edit3DView::widgetInfo() if (!m_edit3DWidget) createEdit3DWidget(); - return createWidgetInfo(m_edit3DWidget.data(), nullptr, "Editor3D", WidgetInfo::CentralPane, 0, tr("3D Editor"), DesignerWidgetFlags::IgnoreErrors); + return createWidgetInfo(m_edit3DWidget.data(), "Editor3D", WidgetInfo::CentralPane, 0, tr("3D Editor"), DesignerWidgetFlags::IgnoreErrors); } Edit3DWidget *Edit3DView::edit3DWidget() const diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index e07c17cb04e..93a072cf17c 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -362,7 +362,7 @@ WidgetInfo FormEditorView::widgetInfo() if (!m_formEditorWidget) createFormEditorWidget(); - return createWidgetInfo(m_formEditorWidget.data(), nullptr, "FormEditor", WidgetInfo::CentralPane, 0, tr("Form Editor"), DesignerWidgetFlags::IgnoreErrors); + return createWidgetInfo(m_formEditorWidget.data(), "FormEditor", WidgetInfo::CentralPane, 0, tr("Form Editor"), DesignerWidgetFlags::IgnoreErrors); } FormEditorWidget *FormEditorView::formEditorWidget() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index e1d078306ce..b3d67f5c8ed 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -306,7 +306,10 @@ void ItemLibraryModel::setSearchText(const QString &searchText) m_searchText = lowerSearchText; bool changed = false; - updateVisibility(&changed); + if (updateVisibility(&changed); changed) { + beginResetModel(); + endResetModel(); + } selectImportFirstVisibleCategory(); } @@ -590,11 +593,6 @@ void ItemLibraryModel::updateVisibility(bool *changed) if (!m_searchText.isEmpty() && hasVisibleItems && !import->importExpanded()) import->setImportExpanded(); } - - if (changed) { - beginResetModel(); - endResetModel(); - } } void ItemLibraryModel::addRoleNames() diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index 6596a10b4a4..321607fc7bf 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -103,12 +103,7 @@ WidgetInfo ItemLibraryView::widgetInfo() imageCacheData()->synchronousFontImageCache}; } - return createWidgetInfo(m_widget.data(), - new WidgetInfo::ToolBarWidgetDefaultFactory<ItemLibraryWidget>(m_widget.data()), - "Components", - WidgetInfo::LeftPane, - 0, - tr("Components")); + return createWidgetInfo(m_widget.data(), "Components", WidgetInfo::LeftPane, 0, tr("Components")); } void ItemLibraryView::modelAttached(Model *model) diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index b1b12da2e0c..606089f6319 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -132,7 +132,6 @@ WidgetInfo NavigatorView::widgetInfo() setupWidget(); return createWidgetInfo(m_widget.data(), - new WidgetInfo::ToolBarWidgetDefaultFactory<NavigatorWidget>(m_widget.data()), QStringLiteral("Navigator"), WidgetInfo::LeftPane, 0, diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index fb5d0c6861e..7c2b3230a13 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -795,7 +795,7 @@ bool PropertyEditorView::hasWidget() const WidgetInfo PropertyEditorView::widgetInfo() { - return createWidgetInfo(m_stackedWidget, nullptr, QStringLiteral("Properties"), WidgetInfo::RightPane, 0, tr("Properties")); + return createWidgetInfo(m_stackedWidget, QStringLiteral("Properties"), WidgetInfo::RightPane, 0, tr("Properties")); } void PropertyEditorView::currentStateChanged(const ModelNode &node) diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp index cf1f3771412..001d356516b 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -77,7 +77,7 @@ WidgetInfo StatesEditorView::widgetInfo() if (!m_statesEditorWidget) m_statesEditorWidget = new StatesEditorWidget(this, m_statesEditorModel.data()); - return createWidgetInfo(m_statesEditorWidget.data(), nullptr, QLatin1String("StatesEditor"), WidgetInfo::BottomPane, 0, tr("States")); + return createWidgetInfo(m_statesEditorWidget.data(), QLatin1String("StatesEditor"), WidgetInfo::BottomPane, 0, tr("States")); } void StatesEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index 7add0cd8e26..e5c9fadc530 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -145,7 +145,7 @@ void TextEditorView::nodeReparented(const ModelNode &/*node*/, const NodeAbstrac WidgetInfo TextEditorView::widgetInfo() { - return createWidgetInfo(m_widget, nullptr, "TextEditor", WidgetInfo::CentralPane, 0, tr("Text Editor"), DesignerWidgetFlags::IgnoreErrors); + return createWidgetInfo(m_widget, "TextEditor", WidgetInfo::CentralPane, 0, tr("Text Editor"), DesignerWidgetFlags::IgnoreErrors); } void TextEditorView::contextHelp(const Core::IContext::HelpCallback &callback) const diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index a8b20f83801..8c53866c1ad 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -669,7 +669,6 @@ TimelineWidget *TimelineView::createWidget() WidgetInfo TimelineView::widgetInfo() { return createWidgetInfo(createWidget(), - nullptr, QStringLiteral("Timelines"), WidgetInfo::BottomPane, 0, diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index ec715624f34..06b83b44b35 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -323,7 +323,6 @@ TransitionEditorWidget *TransitionEditorView::createWidget() WidgetInfo TransitionEditorView::widgetInfo() { return createWidgetInfo(createWidget(), - nullptr, "TransitionEditor", WidgetInfo::BottomPane, 0, diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index b303a4fdef7..920689857a5 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -70,30 +70,6 @@ enum DesignerWidgetFlags { class WidgetInfo { public: - class ToolBarWidgetFactoryInterface { - public: - ToolBarWidgetFactoryInterface() = default; - - virtual QList<QToolButton*> createToolBarWidgets() = 0; - - virtual ~ToolBarWidgetFactoryInterface() = default; - }; - - template <class T> - class ToolBarWidgetDefaultFactory : public ToolBarWidgetFactoryInterface { - public: - ToolBarWidgetDefaultFactory(T *t ) : m_t(t) - {} - - QList<QToolButton*> createToolBarWidgets() override - { - return m_t->createToolBarWidgets(); - } - - private: - T * m_t; - }; - enum PlacementHint { NoPane, LeftPane, @@ -108,7 +84,6 @@ public: QWidget *widget = nullptr; int placementPriority; PlacementHint placementHint; - ToolBarWidgetFactoryInterface *toolBarWidgetFactory = nullptr; DesignerWidgetFlags widgetFlags = DesignerWidgetFlags::DisableOnError; }; @@ -309,7 +284,6 @@ protected: void setModel(Model * model); void removeModel(); static WidgetInfo createWidgetInfo(QWidget *widget = nullptr, - WidgetInfo::ToolBarWidgetFactoryInterface *toolBarWidgetFactory = nullptr, const QString &uniqueId = QString(), WidgetInfo::PlacementHint placementHint = WidgetInfo::NoPane, int placementPriority = 0, diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp index eb994376ea0..5b22c6c55e4 100644 --- a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp +++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp @@ -146,11 +146,6 @@ void ConnectionManager::closeSocketsAndKillProcesses() connection.socket->abort(); } - if (connection.qmlPuppetProcess) { - QTimer::singleShot(3000, connection.qmlPuppetProcess.get(), &QProcess::terminate); - QTimer::singleShot(6000, connection.qmlPuppetProcess.get(), &QProcess::kill); - } - connection.clear(); } } diff --git a/src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h b/src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h index b03c86f772b..9274f916b83 100644 --- a/src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h +++ b/src/plugins/qmldesigner/designercore/instances/qprocessuniqueptr.h @@ -41,10 +41,7 @@ public: QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), process, &QProcess::deleteLater); - - process->terminate(); - - process->deleteLater(); + process->kill(); } }; diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 790b6ad5a53..e7e5636d233 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -128,7 +128,6 @@ void AbstractView::removeModel() } WidgetInfo AbstractView::createWidgetInfo(QWidget *widget, - WidgetInfo::ToolBarWidgetFactoryInterface *toolBarWidgetFactory, const QString &uniqueId, WidgetInfo::PlacementHint placementHint, int placementPriority, @@ -138,7 +137,6 @@ WidgetInfo AbstractView::createWidgetInfo(QWidget *widget, WidgetInfo widgetInfo; widgetInfo.widget = widget; - widgetInfo.toolBarWidgetFactory = toolBarWidgetFactory; widgetInfo.uniqueId = uniqueId; widgetInfo.placementHint = placementHint; widgetInfo.placementPriority = placementPriority; diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index ec0303e54e3..5699875fd78 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -110,37 +110,6 @@ QList<QToolButton *> ItemLibrarySideBarItem::createToolBarWidgets() return qobject_cast<ItemLibraryWidget*>(widget())->createToolBarWidgets(); } -class DesignerSideBarItem : public Core::SideBarItem -{ -public: - explicit DesignerSideBarItem(QWidget *widget, WidgetInfo::ToolBarWidgetFactoryInterface *createToolBarWidgets, const QString &id); - ~DesignerSideBarItem() override; - - QList<QToolButton *> createToolBarWidgets() override; - -private: - WidgetInfo::ToolBarWidgetFactoryInterface *m_toolBarWidgetFactory; - -}; - -DesignerSideBarItem::DesignerSideBarItem(QWidget *widget, WidgetInfo::ToolBarWidgetFactoryInterface *toolBarWidgetFactory, const QString &id) - : Core::SideBarItem(widget, id) , m_toolBarWidgetFactory(toolBarWidgetFactory) -{ -} - -DesignerSideBarItem::~DesignerSideBarItem() -{ - delete m_toolBarWidgetFactory; -} - -QList<QToolButton *> DesignerSideBarItem::createToolBarWidgets() -{ - if (m_toolBarWidgetFactory) - return m_toolBarWidgetFactory->createToolBarWidgets(); - - return QList<QToolButton *>(); -} - // ---------- DesignModeWidget DesignModeWidget::DesignModeWidget() : m_toolBar(new Core::EditorToolBar(this)) @@ -367,7 +336,7 @@ void DesignModeWidget::setup() auto outputPanePlaceholder = new Core::OutputPanePlaceHolder(Core::Constants::MODE_DESIGN); m_outputPaneDockWidget = new ADS::DockWidget(uniqueId); m_outputPaneDockWidget->setWidget(outputPanePlaceholder); - m_outputPaneDockWidget->setWindowTitle(tr("Output Pane")); + m_outputPaneDockWidget->setWindowTitle(tr("Output")); m_dockManager->addDockWidget(ADS::NoDockWidgetArea, m_outputPaneDockWidget); // Set unique id as object name diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp index 5d60d88b974..dffea44be27 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.cpp +++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp @@ -55,7 +55,7 @@ #include <utils/algorithm.h> #include <utils/hostosinfo.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QDir> #include <QFile> diff --git a/src/plugins/qmljstools/qmljsrefactoringchanges.cpp b/src/plugins/qmljstools/qmljsrefactoringchanges.cpp index e2e8bb2bda4..98d86199dd3 100644 --- a/src/plugins/qmljstools/qmljsrefactoringchanges.cpp +++ b/src/plugins/qmljstools/qmljsrefactoringchanges.cpp @@ -57,7 +57,7 @@ public: const QTextBlock end = doc->findBlock(selection.selectionEnd()).next(); const TextEditor::TabSettings &tabSettings = - ProjectExplorer::actualTabSettings(filePath.toString(), textDocument); + ProjectExplorer::actualTabSettings(filePath, textDocument); CreatorCodeFormatter codeFormatter(tabSettings); codeFormatter.updateStateUntil(block); do { @@ -82,7 +82,7 @@ public: const TextEditor::TextDocument *textDocument) const override { const TextEditor::TabSettings &tabSettings = - ProjectExplorer::actualTabSettings(filePath.toString(), textDocument); + ProjectExplorer::actualTabSettings(filePath, textDocument); QmlJSEditor::Internal::Indenter indenter(selection.document()); indenter.reindent(selection, tabSettings); diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp index 1af9f8bb793..22c9db93cb2 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp @@ -149,7 +149,8 @@ LocalQmlPreviewSupport::LocalQmlPreviewSupport(ProjectExplorer::RunControl *runC runnable.command.addArg(QmlDebug::qmlDebugLocalArguments(QmlDebug::QmlPreviewServices, serverUrl.path())); - doStart(runnable, {}); + runnable.device.reset(); + doStart(runnable); }); } diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp index 9eaf06fee4b..06822865ef4 100644 --- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp @@ -255,8 +255,8 @@ LocalQmlProfilerSupport::LocalQmlProfilerSupport(RunControl *runControl, const Q arguments += ' ' + debuggee.command.arguments(); debuggee.command.setArguments(arguments); - - doStart(debuggee, {}); + debuggee.device.reset(); + doStart(debuggee); }); } diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp index 2667fcb66b1..602a880fad5 100644 --- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp @@ -37,7 +37,7 @@ #include <projectexplorer/projectexplorer.h> #include <projectexplorer/target.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <QComboBox> diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index f4363afe93b..0c2a8f1530f 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -49,7 +49,6 @@ #include <utils/aspects.h> #include <utils/environment.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/qtcprocess.h> #include <utils/winutils.h> diff --git a/src/plugins/qnx/qnxanalyzesupport.cpp b/src/plugins/qnx/qnxanalyzesupport.cpp index a7bfd50cffe..6cf073c3504 100644 --- a/src/plugins/qnx/qnxanalyzesupport.cpp +++ b/src/plugins/qnx/qnxanalyzesupport.cpp @@ -62,8 +62,8 @@ QnxQmlProfilerSupport::QnxQmlProfilerSupport(RunControl *runControl) Runnable r = runControl->runnable(); r.command.addArg(QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlProfilerServices, serverUrl)); - - doStart(r, runControl->device()); + r.device = runControl->device(); + doStart(r); }); } diff --git a/src/plugins/qnx/qnxdebugsupport.cpp b/src/plugins/qnx/qnxdebugsupport.cpp index c345d76506e..6e9e701142d 100644 --- a/src/plugins/qnx/qnxdebugsupport.cpp +++ b/src/plugins/qnx/qnxdebugsupport.cpp @@ -38,7 +38,6 @@ #include <debugger/debuggerruncontrol.h> #include <projectexplorer/devicesupport/deviceprocessesdialog.h> -#include <projectexplorer/devicesupport/deviceprocesslist.h> #include <projectexplorer/devicesupport/deviceusedportsgatherer.h> #include <projectexplorer/kit.h> #include <projectexplorer/kitchooser.h> @@ -55,6 +54,7 @@ #include <utils/pathchooser.h> #include <utils/portlist.h> +#include <utils/processinfo.h> #include <utils/qtcassert.h> #include <utils/qtcprocess.h> @@ -116,8 +116,8 @@ public: portsGatherer->qmlServer())); } r.command.setArguments(ProcessArgs::joinArgs(arguments)); - - doStart(r, runControl->device()); + r.device = runControl->device(); + doStart(r); }); } }; @@ -202,7 +202,8 @@ public: Runnable r; r.command = {QNX_DEBUG_EXECUTABLE, {QString::number(pdebugPort)}}; - doStart(r, runControl->device()); + r.device = runControl->device(); + doStart(r); }); } }; @@ -243,8 +244,7 @@ void QnxAttachDebugSupport::showProcessesDialog() if (!runConfig) return; - DeviceProcessItem process = dlg.currentProcess(); - const int pid = process.pid; + const int pid = dlg.currentProcess().processId; // QString projectSourceDirectory = dlg.projectSource(); FilePath localExecutable = dlg.localExecutable(); if (localExecutable.isEmpty()) { diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp index c2b704a3bad..8c479574346 100644 --- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp +++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp @@ -91,7 +91,7 @@ QnxDeployQtLibrariesDialog::QnxDeployQtLibrariesDialog(const IDevice::ConstPtr & m_processRunner = new QSsh::SshRemoteProcessRunner(this); connect(m_processRunner, &QSsh::SshRemoteProcessRunner::connectionError, this, &QnxDeployQtLibrariesDialog::handleRemoteProcessError); - connect(m_processRunner, &QSsh::SshRemoteProcessRunner::processClosed, + connect(m_processRunner, &QSsh::SshRemoteProcessRunner::finished, this, &QnxDeployQtLibrariesDialog::handleRemoteProcessCompleted); connect(m_ui->deployButton, &QAbstractButton::clicked, @@ -201,7 +201,7 @@ void QnxDeployQtLibrariesDialog::handleRemoteProcessCompleted() if (m_state == CheckingRemoteDirectory) { // Directory exists - if (m_processRunner->processExitCode() == 0) { + if (m_processRunner->exitCode() == 0) { int answer = QMessageBox::question(this, windowTitle(), tr("The remote directory \"%1\" already exists. " "Deploying to that directory will remove any files " @@ -217,7 +217,7 @@ void QnxDeployQtLibrariesDialog::handleRemoteProcessCompleted() startUpload(); } } else if (m_state == RemovingRemoteDirectory) { - QTC_ASSERT(m_processRunner->processExitCode() == 0, return); + QTC_ASSERT(m_processRunner->exitCode() == 0, return); startUpload(); } diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp index 764355b1e1e..9a01263cbd8 100644 --- a/src/plugins/qnx/qnxdevice.cpp +++ b/src/plugins/qnx/qnxdevice.cpp @@ -101,12 +101,11 @@ void QnxDevice::updateVersionNumber() const { QEventLoop eventLoop; SshDeviceProcess versionNumberProcess(sharedFromThis()); - QObject::connect(&versionNumberProcess, &SshDeviceProcess::finished, &eventLoop, &QEventLoop::quit); - QObject::connect(&versionNumberProcess, &DeviceProcess::errorOccurred, &eventLoop, &QEventLoop::quit); + QObject::connect(&versionNumberProcess, &QtcProcess::finished, &eventLoop, &QEventLoop::quit); + QObject::connect(&versionNumberProcess, &QtcProcess::errorOccurred, &eventLoop, &QEventLoop::quit); - Runnable r; - r.command = {"uname", {"-r"}}; - versionNumberProcess.start(r); + versionNumberProcess.setCommand({"uname", {"-r"}}); + versionNumberProcess.start(); bool isGuiThread = QThread::currentThread() == QCoreApplication::instance()->thread(); if (isGuiThread) @@ -157,7 +156,7 @@ DeviceTester *QnxDevice::createDeviceTester() const return new QnxDeviceTester; } -DeviceProcess *QnxDevice::createProcess(QObject *parent) const +QtcProcess *QnxDevice::createProcess(QObject *parent) const { return new QnxDeviceProcess(sharedFromThis(), parent); } diff --git a/src/plugins/qnx/qnxdevice.h b/src/plugins/qnx/qnxdevice.h index 7ea275c7428..cd9f91fca6c 100644 --- a/src/plugins/qnx/qnxdevice.h +++ b/src/plugins/qnx/qnxdevice.h @@ -47,7 +47,7 @@ public: ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override; ProjectExplorer::DeviceTester *createDeviceTester() const override; - ProjectExplorer::DeviceProcess *createProcess(QObject *parent) const override; + Utils::QtcProcess *createProcess(QObject *parent) const override; int qnxVersion() const; diff --git a/src/plugins/qnx/qnxdeviceprocess.cpp b/src/plugins/qnx/qnxdeviceprocess.cpp index 0a272c3d001..3236ba62d24 100644 --- a/src/plugins/qnx/qnxdeviceprocess.cpp +++ b/src/plugins/qnx/qnxdeviceprocess.cpp @@ -44,21 +44,22 @@ QnxDeviceProcess::QnxDeviceProcess(const QSharedPointer<const IDevice> &device, m_pidFile = QString::fromLatin1("/var/run/qtc.%1.pid").arg(++pidFileCounter); } -QString QnxDeviceProcess::fullCommandLine(const Runnable &runnable) const +QString QnxDeviceProcess::fullCommandLine() const { - QStringList args = ProcessArgs::splitArgs(runnable.command.arguments()); - args.prepend(runnable.command.executable().toString()); + const CommandLine command = commandLine(); + QStringList args = ProcessArgs::splitArgs(command.arguments()); + args.prepend(command.executable().toString()); QString cmd = ProcessArgs::createUnixArgs(args).toString(); QString fullCommandLine = "test -f /etc/profile && . /etc/profile ; " "test -f $HOME/profile && . $HOME/profile ; "; - if (!runnable.workingDirectory.isEmpty()) + if (!workingDirectory().isEmpty()) fullCommandLine += QString::fromLatin1("cd %1 ; ").arg( - ProcessArgs::quoteArg(runnable.workingDirectory.toString())); + ProcessArgs::quoteArg(workingDirectory().toString())); - const Environment env = runnable.environment; + const Environment env = remoteEnvironment(); for (auto it = env.constBegin(); it != env.constEnd(); ++it) { fullCommandLine += QString::fromLatin1("%1='%2' ") .arg(env.key(it)).arg(env.expandedValueForKey(env.key(it))); @@ -72,11 +73,10 @@ QString QnxDeviceProcess::fullCommandLine(const Runnable &runnable) const void QnxDeviceProcess::doSignal(int sig) { auto signaler = new SshDeviceProcess(device(), this); - Runnable r; const QString args = QString("-%2 `cat %1`").arg(m_pidFile).arg(sig); - r.command = CommandLine(FilePath::fromString("kill"), args, CommandLine::Raw); + signaler->setCommand({"kill", args, CommandLine::Raw}); connect(signaler, &SshDeviceProcess::finished, signaler, &QObject::deleteLater); - signaler->start(r); + signaler->start(); } } // namespace Internal diff --git a/src/plugins/qnx/qnxdeviceprocess.h b/src/plugins/qnx/qnxdeviceprocess.h index eeffad6fbb4..bcbf386bff4 100644 --- a/src/plugins/qnx/qnxdeviceprocess.h +++ b/src/plugins/qnx/qnxdeviceprocess.h @@ -26,7 +26,7 @@ #pragma once #include "qnx_export.h" -#include <remotelinux/linuxdevice.h> + #include <projectexplorer/devicesupport/sshdeviceprocess.h> namespace Qnx { @@ -40,7 +40,7 @@ public: void interrupt() override { doSignal(2); } void terminate() override { doSignal(15); } void kill() override { doSignal(9); } - QString fullCommandLine(const ProjectExplorer::Runnable &runnable) const override; + QString fullCommandLine() const override; private: void doSignal(int sig); diff --git a/src/plugins/qnx/qnxdeviceprocesslist.cpp b/src/plugins/qnx/qnxdeviceprocesslist.cpp index 1269ea3c165..3316e0365ff 100644 --- a/src/plugins/qnx/qnxdeviceprocesslist.cpp +++ b/src/plugins/qnx/qnxdeviceprocesslist.cpp @@ -27,12 +27,14 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> +#include <utils/processinfo.h> #include <QRegularExpression> #include <QStringList> using namespace Qnx; using namespace Qnx::Internal; +using namespace Utils; QnxDeviceProcessList::QnxDeviceProcessList( const ProjectExplorer::IDevice::ConstPtr &device, QObject *parent) @@ -45,10 +47,9 @@ QString QnxDeviceProcessList::listProcessesCommandLine() const return QLatin1String("pidin -F '%a %A {/%n}'"); } -QList<ProjectExplorer::DeviceProcessItem> QnxDeviceProcessList::buildProcessList( - const QString &listProcessesReply) const +QList<ProcessInfo> QnxDeviceProcessList::buildProcessList(const QString &listProcessesReply) const { - QList<ProjectExplorer::DeviceProcessItem> processes; + QList<ProcessInfo> processes; QStringList lines = listProcessesReply.split(QLatin1Char('\n')); if (lines.isEmpty()) return processes; @@ -64,10 +65,10 @@ QList<ProjectExplorer::DeviceProcessItem> QnxDeviceProcessList::buildProcessList const int pid = captures[1].toInt(); const QString args = captures[2]; const QString exe = captures[3]; - ProjectExplorer::DeviceProcessItem deviceProcess; - deviceProcess.pid = pid; - deviceProcess.exe = exe.trimmed(); - deviceProcess.cmdLine = args.trimmed(); + ProcessInfo deviceProcess; + deviceProcess.processId = pid; + deviceProcess.executable = exe.trimmed(); + deviceProcess.commandLine = args.trimmed(); processes.append(deviceProcess); } } diff --git a/src/plugins/qnx/qnxdeviceprocesslist.h b/src/plugins/qnx/qnxdeviceprocesslist.h index 4f96b1afe7e..54fb005c392 100644 --- a/src/plugins/qnx/qnxdeviceprocesslist.h +++ b/src/plugins/qnx/qnxdeviceprocesslist.h @@ -40,8 +40,7 @@ public: private: QString listProcessesCommandLine() const override; - QList<ProjectExplorer::DeviceProcessItem> buildProcessList( - const QString &listProcessesReply) const override; + QList<Utils::ProcessInfo> buildProcessList(const QString &listProcessesReply) const override; }; } // namespace Internal diff --git a/src/plugins/qnx/qnxdevicetester.cpp b/src/plugins/qnx/qnxdevicetester.cpp index 17787274c0f..06af90dc786 100644 --- a/src/plugins/qnx/qnxdevicetester.cpp +++ b/src/plugins/qnx/qnxdevicetester.cpp @@ -50,7 +50,7 @@ QnxDeviceTester::QnxDeviceTester(QObject *parent) m_processRunner = new QSsh::SshRemoteProcessRunner(this); connect(m_processRunner, &QSsh::SshRemoteProcessRunner::connectionError, this, &QnxDeviceTester::handleConnectionError); - connect(m_processRunner, &QSsh::SshRemoteProcessRunner::processClosed, + connect(m_processRunner, &QSsh::SshRemoteProcessRunner::finished, this, &QnxDeviceTester::handleProcessFinished); m_commandsToTest << QLatin1String("awk") @@ -124,7 +124,7 @@ void QnxDeviceTester::handleVarRunProcessFinished(const QString &error) QTC_ASSERT(m_state == VarRunTest, return); if (error.isEmpty()) { - if (m_processRunner->processExitCode() == 0) { + if (m_processRunner->exitCode() == 0) { emit progressMessage(tr("Files can be created in /var/run.") + QLatin1Char('\n')); } else { emit errorMessage(tr("Files cannot be created in /var/run.") + QLatin1Char('\n')); @@ -145,8 +145,9 @@ void QnxDeviceTester::handleVarRunProcessFinished(const QString &error) testNextCommand(); } -void QnxDeviceTester::handleProcessFinished(const QString &error) +void QnxDeviceTester::handleProcessFinished() { + const QString error = m_processRunner->errorString(); if (m_state == VarRunTest) { handleVarRunProcessFinished(error); return; @@ -156,7 +157,7 @@ void QnxDeviceTester::handleProcessFinished(const QString &error) const QString command = m_commandsToTest[m_currentCommandIndex]; if (error.isEmpty()) { - if (m_processRunner->processExitCode() == 0) { + if (m_processRunner->exitCode() == 0) { emit progressMessage(tr("%1 found.").arg(command) + QLatin1Char('\n')); } else { emit errorMessage(tr("%1 not found.").arg(command) + QLatin1Char('\n')); diff --git a/src/plugins/qnx/qnxdevicetester.h b/src/plugins/qnx/qnxdevicetester.h index 21c555e5381..e5f3a413209 100644 --- a/src/plugins/qnx/qnxdevicetester.h +++ b/src/plugins/qnx/qnxdevicetester.h @@ -45,8 +45,7 @@ public: private slots: void handleGenericTestFinished(ProjectExplorer::DeviceTester::TestResult result); - - void handleProcessFinished(const QString &error); + void handleProcessFinished(); void handleConnectionError(); private: diff --git a/src/plugins/qnx/qnxdevicewizard.cpp b/src/plugins/qnx/qnxdevicewizard.cpp index c0e64a53903..75f93665752 100644 --- a/src/plugins/qnx/qnxdevicewizard.cpp +++ b/src/plugins/qnx/qnxdevicewizard.cpp @@ -27,7 +27,6 @@ #include "qnxconstants.h" -#include <projectexplorer/devicesupport/deviceusedportsgatherer.h> #include <remotelinux/genericlinuxdeviceconfigurationwizardpages.h> #include <utils/portlist.h> diff --git a/src/plugins/qnx/qnxutils.cpp b/src/plugins/qnx/qnxutils.cpp index 90e3aeb8518..6639f772059 100644 --- a/src/plugins/qnx/qnxutils.cpp +++ b/src/plugins/qnx/qnxutils.cpp @@ -123,7 +123,7 @@ EnvironmentItems QnxUtils::qnxEnvironmentFromEnvFile(const FilePath &filePath) return items; } - if (process.result() != QtcProcess::FinishedWithSuccess) + if (process.result() != ProcessResult::FinishedWithSuccess) return items; // parsing process output diff --git a/src/plugins/qnx/slog2inforunner.cpp b/src/plugins/qnx/slog2inforunner.cpp index c5209fdfe54..3abb35d2b2e 100644 --- a/src/plugins/qnx/slog2inforunner.cpp +++ b/src/plugins/qnx/slog2inforunner.cpp @@ -52,15 +52,15 @@ Slog2InfoRunner::Slog2InfoRunner(RunControl *runControl) m_applicationId.truncate(63); m_testProcess = new QnxDeviceProcess(device(), this); - connect(m_testProcess, &DeviceProcess::finished, this, &Slog2InfoRunner::handleTestProcessCompleted); + connect(m_testProcess, &QtcProcess::finished, this, &Slog2InfoRunner::handleTestProcessCompleted); m_launchDateTimeProcess = new SshDeviceProcess(device(), this); - connect(m_launchDateTimeProcess, &DeviceProcess::finished, this, &Slog2InfoRunner::launchSlog2Info); + connect(m_launchDateTimeProcess, &QtcProcess::finished, this, &Slog2InfoRunner::launchSlog2Info); m_logProcess = new QnxDeviceProcess(device(), this); - connect(m_logProcess, &DeviceProcess::readyReadStandardOutput, this, &Slog2InfoRunner::readLogStandardOutput); - connect(m_logProcess, &DeviceProcess::readyReadStandardError, this, &Slog2InfoRunner::readLogStandardError); - connect(m_logProcess, &DeviceProcess::errorOccurred, this, &Slog2InfoRunner::handleLogError); + connect(m_logProcess, &QtcProcess::readyReadStandardOutput, this, &Slog2InfoRunner::readLogStandardOutput); + connect(m_logProcess, &QtcProcess::readyReadStandardError, this, &Slog2InfoRunner::readLogStandardError); + connect(m_logProcess, &QtcProcess::errorOccurred, this, &Slog2InfoRunner::handleLogError); } void Slog2InfoRunner::printMissingWarning() @@ -70,9 +70,8 @@ void Slog2InfoRunner::printMissingWarning() void Slog2InfoRunner::start() { - Runnable r; - r.command = {"slog2info", {}}; - m_testProcess->start(r); + m_testProcess->setCommand({"slog2info", {}}); + m_testProcess->start(); reportStarted(); } @@ -108,9 +107,8 @@ void Slog2InfoRunner::handleTestProcessCompleted() void Slog2InfoRunner::readLaunchTime() { - Runnable r; - r.command = CommandLine("date", "+\"%d %H:%M:%S\"", CommandLine::Raw); - m_launchDateTimeProcess->start(r); + m_launchDateTimeProcess->setCommand({"date", "+\"%d %H:%M:%S\"", CommandLine::Raw}); + m_launchDateTimeProcess->start(); } void Slog2InfoRunner::launchSlog2Info() @@ -124,9 +122,8 @@ void Slog2InfoRunner::launchSlog2Info() m_launchDateTime = QDateTime::fromString(QString::fromLatin1(m_launchDateTimeProcess->readAllStandardOutput()).trimmed(), QString::fromLatin1("dd HH:mm:ss")); - Runnable r; - r.command = {"slog2info", {"-w"}}; - m_logProcess->start(r); + m_logProcess->setCommand({"slog2info", {"-w"}}); + m_logProcess->start(); } void Slog2InfoRunner::readLogStandardOutput() diff --git a/src/plugins/remotelinux/genericdirectuploadservice.cpp b/src/plugins/remotelinux/genericdirectuploadservice.cpp index ecbac0498c6..e1f95e1c56b 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.cpp +++ b/src/plugins/remotelinux/genericdirectuploadservice.cpp @@ -200,12 +200,11 @@ void GenericDirectUploadService::runStat(const DeployableFile &file) const QString statCmd = "stat -t " + Utils::ProcessArgs::quoteArgUnix(file.remoteFilePath()); SshRemoteProcess * const statProc = connection()->createRemoteProcess(statCmd).release(); statProc->setParent(this); - connect(statProc, &SshRemoteProcess::done, this, - [this, statProc, state = d->state](const QString &errorMsg) { + connect(statProc, &SshRemoteProcess::finished, this, [this, statProc, state = d->state] { QTC_ASSERT(d->state == state, return); const DeployableFile file = d->getFileForProcess(statProc); QTC_ASSERT(file.isValid(), return); - const QDateTime timestamp = timestampFromStat(file, statProc, errorMsg); + const QDateTime timestamp = timestampFromStat(file, statProc, statProc->errorString()); statProc->deleteLater(); switch (state) { case PreChecking: @@ -342,11 +341,11 @@ void GenericDirectUploadService::chmod() SshRemoteProcess * const chmodProc = connection()->createRemoteProcess(command).release(); chmodProc->setParent(this); - connect(chmodProc, &SshRemoteProcess::done, this, - [this, chmodProc, state = d->state](const QString &error) { + connect(chmodProc, &SshRemoteProcess::finished, this, [this, chmodProc, state = d->state] { QTC_ASSERT(state == d->state, return); const DeployableFile file = d->getFileForProcess(chmodProc); QTC_ASSERT(file.isValid(), return); + const QString error = chmodProc->errorString(); if (!error.isEmpty()) { emit warningMessage(tr("Remote chmod failed for file \"%1\": %2") .arg(file.remoteFilePath(), error)); diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 7b19d51b2e9..a69a35c1787 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -48,6 +48,7 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/port.h> +#include <utils/processinfo.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> #include <utils/temporaryfile.h> @@ -100,12 +101,12 @@ private: "done").arg(QLatin1String(Delimiter0)).arg(QLatin1String(Delimiter1)); } - QList<DeviceProcessItem> buildProcessList(const QString &listProcessesReply) const override + QList<ProcessInfo> buildProcessList(const QString &listProcessesReply) const override { - QList<DeviceProcessItem> processes; + QList<ProcessInfo> processes; const QStringList lines = listProcessesReply.split(QString::fromLatin1(Delimiter0) + QString::fromLatin1(Delimiter1), Qt::SkipEmptyParts); - foreach (const QString &line, lines) { + for (const QString &line : lines) { const QStringList elements = line.split(QLatin1Char('\n')); if (elements.count() < 4) { qDebug("%s: Expected four list elements, got %d. Line was '%s'.", Q_FUNC_INFO, @@ -132,10 +133,10 @@ private: + QLatin1Char(']'); } - DeviceProcessItem process; - process.pid = pid; - process.cmdLine = command; - process.exe = elements.at(3); + ProcessInfo process; + process.processId = pid; + process.commandLine = command; + process.executable = elements.at(3); processes.append(process); } @@ -209,8 +210,8 @@ public: // connect to it // wait for connected m_shell = new SshRemoteProcess("/bin/sh", - parameters.connectionOptions(SshSettings::sshFilePath()) << parameters.host(), - ProcessMode::Writer); + parameters.connectionOptions(SshSettings::sshFilePath()) << parameters.host()); + m_shell->setProcessMode(ProcessMode::Writer); m_shell->start(); const bool startOK = m_shell->waitForStarted(); if (!startOK) @@ -287,7 +288,7 @@ public: ~LinuxDevicePrivate(); CommandLine fullLocalCommandLine(const CommandLine &remoteCommand, - QtcProcess::TerminalMode terminalMode, + TerminalMode terminalMode, bool hasDisplay) const; bool setupShell(); bool runInShell(const CommandLine &cmd, const QByteArray &data = {}); @@ -317,30 +318,28 @@ LinuxDevice::LinuxDevice() }}); setOpenTerminal([this](const Environment &env, const FilePath &workingDir) { - DeviceProcess * const proc = createProcess(nullptr); - QObject::connect(proc, &DeviceProcess::finished, [proc] { + QtcProcess * const proc = createProcess(nullptr); + QObject::connect(proc, &QtcProcess::finished, [proc] { if (!proc->errorString().isEmpty()) { Core::MessageManager::writeDisrupting( tr("Error running remote shell: %1").arg(proc->errorString())); } proc->deleteLater(); }); - QObject::connect(proc, &DeviceProcess::errorOccurred, [proc] { + QObject::connect(proc, &QtcProcess::errorOccurred, [proc] { Core::MessageManager::writeDisrupting(tr("Error starting remote shell.")); proc->deleteLater(); }); - Runnable runnable; - runnable.device = sharedFromThis(); - runnable.environment = env; - runnable.workingDirectory = workingDir; // It seems we cannot pass an environment to OpenSSH dynamically // without specifying an executable. if (env.size() > 0) - runnable.command.setExecutable("/bin/sh"); + proc->setCommand({"/bin/sh", {}}); - proc->setRunInTerminal(true); - proc->start(runnable); + proc->setTerminalMode(TerminalMode::On); + proc->setEnvironment(env); + proc->setWorkingDirectory(workingDir); + proc->start(); }); if (Utils::HostOsInfo::isAnyUnixHost()) { @@ -360,7 +359,7 @@ IDeviceWidget *LinuxDevice::createWidget() return new GenericLinuxDeviceConfigurationWidget(sharedFromThis()); } -DeviceProcess *LinuxDevice::createProcess(QObject *parent) const +QtcProcess *LinuxDevice::createProcess(QObject *parent) const { return new LinuxDeviceProcess(sharedFromThis(), parent); } @@ -440,7 +439,7 @@ bool LinuxDevice::handlesFile(const FilePath &filePath) const } CommandLine LinuxDevicePrivate::fullLocalCommandLine(const CommandLine &remoteCommand, - QtcProcess::TerminalMode terminalMode, + TerminalMode terminalMode, bool hasDisplay) const { Utils::CommandLine cmd{SshSettings::sshFilePath()}; @@ -448,7 +447,7 @@ CommandLine LinuxDevicePrivate::fullLocalCommandLine(const CommandLine &remoteCo if (hasDisplay) cmd.addArg("-X"); - if (terminalMode != QtcProcess::TerminalOff) + if (terminalMode != TerminalMode::Off) cmd.addArg("-tt"); cmd.addArg("-q"); @@ -468,21 +467,7 @@ void LinuxDevice::runProcess(QtcProcess &process) const { QTC_ASSERT(!process.isRunning(), return); - Utils::Environment env = process.hasEnvironment() ? process.environment() - : Utils::Environment::systemEnvironment(); - const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0")); - if (SshSettings::askpassFilePath().exists()) { - env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput()); - - // OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform. - if (!env.hasKey("DISPLAY")) - env.set("DISPLAY", ":0"); - } - process.setEnvironment(env); - - // Otherwise, ssh will ignore SSH_ASKPASS and read from /dev/tty directly. - process.setDisableUnixTerminal(); - + const bool hasDisplay = SshRemoteProcess::setupSshEnvironment(&process); process.setCommand(d->fullLocalCommandLine(process.commandLine(), process.terminalMode(), hasDisplay)); process.start(); @@ -720,40 +705,6 @@ bool LinuxDevice::setPermissions(const Utils::FilePath &filePath, QFileDevice::P return d->runInShell({"chmod", {QString::number(flags, 16), filePath.path()}}); } -static void filterEntriesHelper(const FilePath &base, - const std::function<bool(const FilePath &)> &callBack, - const QStringList &entries, - const FileFilter &filter) -{ - const QList<QRegularExpression> nameRegexps = - transform(filter.nameFilters, [](const QString &filter) { - QRegularExpression re; - re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); - QTC_CHECK(re.isValid()); - return re; - }); - - const auto nameMatches = [&nameRegexps](const QString &fileName) { - for (const QRegularExpression &re : nameRegexps) { - const QRegularExpressionMatch match = re.match(fileName); - if (match.hasMatch()) - return true; - } - return nameRegexps.isEmpty(); - }; - - // FIXME: Handle filters. For now bark on unsupported options. - QTC_CHECK(filter.fileFilters == QDir::NoFilter); - QTC_CHECK(filter.iteratorFlags == QDirIterator::NoIteratorFlags); - - for (const QString &entry : entries) { - if (!nameMatches(entry)) - continue; - if (!callBack(base.pathAppended(entry))) - break; - } -} - void LinuxDevice::iterateDirectory(const FilePath &filePath, const std::function<bool(const FilePath &)> &callBack, const FileFilter &filter) const @@ -761,9 +712,8 @@ void LinuxDevice::iterateDirectory(const FilePath &filePath, QTC_ASSERT(handlesFile(filePath), return); // if we do not have find - use ls as fallback const QByteArray output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}}); - const QString out = QString::fromUtf8(output.data(), output.size()); - const QStringList entries = out.split('\n', Qt::SkipEmptyParts); - filterEntriesHelper(filePath, callBack, entries, filter); + const QStringList entries = QString::fromUtf8(output).split('\n', Qt::SkipEmptyParts); + FileUtils::iterateLsOutput(filePath, entries, filter, callBack); } QByteArray LinuxDevice::fileContents(const FilePath &filePath, qint64 limit, qint64 offset) const diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index d2906b230c3..2e55cd5bb85 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -47,7 +47,7 @@ public: ProjectExplorer::IDeviceWidget *createWidget() override; bool canCreateProcess() const override { return true; } - ProjectExplorer::DeviceProcess *createProcess(QObject *parent) const override; + Utils::QtcProcess *createProcess(QObject *parent) const override; bool canAutoDetectPorts() const override; ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override; bool canCreateProcessModel() const override { return true; } diff --git a/src/plugins/remotelinux/linuxdeviceprocess.cpp b/src/plugins/remotelinux/linuxdeviceprocess.cpp index 4953c8e5aba..4b3d2c39e99 100644 --- a/src/plugins/remotelinux/linuxdeviceprocess.cpp +++ b/src/plugins/remotelinux/linuxdeviceprocess.cpp @@ -40,10 +40,10 @@ LinuxDeviceProcess::LinuxDeviceProcess(const QSharedPointer<const ProjectExplore QObject *parent) : ProjectExplorer::SshDeviceProcess(device, parent) { - connect(this, &DeviceProcess::finished, this, [this]() { + connect(this, &QtcProcess::finished, this, [this]() { m_processId = 0; }); - connect(this, &DeviceProcess::started, this, [this]() { + connect(this, &QtcProcess::started, this, [this]() { m_pidParsed = false; m_output.clear(); }); @@ -52,7 +52,7 @@ LinuxDeviceProcess::LinuxDeviceProcess(const QSharedPointer<const ProjectExplore QByteArray LinuxDeviceProcess::readAllStandardOutput() { QByteArray output = SshDeviceProcess::readAllStandardOutput(); - if (m_pidParsed || runInTerminal()) + if (m_pidParsed || usesTerminal()) return output; m_output.append(output); @@ -79,7 +79,7 @@ qint64 LinuxDeviceProcess::processId() const return m_processId < 0 ? 0 : m_processId; } -QString LinuxDeviceProcess::fullCommandLine(const Runnable &runnable) const +QString LinuxDeviceProcess::fullCommandLine() const { CommandLine cmd; @@ -91,23 +91,22 @@ QString LinuxDeviceProcess::fullCommandLine(const Runnable &runnable) const cmd.addArgs(";", CommandLine::Raw); } - if (!runnable.workingDirectory.isEmpty()) { - cmd.addArgs({"cd", runnable.workingDirectory.path()}); + if (!workingDirectory().isEmpty()) { + cmd.addArgs({"cd", workingDirectory().path()}); cmd.addArgs("&&", CommandLine::Raw); } - if (!runInTerminal()) + if (!usesTerminal()) cmd.addArgs(QString("echo ") + pidMarker + "$$" + pidMarker + " && ", CommandLine::Raw); - const Environment &env = runnable.environment; + const Environment &env = remoteEnvironment(); for (auto it = env.constBegin(); it != env.constEnd(); ++it) cmd.addArgs(env.key(it) + "='" + env.expandedValueForKey(env.key(it)) + '\'', CommandLine::Raw); - if (!runInTerminal()) + if (!usesTerminal()) cmd.addArg("exec"); - cmd.addArg(runnable.command.executable().toString()); - cmd.addArgs(runnable.command.arguments(), CommandLine::Raw); + cmd.addCommandLineAsArgs(commandLine(), CommandLine::Raw); return cmd.arguments(); } diff --git a/src/plugins/remotelinux/linuxdeviceprocess.h b/src/plugins/remotelinux/linuxdeviceprocess.h index 2de4ceadf1e..2faefd2bdb1 100644 --- a/src/plugins/remotelinux/linuxdeviceprocess.h +++ b/src/plugins/remotelinux/linuxdeviceprocess.h @@ -29,8 +29,6 @@ #include <projectexplorer/devicesupport/sshdeviceprocess.h> -#include <QStringList> - namespace RemoteLinux { class REMOTELINUX_EXPORT LinuxDeviceProcess : public ProjectExplorer::SshDeviceProcess @@ -43,7 +41,7 @@ public: QByteArray readAllStandardOutput() override; private: - QString fullCommandLine(const ProjectExplorer::Runnable &) const override; + QString fullCommandLine() const override; qint64 processId() const override; QByteArray m_output; diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index 1353d91764f..641e250219d 100644 --- a/src/plugins/remotelinux/linuxdevicetester.cpp +++ b/src/plugins/remotelinux/linuxdevicetester.cpp @@ -55,7 +55,7 @@ public: SshRemoteProcessPtr process; DeviceUsedPortsGatherer portsGatherer; SftpTransferPtr sftpTransfer; - SshProcess rsyncProcess; + Utils::QtcProcess rsyncProcess; State state = Inactive; bool sftpWorks = false; }; @@ -67,6 +67,7 @@ using namespace Internal; GenericLinuxDeviceTester::GenericLinuxDeviceTester(QObject *parent) : DeviceTester(parent), d(new GenericLinuxDeviceTesterPrivate) { + SshRemoteProcess::setupSshEnvironment(&d->rsyncProcess); } GenericLinuxDeviceTester::~GenericLinuxDeviceTester() @@ -125,7 +126,7 @@ void GenericLinuxDeviceTester::handleConnected() QTC_ASSERT(d->state == Connecting, return); d->process = d->connection->createRemoteProcess("uname -rsm"); - connect(d->process.get(), &SshRemoteProcess::done, + connect(d->process.get(), &SshRemoteProcess::finished, this, &GenericLinuxDeviceTester::handleProcessFinished); emit progressMessage(tr("Checking kernel version...")); @@ -142,11 +143,11 @@ void GenericLinuxDeviceTester::handleConnectionFailure() setFinished(TestFailure); } -void GenericLinuxDeviceTester::handleProcessFinished(const QString &error) +void GenericLinuxDeviceTester::handleProcessFinished() { QTC_ASSERT(d->state == RunningUname, return); - if (!error.isEmpty() || d->process->exitCode() != 0) { + if (!d->process->errorString().isEmpty() || d->process->exitCode() != 0) { const QByteArray stderrOutput = d->process->readAllStandardError(); if (!stderrOutput.isEmpty()) emit errorMessage(tr("uname failed: %1").arg(QString::fromUtf8(stderrOutput)) + QLatin1Char('\n')); diff --git a/src/plugins/remotelinux/linuxdevicetester.h b/src/plugins/remotelinux/linuxdevicetester.h index 4cc6c7f9af1..c573623a90a 100644 --- a/src/plugins/remotelinux/linuxdevicetester.h +++ b/src/plugins/remotelinux/linuxdevicetester.h @@ -47,7 +47,7 @@ public: private: void handleConnected(); void handleConnectionFailure(); - void handleProcessFinished(const QString &error); + void handleProcessFinished(); void handlePortsGatheringError(const QString &message); void handlePortListReady(); void handleSftpStarted(); diff --git a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp index 7b7f59efd5c..15b8d0fe7bd 100644 --- a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp +++ b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.cpp @@ -87,7 +87,7 @@ void RemoteLinuxCustomCommandDeployService::doDeploy() this, &RemoteLinuxCustomCommandDeployService::handleStdout); connect(d->runner, &SshRemoteProcessRunner::readyReadStandardError, this, &RemoteLinuxCustomCommandDeployService::handleStderr); - connect(d->runner, &SshRemoteProcessRunner::processClosed, + connect(d->runner, &SshRemoteProcessRunner::finished, this, &RemoteLinuxCustomCommandDeployService::handleProcessClosed); emit progressMessage(tr("Starting remote command \"%1\"...").arg(d->commandLine)); @@ -115,15 +115,16 @@ void RemoteLinuxCustomCommandDeployService::handleStderr() emit stdErrData(QString::fromUtf8(d->runner->readAllStandardError())); } -void RemoteLinuxCustomCommandDeployService::handleProcessClosed(const QString &error) +void RemoteLinuxCustomCommandDeployService::handleProcessClosed() { + const QString error = d->runner->errorString(); QTC_ASSERT(d->state == Running, return); if (!error.isEmpty()) { emit errorMessage(tr("Remote process failed: %1").arg(error)); - } else if (d->runner->processExitCode() != 0) { + } else if (d->runner->exitCode() != 0) { emit errorMessage(tr("Remote process finished with exit code %1.") - .arg(d->runner->processExitCode())); + .arg(d->runner->exitCode())); } else { emit progressMessage(tr("Remote command finished successfully.")); } diff --git a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h index ee0ff0ebcc1..c6f07ff99c3 100644 --- a/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h +++ b/src/plugins/remotelinux/remotelinuxcustomcommanddeployservice.h @@ -50,7 +50,7 @@ protected: private: void handleStdout(); void handleStderr(); - void handleProcessClosed(const QString &error); + void handleProcessClosed(); Internal::RemoteLinuxCustomCommandDeployservicePrivate *d; }; diff --git a/src/plugins/remotelinux/remotelinuxenvironmentreader.cpp b/src/plugins/remotelinux/remotelinuxenvironmentreader.cpp index fd59e384317..a344f4fd65f 100644 --- a/src/plugins/remotelinux/remotelinuxenvironmentreader.cpp +++ b/src/plugins/remotelinux/remotelinuxenvironmentreader.cpp @@ -25,10 +25,10 @@ #include "remotelinuxenvironmentreader.h" -#include <projectexplorer/devicesupport/deviceprocess.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/runcontrol.h> +#include <utils/qtcprocess.h> #include <utils/stringutils.h> using namespace ProjectExplorer; @@ -54,13 +54,12 @@ void RemoteLinuxEnvironmentReader::start() } m_stop = false; m_deviceProcess = m_device->createProcess(this); - connect(m_deviceProcess, &DeviceProcess::errorOccurred, + connect(m_deviceProcess, &QtcProcess::errorOccurred, this, &RemoteLinuxEnvironmentReader::handleError); - connect(m_deviceProcess, &DeviceProcess::finished, + connect(m_deviceProcess, &QtcProcess::finished, this, &RemoteLinuxEnvironmentReader::remoteProcessFinished); - Runnable runnable; - runnable.command.setExecutable("env"); - m_deviceProcess->start(runnable); + m_deviceProcess->setCommand({"env", {}}); + m_deviceProcess->start(); } void RemoteLinuxEnvironmentReader::stop() diff --git a/src/plugins/remotelinux/remotelinuxenvironmentreader.h b/src/plugins/remotelinux/remotelinuxenvironmentreader.h index d16664e8708..6f92dcff1a8 100644 --- a/src/plugins/remotelinux/remotelinuxenvironmentreader.h +++ b/src/plugins/remotelinux/remotelinuxenvironmentreader.h @@ -30,8 +30,6 @@ #include <QObject> -namespace ProjectExplorer { class DeviceProcess; } - namespace RemoteLinux { namespace Internal { @@ -61,7 +59,7 @@ private: bool m_stop = false; Utils::Environment m_env; ProjectExplorer::IDevice::ConstPtr m_device; - ProjectExplorer::DeviceProcess *m_deviceProcess = nullptr; + Utils::QtcProcess *m_deviceProcess = nullptr; }; } // namespace Internal diff --git a/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp b/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp index e61a0bb7e9d..75a36ea2012 100644 --- a/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp +++ b/src/plugins/remotelinux/remotelinuxpackageinstaller.cpp @@ -71,7 +71,7 @@ void AbstractRemoteLinuxPackageInstaller::installPackage(const IDevice::ConstPtr this, &AbstractRemoteLinuxPackageInstaller::handleInstallerOutput); connect(d->installer, &SshRemoteProcessRunner::readyReadStandardError, this, &AbstractRemoteLinuxPackageInstaller::handleInstallerErrorOutput); - connect(d->installer, &SshRemoteProcessRunner::processClosed, + connect(d->installer, &SshRemoteProcessRunner::finished, this, &AbstractRemoteLinuxPackageInstaller::handleInstallationFinished); QString cmdLine = installCommandLine(packageFilePath); @@ -99,12 +99,13 @@ void AbstractRemoteLinuxPackageInstaller::handleConnectionError() setFinished(); } -void AbstractRemoteLinuxPackageInstaller::handleInstallationFinished(const QString &error) +void AbstractRemoteLinuxPackageInstaller::handleInstallationFinished() { + const QString error = d->installer->errorString(); if (!d->isRunning) return; - if (!error.isEmpty() || d->installer->processExitCode() != 0) + if (!error.isEmpty() || d->installer->exitCode() != 0) emit finished(tr("Installing package failed.")); else if (!errorString().isEmpty()) emit finished(errorString()); diff --git a/src/plugins/remotelinux/remotelinuxpackageinstaller.h b/src/plugins/remotelinux/remotelinuxpackageinstaller.h index ddf713eda28..3f4ce9d7ccb 100644 --- a/src/plugins/remotelinux/remotelinuxpackageinstaller.h +++ b/src/plugins/remotelinux/remotelinuxpackageinstaller.h @@ -54,7 +54,7 @@ protected: private: void handleConnectionError(); - void handleInstallationFinished(const QString &error); + void handleInstallationFinished(); void handleInstallerOutput(); void handleInstallerErrorOutput(); diff --git a/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp b/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp index 674371278f6..7b732e15490 100644 --- a/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp +++ b/src/plugins/remotelinux/remotelinuxqmltoolingsupport.cpp @@ -59,8 +59,8 @@ RemoteLinuxQmlToolingSupport::RemoteLinuxQmlToolingSupport(RunControl *runContro Runnable r = runControl->runnable(); r.command.addArg(QmlDebug::qmlDebugTcpArguments(services, serverUrl)); - - doStart(r, runControl->device()); + r.device = runControl->device(); + doStart(r); }); } diff --git a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp index cbe3b6f9d5e..115391d3045 100644 --- a/src/plugins/remotelinux/remotelinuxsignaloperation.cpp +++ b/src/plugins/remotelinux/remotelinuxsignaloperation.cpp @@ -53,7 +53,7 @@ void RemoteLinuxSignalOperation::run(const QString &command) { QTC_ASSERT(!m_runner, return); m_runner = new QSsh::SshRemoteProcessRunner(); - connect(m_runner, &QSsh::SshRemoteProcessRunner::processClosed, + connect(m_runner, &QSsh::SshRemoteProcessRunner::finished, this, &RemoteLinuxSignalOperation::runnerProcessFinished); connect(m_runner, &QSsh::SshRemoteProcessRunner::connectionError, this, &RemoteLinuxSignalOperation::runnerConnectionError); @@ -115,10 +115,10 @@ void RemoteLinuxSignalOperation::interruptProcess(const QString &filePath) void RemoteLinuxSignalOperation::runnerProcessFinished() { m_errorMessage.clear(); - if (m_runner->processExitStatus() != QProcess::NormalExit) { - m_errorMessage = m_runner->processErrorString(); - } else if (m_runner->processExitCode() != 0) { - m_errorMessage = tr("Exit code is %1. stderr:").arg(m_runner->processExitCode()) + if (m_runner->exitStatus() != QProcess::NormalExit) { + m_errorMessage = m_runner->errorString(); + } else if (m_runner->exitCode() != 0) { + m_errorMessage = tr("Exit code is %1. stderr:").arg(m_runner->exitCode()) + QLatin1Char(' ') + QString::fromLatin1(m_runner->readAllStandardError()); } diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 21981ba9626..49d032d26ad 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -48,7 +48,8 @@ class RsyncDeployService : public AbstractRemoteLinuxDeployService { Q_OBJECT public: - RsyncDeployService(QObject *parent = nullptr) : AbstractRemoteLinuxDeployService(parent) {} + RsyncDeployService(QObject *parent = nullptr) : AbstractRemoteLinuxDeployService(parent) + { SshRemoteProcess::setupSshEnvironment(&m_rsync); } void setDeployableFiles(const QList<DeployableFile> &files) { m_deployableFiles = files; } void setIgnoreMissingFiles(bool ignore) { m_ignoreMissingFiles = ignore; } @@ -69,7 +70,7 @@ private: mutable QList<DeployableFile> m_deployableFiles; bool m_ignoreMissingFiles = false; QString m_flags; - SshProcess m_rsync; + QtcProcess m_rsync; SshRemoteProcessPtr m_mkdir; }; @@ -102,8 +103,9 @@ void RsyncDeployService::createRemoteDirectories() remoteDirs.removeDuplicates(); m_mkdir = connection()->createRemoteProcess("mkdir -p " + ProcessArgs::createUnixArgs(remoteDirs).toString()); - connect(m_mkdir.get(), &SshRemoteProcess::done, this, [this](const QString &error) { + connect(m_mkdir.get(), &SshRemoteProcess::finished, this, [this] { QString userError; + const QString error = m_mkdir->errorString(); if (!error.isEmpty()) userError = error; if (m_mkdir->exitCode() != 0) diff --git a/src/plugins/remotelinux/sshkeydeployer.cpp b/src/plugins/remotelinux/sshkeydeployer.cpp index 9d5435ed4bf..d5a81799d9d 100644 --- a/src/plugins/remotelinux/sshkeydeployer.cpp +++ b/src/plugins/remotelinux/sshkeydeployer.cpp @@ -66,7 +66,7 @@ void SshKeyDeployer::deployPublicKey(const SshConnectionParameters &sshParams, connect(&d->deployProcess, &SshRemoteProcessRunner::connectionError, this, &SshKeyDeployer::handleConnectionFailure); - connect(&d->deployProcess, &SshRemoteProcessRunner::processClosed, + connect(&d->deployProcess, &SshRemoteProcessRunner::finished, this, &SshKeyDeployer::handleKeyUploadFinished); const QString command = "test -d .ssh " "|| mkdir -p ~/.ssh && chmod 0700 .ssh && echo '" @@ -83,8 +83,8 @@ void SshKeyDeployer::handleConnectionFailure() void SshKeyDeployer::handleKeyUploadFinished() { - const int exitCode = d->deployProcess.processExitCode(); - const QString errorMsg = d->deployProcess.processErrorString(); + const int exitCode = d->deployProcess.exitCode(); + const QString errorMsg = d->deployProcess.errorString(); cleanup(); if (errorMsg.isEmpty() && exitCode == 0) { emit finishedSuccessfully(); diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp index 75f35da4d8a..30070e7ec71 100644 --- a/src/plugins/resourceeditor/resourcenode.cpp +++ b/src/plugins/resourceeditor/resourcenode.cpp @@ -34,7 +34,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/threadutils.h> diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 2b92edb4e21..d1436cc0430 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -54,7 +54,6 @@ #include <utils/hostosinfo.h> #include <utils/icon.h> #include <utils/infobar.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/stringutils.h> #include <utils/theme/theme.h> @@ -586,7 +585,7 @@ void StudioWelcomePlugin::extensionsInitialized() const QString filters = QString("Project (*.qmlproject);;UI file (*.ui.qml);;QML file " "(*.qml);;JavaScript file (*.js);;%1") - .arg(Utils::allFilesFilterString()); + .arg(Core::DocumentManager::allFilesFilterString()); Core::DocumentManager::setFileDialogFilter(filters); } diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp index 01c4eae407f..3bc6804950c 100644 --- a/src/plugins/subversion/subversionclient.cpp +++ b/src/plugins/subversion/subversionclient.cpp @@ -94,7 +94,7 @@ bool SubversionClient::doCommit(const FilePath &repositoryRoot, QtcProcess proc; vcsSynchronousExec(proc, repositoryRoot, args << svnExtraOptions << escapeFiles(files), VcsCommand::ShowStdOut | VcsCommand::NoFullySync); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } void SubversionClient::commit(const FilePath &repositoryRoot, @@ -153,7 +153,7 @@ QString SubversionClient::synchronousTopic(const FilePath &repository) const svnVersionBinary.append(HostOsInfo::withExecutableSuffix("svnversion")); QtcProcess proc; vcsFullySynchronousExec(proc, repository, {FilePath::fromString(svnVersionBinary), args}); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return QString(); return proc.stdOut().trimmed(); diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index e65a1b88cb8..47b67b6362a 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -1030,7 +1030,7 @@ SubversionResponse SubversionPluginPrivate::runSvn(const FilePath &workingDir, QtcProcess proc; m_client->vcsFullySynchronousExec(proc, workingDir, arguments, flags, timeOutS, outputCodec); - response.error = proc.result() != QtcProcess::FinishedWithSuccess; + response.error = proc.result() != ProcessResult::FinishedWithSuccess; if (response.error) response.message = proc.exitMessage(); response.stdErr = proc.stdErr(); diff --git a/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp b/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp index ddf0d8a16a1..a12912f186d 100644 --- a/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp +++ b/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp @@ -86,7 +86,7 @@ DocumentContentCompletionProcessor::~DocumentContentCompletionProcessor() static void createProposal(QFutureInterface<QStringList> &future, const QString &text, const QString &wordUnderCursor) { - const QRegularExpression wordRE("([a-zA-Z_][a-zA-Z0-9_]{2,})"); + const QRegularExpression wordRE("([\\p{L}_][\\p{L}0-9_]{2,})"); QSet<QString> words; QRegularExpressionMatchIterator it = wordRE.globalMatch(text); diff --git a/src/plugins/texteditor/formattexteditor.cpp b/src/plugins/texteditor/formattexteditor.cpp index 830a8e8a992..e5109bbf682 100644 --- a/src/plugins/texteditor/formattexteditor.cpp +++ b/src/plugins/texteditor/formattexteditor.cpp @@ -92,7 +92,7 @@ static FormatTask format(FormatTask task) process.setTimeoutS(5); process.setCommand({FilePath::fromString(executable), options}); process.runBlocking(); - if (process.result() != QtcProcess::FinishedWithSuccess) { + if (process.result() != ProcessResult::FinishedWithSuccess) { task.error = QString(QT_TRANSLATE_NOOP("TextEditor", "Failed to format: %1.")) .arg(process.exitMessage()); return task; diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp index 5a7cb017055..47204ab8909 100644 --- a/src/plugins/texteditor/highlighter.cpp +++ b/src/plugins/texteditor/highlighter.cpp @@ -34,7 +34,7 @@ #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/stylehelper.h> diff --git a/src/plugins/texteditor/highlightersettings.cpp b/src/plugins/texteditor/highlightersettings.cpp index 75875249b7a..7691f39cbed 100644 --- a/src/plugins/texteditor/highlightersettings.cpp +++ b/src/plugins/texteditor/highlightersettings.cpp @@ -70,7 +70,7 @@ FilePath findFallbackDefinitionsLocation() process.setTimeoutS(5); process.setCommand({program, {"--prefix"}}); process.runBlocking(); - if (process.result() == QtcProcess::FinishedWithSuccess) { + if (process.result() == ProcessResult::FinishedWithSuccess) { QString output = process.stdOut(); output.remove('\n'); const FilePath dir = FilePath::fromString(output); diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index 6872c18c60a..690bd745e27 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -48,12 +48,8 @@ using namespace Utils; namespace TextEditor { -RefactoringChanges::RefactoringChanges() - : m_data(new RefactoringChangesData) -{} - RefactoringChanges::RefactoringChanges(RefactoringChangesData *data) - : m_data(data) + : m_data(data ? data : new RefactoringChangesData) {} RefactoringChanges::~RefactoringChanges() = default; diff --git a/src/plugins/texteditor/refactoringchanges.h b/src/plugins/texteditor/refactoringchanges.h index bce0e5d745f..714bdfd6835 100644 --- a/src/plugins/texteditor/refactoringchanges.h +++ b/src/plugins/texteditor/refactoringchanges.h @@ -122,7 +122,7 @@ class TEXTEDITOR_EXPORT RefactoringChanges public: using Range = Utils::ChangeSet::Range; - RefactoringChanges(); + explicit RefactoringChanges(RefactoringChangesData *data = nullptr); virtual ~RefactoringChanges(); static RefactoringFilePtr file(TextEditorWidget *editor); @@ -134,8 +134,6 @@ public: bool removeFile(const Utils::FilePath &filePath) const; protected: - explicit RefactoringChanges(RefactoringChangesData *data); - static TextEditorWidget *openEditor(const Utils::FilePath &filePath, bool activate, int line, diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 696e4caf0a2..e31f9044fbc 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -27,21 +27,21 @@ #include "extraencodingsettings.h" #include "fontsettings.h" -#include "textindenter.h" #include "storagesettings.h" #include "syntaxhighlighter.h" #include "tabsettings.h" #include "textdocumentlayout.h" #include "texteditor.h" #include "texteditorconstants.h" +#include "textindenter.h" #include "typingsettings.h" #include <coreplugin/diffservice.h> -#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/documentmodel.h> +#include <coreplugin/editormanager/editormanager.h> #include <extensionsystem/pluginmanager.h> -#include <utils/textutils.h> #include <utils/guard.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> +#include <utils/textutils.h> #include <QAction> #include <QApplication> diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index e0f1afe2d6f..1864fc4caba 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -79,7 +79,7 @@ #include <utils/fixedsizeclicklabel.h> #include <utils/hostosinfo.h> #include <utils/infobar.h> -#include <utils/mimetypes/mimedatabase.h> +#include <utils/mimeutils.h> #include <utils/multitextcursor.h> #include <utils/qtcassert.h> #include <utils/styledbar.h> @@ -642,6 +642,7 @@ public: void reconfigure(); void updateSyntaxInfoBar(const Highlighter::Definitions &definitions, const QString &fileName); + void removeSyntaxInfoBar(); void configureGenericHighlighter(const KSyntaxHighlighting::Definition &definition); void rememberCurrentSyntaxDefinition(); void openLinkUnderCursor(bool openInNextSplit); @@ -3270,6 +3271,13 @@ void TextEditorWidgetPrivate::updateSyntaxInfoBar(const Highlighter::Definitions } } +void TextEditorWidgetPrivate::removeSyntaxInfoBar() +{ + InfoBar *infoBar = m_document->infoBar(); + infoBar->removeInfo(Constants::INFO_MISSING_SYNTAX_DEFINITION); + infoBar->removeInfo(Constants::INFO_MULTIPLE_SYNTAX_DEFINITIONS); +} + void TextEditorWidgetPrivate::configureGenericHighlighter( const KSyntaxHighlighting::Definition &definition) { @@ -3962,7 +3970,7 @@ void TextEditorWidgetPrivate::updateLineAnnotation(const PaintEventData &data, return; TextMarks marks = Utils::filtered(blockUserData->marks(), [](const TextMark* mark){ - return !mark->lineAnnotation().isEmpty(); + return !mark->lineAnnotation().isEmpty() && mark->isVisible(); }); const bool annotationsVisible = !marks.isEmpty(); @@ -4007,24 +4015,21 @@ void TextEditorWidgetPrivate::updateLineAnnotation(const PaintEventData &data, } for (const TextMark *mark : qAsConst(marks)) { - if (!mark->isVisible()) - continue; boundingRect = QRectF(x, boundingRect.top(), q->viewport()->width() - x, boundingRect.height()); if (boundingRect.isEmpty()) break; - if (data.eventRect.intersects(boundingRect.toRect())) - mark->paintAnnotation(painter, &boundingRect, offset, itemOffset / 2, q->contentOffset()); + + mark->paintAnnotation(painter, + data.eventRect, + &boundingRect, + offset, + itemOffset / 2, + q->contentOffset()); x = boundingRect.right(); offset = itemOffset / 2; m_annotationRects[data.block.blockNumber()].append({boundingRect, mark}); } - - QRect updateRect(lineRect.toRect().topRight(), boundingRect.toRect().bottomRight()); - updateRect.setLeft(qBound(0, updateRect.left(), q->viewport()->width() - 1)); - updateRect.setRight(qBound(0, updateRect.right(), q->viewport()->width() - 1)); - if (!updateRect.isEmpty() && !data.eventRect.contains(q->viewport()->rect() & updateRect)) - q->viewport()->update(updateRect); } QColor blendRightMarginColor(const FontSettings &settings, bool areaColor) @@ -4237,9 +4242,6 @@ void TextEditorWidgetPrivate::paintCurrentLineHighlight(const PaintEventData &da lineRect.moveTop(lineRect.top() + blockRect.top()); lineRect.setLeft(0); lineRect.setRight(data.viewportRect.width()); - // set alpha, otherwise we cannot see block highlighting and find scope underneath - if (!data.eventRect.contains(lineRect.toAlignedRect())) - q->viewport()->update(lineRect.toAlignedRect()); painter.fillRect(lineRect, color); } } @@ -4624,8 +4626,8 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) d->paintAdditionalVisualWhitespaces(data, painter, blockData.boundingRect.top()); d->paintReplacement(data, painter, blockData.boundingRect.top()); + d->updateLineAnnotation(data, blockData, painter); } - d->updateLineAnnotation(data, blockData, painter); data.offset.ry() += blockData.boundingRect.height(); @@ -5104,25 +5106,28 @@ void TextEditorWidgetPrivate::updateCurrentLineHighlight() // the extra area shows information for the entire current block, not just the currentline. // This is why we must force a bigger update region. - QList<int> cursorBlockNumbers; const QPointF offset = q->contentOffset(); + auto updateBlock = [&](const QTextBlock &block) { + if (block.isValid() && block.isVisible()) { + QRect updateRect = q->blockBoundingGeometry(block).translated(offset).toAlignedRect(); + m_extraArea->update(updateRect); + updateRect.setLeft(0); + updateRect.setRight(q->viewport()->width()); + q->viewport()->update(updateRect); + } + }; + QList<int> cursorBlockNumbers; for (const QTextCursor &c : m_cursors) { int cursorBlockNumber = c.blockNumber(); - if (!m_cursorBlockNumbers.contains(cursorBlockNumber)) { - QTextBlock block = c.block(); - if (block.isValid() && block.isVisible()) - m_extraArea->update(q->blockBoundingGeometry(block).translated(offset).toAlignedRect()); - } + if (!m_cursorBlockNumbers.contains(cursorBlockNumber)) + updateBlock(c.block()); if (!cursorBlockNumbers.contains(c.blockNumber())) cursorBlockNumbers << c.blockNumber(); } if (m_cursorBlockNumbers != cursorBlockNumbers) { for (int oldBlock : m_cursorBlockNumbers) { - if (cursorBlockNumbers.contains(oldBlock)) - continue; - QTextBlock block = m_document->document()->findBlockByNumber(oldBlock); - if (block.isValid() && block.isVisible()) - m_extraArea->update(q->blockBoundingGeometry(block).translated(offset).toAlignedRect()); + if (!cursorBlockNumbers.contains(oldBlock)) + updateBlock(m_document->document()->findBlockByNumber(oldBlock)); } m_cursorBlockNumbers = cursorBlockNumbers; } @@ -8170,6 +8175,14 @@ void TextEditorWidget::configureGenericHighlighter() d->updateSyntaxInfoBar(definitions, textDocument()->filePath().fileName()); } +void TextEditorWidget::configureGenericHighlighter(const Utils::MimeType &mimeType) +{ + Highlighter::Definitions definitions = Highlighter::definitionsForMimeType(mimeType.name()); + d->configureGenericHighlighter(definitions.isEmpty() ? Highlighter::Definition() + : definitions.first()); + d->removeSyntaxInfoBar(); +} + int TextEditorWidget::blockNumberForVisibleRow(int row) const { QTextBlock block = blockForVisibleRow(row); diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 7daed6de22c..71759cb17b2 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -457,7 +457,11 @@ public: /// Abort code assistant if it is running. void abortAssist(); + /// Overwrite the current highlighter with a new generic highlighter based on the mimetype of + /// the current document void configureGenericHighlighter(); + /// Overwrite the current highlighter with a new generic highlighter based on the given mimetype + void configureGenericHighlighter(const Utils::MimeType &mimeType); Q_INVOKABLE void inSnippetMode(bool *active); // Used by FakeVim. diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp index 4fb7de00a25..5af06eb187b 100644 --- a/src/plugins/texteditor/texteditoroverlay.cpp +++ b/src/plugins/texteditor/texteditoroverlay.cpp @@ -312,7 +312,8 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co } void TextEditorOverlay::paintSelection(QPainter *painter, - const OverlaySelection &selection) + const OverlaySelection &selection, + const QRect &clip) { QTextCursor begin = selection.m_cursor_begin; @@ -325,7 +326,7 @@ void TextEditorOverlay::paintSelection(QPainter *painter, if (begin.isNull() || end.isNull() || begin.position() > end.position() || !bg.isValid()) return; - QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect()); + QPainterPath path = createSelectionPath(begin, end, clip); painter->save(); QColor penColor = fg; @@ -374,14 +375,15 @@ void TextEditorOverlay::paintSelection(QPainter *painter, void TextEditorOverlay::fillSelection(QPainter *painter, const OverlaySelection &selection, - const QColor &color) + const QColor &color, + const QRect &clip) { const QTextCursor &begin = selection.m_cursor_begin; const QTextCursor &end= selection.m_cursor_end; if (begin.isNull() || end.isNull() || begin.position() > end.position()) return; - QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect()); + QPainterPath path = createSelectionPath(begin, end, clip); painter->save(); painter->translate(-.5, -.5); @@ -402,7 +404,7 @@ void TextEditorOverlay::paint(QPainter *painter, const QRect &clip) != selection.m_fixedLength) continue; - paintSelection(painter, selection); + paintSelection(painter, selection, clip); } for (int i = m_selections.size()-1; i >= 0; --i) { const OverlaySelection &selection = m_selections.at(i); @@ -413,7 +415,7 @@ void TextEditorOverlay::paint(QPainter *painter, const QRect &clip) != selection.m_fixedLength) continue; - paintSelection(painter, selection); + paintSelection(painter, selection, clip); } } @@ -444,7 +446,7 @@ void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect != selection.m_fixedLength) continue; - fillSelection(painter, selection, color); + fillSelection(painter, selection, color, clip); } for (int i = m_selections.size()-1; i >= 0; --i) { const OverlaySelection &selection = m_selections.at(i); @@ -455,7 +457,7 @@ void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect != selection.m_fixedLength) continue; - fillSelection(painter, selection, color); + fillSelection(painter, selection, color, clip); } } diff --git a/src/plugins/texteditor/texteditoroverlay.h b/src/plugins/texteditor/texteditoroverlay.h index d87a0fe7aab..4c3811b8950 100644 --- a/src/plugins/texteditor/texteditoroverlay.h +++ b/src/plugins/texteditor/texteditoroverlay.h @@ -100,8 +100,8 @@ protected: private: QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip); - void paintSelection(QPainter *painter, const OverlaySelection &selection); - void fillSelection(QPainter *painter, const OverlaySelection &selection, const QColor &color); + void paintSelection(QPainter *painter, const OverlaySelection &selection, const QRect &clip); + void fillSelection(QPainter *painter, const OverlaySelection &selection, const QColor &color, const QRect &clip); bool m_visible; bool m_alpha; diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp index 43aaf3e54b5..09a373f6a27 100644 --- a/src/plugins/texteditor/textmark.cpp +++ b/src/plugins/texteditor/textmark.cpp @@ -133,23 +133,35 @@ void TextMark::paintIcon(QPainter *painter, const QRect &rect) const icon().paint(painter, rect, Qt::AlignCenter); } -void TextMark::paintAnnotation(QPainter &painter, QRectF *annotationRect, - const qreal fadeInOffset, const qreal fadeOutOffset, +void TextMark::paintAnnotation(QPainter &painter, + const QRect &eventRect, + QRectF *annotationRect, + const qreal fadeInOffset, + const qreal fadeOutOffset, const QPointF &contentOffset) const { QString text = lineAnnotation(); if (text.isEmpty()) return; - const AnnotationRects &rects = annotationRects(*annotationRect, painter.fontMetrics(), - fadeInOffset, fadeOutOffset); + const AnnotationRects &rects = annotationRects(*annotationRect, + painter.fontMetrics(), + fadeInOffset, + fadeOutOffset); + annotationRect->setRight(rects.fadeOutRect.right()); + const QRectF eventRectF(eventRect); + if (!(rects.fadeInRect.intersects(eventRectF) || rects.annotationRect.intersects(eventRectF) + || rects.fadeOutRect.intersects(eventRectF))) { + return; + } + const QColor &markColor = m_color.has_value() ? Utils::creatorTheme()->color(m_color.value()).toHsl() : painter.pen().color(); const FontSettings &fontSettings = m_baseTextDocument->fontSettings(); const AnnotationColors &colors = AnnotationColors::getAnnotationColors( - markColor, fontSettings.toTextCharFormat(C_TEXT).background().color()); + markColor, fontSettings.toTextCharFormat(C_TEXT).background().color()); painter.save(); QLinearGradient grad(rects.fadeInRect.topLeft() - contentOffset, @@ -169,7 +181,6 @@ void TextMark::paintAnnotation(QPainter &painter, QRectF *annotationRect, painter.fillRect(rects.fadeOutRect, grad); } painter.restore(); - annotationRect->setRight(rects.fadeOutRect.right()); } TextMark::AnnotationRects TextMark::annotationRects(const QRectF &boundingRect, @@ -348,11 +359,13 @@ void TextMark::setIcon(const QIcon &icon) { m_icon = icon; m_iconProvider = std::function<QIcon()>(); + updateMarker(); } void TextMark::setIconProvider(const std::function<QIcon ()> &iconProvider) { m_iconProvider = iconProvider; + updateMarker(); } const QIcon TextMark::icon() const @@ -368,6 +381,13 @@ Utils::optional<Theme::Color> TextMark::color() const void TextMark::setColor(const Theme::Color &color) { m_color = color; + updateMarker(); +} + +void TextMark::setLineAnnotation(const QString &lineAnnotation) +{ + m_lineAnnotation = lineAnnotation; + updateMarker(); } void TextMark::setToolTipProvider(const std::function<QString()> &toolTipProvider) diff --git a/src/plugins/texteditor/textmark.h b/src/plugins/texteditor/textmark.h index fe999755e3c..2c69592aeed 100644 --- a/src/plugins/texteditor/textmark.h +++ b/src/plugins/texteditor/textmark.h @@ -72,8 +72,11 @@ public: int lineNumber() const; virtual void paintIcon(QPainter *painter, const QRect &rect) const; - virtual void paintAnnotation(QPainter &painter, QRectF *annotationRect, - const qreal fadeInOffset, const qreal fadeOutOffset, + virtual void paintAnnotation(QPainter &painter, + const QRect &eventRect, + QRectF *annotationRect, + const qreal fadeInOffset, + const qreal fadeOutOffset, const QPointF &contentOffset) const; struct AnnotationRects { @@ -102,7 +105,6 @@ public: void setIcon(const QIcon &icon); void setIconProvider(const std::function<QIcon()> &iconProvider); const QIcon icon() const; - // call this if the icon has changed. void updateMarker(); Priority priority() const { return m_priority;} void setPriority(Priority prioriy); @@ -122,7 +124,7 @@ public: void setBaseTextDocument(TextDocument *baseTextDocument) { m_baseTextDocument = baseTextDocument; } QString lineAnnotation() const { return m_lineAnnotation; } - void setLineAnnotation(const QString &lineAnnotation) { m_lineAnnotation = lineAnnotation; } + void setLineAnnotation(const QString &lineAnnotation); QString toolTip() const; void setToolTip(const QString &toolTip); diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp index e2271ad396f..55cc5589ce6 100644 --- a/src/plugins/updateinfo/updateinfoplugin.cpp +++ b/src/plugins/updateinfo/updateinfoplugin.cpp @@ -145,7 +145,7 @@ void UpdateInfoPlugin::startCheckForUpdates() 60 * 3, // 3 minutes timeout /*workingDirectory=*/{}, [](int /*exitCode*/) { - return Utils::QtcProcess::FinishedWithSuccess; + return Utils::ProcessResult::FinishedWithSuccess; }); if (d->m_settings.checkForQtVersions) { d->m_checkUpdatesCommand @@ -153,7 +153,7 @@ void UpdateInfoPlugin::startCheckForUpdates() {"se", "qt[.]qt[0-9][.][0-9]+$", "-g", "*=false,ifw.package.*=true"}}, 60 * 3, // 3 minutes timeout /*workingDirectory=*/{}, - [](int /*exitCode*/) { return Utils::QtcProcess::FinishedWithSuccess; }); + [](int /*exitCode*/) { return Utils::ProcessResult::FinishedWithSuccess; }); } d->m_checkUpdatesCommand->execute(); d->m_progress = d->m_checkUpdatesCommand->futureProgress(); diff --git a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp b/src/plugins/valgrind/callgrind/callgrindcontroller.cpp index 83f98cc71bb..d2c8a3c6155 100644 --- a/src/plugins/valgrind/callgrind/callgrindcontroller.cpp +++ b/src/plugins/valgrind/callgrind/callgrindcontroller.cpp @@ -117,20 +117,17 @@ void CallgrindController::run(Option option) #if CALLGRIND_CONTROL_DEBUG m_controllerProcess->setProcessChannelMode(QProcess::ForwardedChannels); #endif - connect(m_controllerProcess, &ApplicationLauncher::processExited, + connect(m_controllerProcess, &ApplicationLauncher::finished, this, &CallgrindController::controllerProcessFinished); - connect(m_controllerProcess, &ApplicationLauncher::error, + connect(m_controllerProcess, &ApplicationLauncher::errorOccurred, this, &CallgrindController::handleControllerProcessError); Runnable controller = m_valgrindRunnable; controller.command.setExecutable(FilePath::fromString(CALLGRIND_CONTROL_BINARY)); controller.command.setArguments(QString("%1 %2").arg(toOptionString(option)).arg(m_pid)); - - if (!m_valgrindRunnable.device - || m_valgrindRunnable.device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) - m_controllerProcess->start(controller); - else - m_controllerProcess->start(controller, m_valgrindRunnable.device); + controller.device = m_valgrindRunnable.device; + m_controllerProcess->setRunnable(controller); + m_controllerProcess->start(); } void CallgrindController::setValgrindPid(qint64 pid) @@ -148,7 +145,7 @@ void CallgrindController::handleControllerProcessError(QProcess::ProcessError) m_controllerProcess = nullptr; } -void CallgrindController::controllerProcessFinished(int rc, QProcess::ExitStatus status) +void CallgrindController::controllerProcessFinished() { QTC_ASSERT(m_controllerProcess, return); const QString error = m_controllerProcess->errorString(); @@ -156,7 +153,7 @@ void CallgrindController::controllerProcessFinished(int rc, QProcess::ExitStatus m_controllerProcess->deleteLater(); // Called directly from finished() signal in m_process m_controllerProcess = nullptr; - if (rc != 0 || status != QProcess::NormalExit) { + if (m_controllerProcess->exitCode() != 0 || m_controllerProcess->exitStatus() != QProcess::NormalExit) { qWarning() << "Controller exited abnormally:" << error; return; } diff --git a/src/plugins/valgrind/callgrind/callgrindcontroller.h b/src/plugins/valgrind/callgrind/callgrindcontroller.h index 848c2ba2b62..6f0b7af2716 100644 --- a/src/plugins/valgrind/callgrind/callgrindcontroller.h +++ b/src/plugins/valgrind/callgrind/callgrindcontroller.h @@ -76,7 +76,7 @@ private: void sftpJobFinished(QSsh::SftpJobId job, const QString &error); void cleanupTempFile(); - void controllerProcessFinished(int, QProcess::ExitStatus); + void controllerProcessFinished(); void controllerProcessError(QProcess::ProcessError); ProjectExplorer::ApplicationLauncher *m_controllerProcess = nullptr; diff --git a/src/plugins/valgrind/valgrindrunner.cpp b/src/plugins/valgrind/valgrindrunner.cpp index 95cfcc0faf5..ccffd459106 100644 --- a/src/plugins/valgrind/valgrindrunner.cpp +++ b/src/plugins/valgrind/valgrindrunner.cpp @@ -114,11 +114,11 @@ bool ValgrindRunner::Private::run() // consider appending our options last so they override any interfering user-supplied options // -q as suggested by valgrind manual - connect(&m_valgrindProcess, &ApplicationLauncher::processExited, + connect(&m_valgrindProcess, &ApplicationLauncher::finished, q, &ValgrindRunner::processFinished); - connect(&m_valgrindProcess, &ApplicationLauncher::processStarted, + connect(&m_valgrindProcess, &ApplicationLauncher::started, this, &ValgrindRunner::Private::processStarted); - connect(&m_valgrindProcess, &ApplicationLauncher::error, + connect(&m_valgrindProcess, &ApplicationLauncher::errorOccurred, q, &ValgrindRunner::processError); connect(&m_valgrindProcess, &ApplicationLauncher::appendMessage, q, &ValgrindRunner::processOutputReceived); @@ -135,17 +135,11 @@ bool ValgrindRunner::Private::run() valgrind.command = cmd; valgrind.workingDirectory = m_debuggee.workingDirectory; valgrind.environment = m_debuggee.environment; - valgrind.device = m_device; - - if (m_device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { - m_valgrindProcess.start(valgrind); - } else if (m_device->type() == "DockerDeviceType") { - valgrind.device = {}; - m_valgrindProcess.start(valgrind); - } else { - m_valgrindProcess.start(valgrind, m_device); - } + if (m_device->type() != "DockerDeviceType") + valgrind.device = m_device; + m_valgrindProcess.setRunnable(valgrind); + m_valgrindProcess.start(); return true; } @@ -189,11 +183,13 @@ void ValgrindRunner::Private::remoteProcessStarted() " | awk '\\$5 ~ /^%3/" // 5th column must start with valgrind process " {print \\$1;}'" // print 1st then (with PID) "\"").arg(proc, m_debuggee.command.executable().fileName(), procEscaped)); + findPid.device = m_device; // m_remote.m_findPID = m_remote.m_connection->createRemoteProcess(cmd.toUtf8()); connect(&m_findPID, &ApplicationLauncher::appendMessage, this, &ValgrindRunner::Private::findPidOutputReceived); - m_findPID.start(findPid, m_device); + m_findPID.setRunnable(findPid); + m_findPID.start(); } void ValgrindRunner::Private::findPidOutputReceived(const QString &out, Utils::OutputFormat format) @@ -291,7 +287,7 @@ void ValgrindRunner::processError(QProcess::ProcessError e) emit finished(); } -void ValgrindRunner::processFinished(int ret, QProcess::ExitStatus status) +void ValgrindRunner::processFinished() { emit extraProcessFinished(); @@ -303,8 +299,8 @@ void ValgrindRunner::processFinished(int ret, QProcess::ExitStatus status) // make sure we don't wait for the connection anymore emit finished(); - if (ret != 0 || status == QProcess::CrashExit) - emit processErrorReceived(errorString(), d->m_valgrindProcess.processError()); + if (d->m_valgrindProcess.exitCode() != 0 || d->m_valgrindProcess.exitStatus() == QProcess::CrashExit) + emit processErrorReceived(errorString(), d->m_valgrindProcess.error()); } QString ValgrindRunner::errorString() const diff --git a/src/plugins/valgrind/valgrindrunner.h b/src/plugins/valgrind/valgrindrunner.h index 647cac8bae8..1e2e83fd464 100644 --- a/src/plugins/valgrind/valgrindrunner.h +++ b/src/plugins/valgrind/valgrindrunner.h @@ -72,7 +72,7 @@ signals: private: bool startServers(); void processError(QProcess::ProcessError); - void processFinished(int, QProcess::ExitStatus); + void processFinished(); void xmlSocketConnected(); void logSocketConnected(); diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp index 9339d2ffd68..8fc4117ce5b 100644 --- a/src/plugins/vcsbase/vcsbaseclient.cpp +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -258,7 +258,7 @@ bool VcsBaseClient::synchronousCreateRepository(const FilePath &workingDirectory args << extraOptions; QtcProcess proc; vcsFullySynchronousExec(proc, workingDirectory, args); - if (proc.result() != QtcProcess::FinishedWithSuccess) + if (proc.result() != ProcessResult::FinishedWithSuccess) return false; VcsOutputWindow::append(proc.stdOut()); @@ -279,7 +279,7 @@ bool VcsBaseClient::synchronousClone(const FilePath &workingDir, QtcProcess proc; vcsFullySynchronousExec(proc, workingDir, args); resetCachedVcsInfo(workingDir); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } bool VcsBaseClient::synchronousAdd(const FilePath &workingDir, @@ -290,7 +290,7 @@ bool VcsBaseClient::synchronousAdd(const FilePath &workingDir, args << vcsCommandString(AddCommand) << extraOptions << relFileName; QtcProcess proc; vcsFullySynchronousExec(proc, workingDir, args); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } bool VcsBaseClient::synchronousRemove(const FilePath &workingDir, @@ -301,7 +301,7 @@ bool VcsBaseClient::synchronousRemove(const FilePath &workingDir, args << vcsCommandString(RemoveCommand) << extraOptions << filename; QtcProcess proc; vcsFullySynchronousExec(proc, workingDir, args); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } bool VcsBaseClient::synchronousMove(const FilePath &workingDir, @@ -313,7 +313,7 @@ bool VcsBaseClient::synchronousMove(const FilePath &workingDir, args << vcsCommandString(MoveCommand) << extraOptions << from << to; QtcProcess proc; vcsFullySynchronousExec(proc, workingDir, args); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } bool VcsBaseClient::synchronousPull(const FilePath &workingDir, @@ -329,7 +329,7 @@ bool VcsBaseClient::synchronousPull(const FilePath &workingDir, | VcsCommand::ShowSuccessMessage; QtcProcess proc; vcsSynchronousExec(proc, workingDir, args, flags); - const bool ok = proc.result() == QtcProcess::FinishedWithSuccess; + const bool ok = proc.result() == ProcessResult::FinishedWithSuccess; if (ok) emit changed(QVariant(workingDir.toString())); return ok; @@ -348,7 +348,7 @@ bool VcsBaseClient::synchronousPush(const FilePath &workingDir, | VcsCommand::ShowSuccessMessage; QtcProcess proc; vcsSynchronousExec(proc, workingDir, args, flags); - return proc.result() == QtcProcess::FinishedWithSuccess; + return proc.result() == ProcessResult::FinishedWithSuccess; } VcsBaseEditorWidget *VcsBaseClient::annotate( diff --git a/src/plugins/vcsbase/vcsbaseclient.h b/src/plugins/vcsbase/vcsbaseclient.h index 7abc9a03f33..6a5f6e46d05 100644 --- a/src/plugins/vcsbase/vcsbaseclient.h +++ b/src/plugins/vcsbase/vcsbaseclient.h @@ -31,7 +31,7 @@ #include <utils/fileutils.h> #include <utils/id.h> -#include <utils/qtcprocess.h> +#include <utils/processenums.h> #include <QObject> #include <QStringList> @@ -41,9 +41,12 @@ QT_BEGIN_NAMESPACE class QFileInfo; +class QTextCodec; class QToolBar; QT_END_NAMESPACE +namespace Utils { class QtcProcess; } + namespace VcsBase { class VcsCommand; diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp index 3448af8e16c..577da8cdc94 100644 --- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp +++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp @@ -31,6 +31,7 @@ #include <coreplugin/progressmanager/progressmanager.h> #include <diffeditor/diffutils.h> +#include <utils/commandline.h> #include <utils/environment.h> #include <utils/qtcassert.h> #include <utils/runextensions.h> diff --git a/src/plugins/vcsbase/wizard/vcscommandpage.cpp b/src/plugins/vcsbase/wizard/vcscommandpage.cpp index 5da40d2cd03..f3297feceb4 100644 --- a/src/plugins/vcsbase/wizard/vcscommandpage.cpp +++ b/src/plugins/vcsbase/wizard/vcscommandpage.cpp @@ -31,6 +31,7 @@ #include <projectexplorer/jsonwizard/jsonwizard.h> #include <utils/algorithm.h> +#include <utils/commandline.h> #include <utils/qtcassert.h> #include <QDir> diff --git a/src/plugins/webassembly/webassemblydevice.cpp b/src/plugins/webassembly/webassemblydevice.cpp index ae9fb02ae3e..43d06ae5546 100644 --- a/src/plugins/webassembly/webassemblydevice.cpp +++ b/src/plugins/webassembly/webassemblydevice.cpp @@ -26,7 +26,6 @@ #include "webassemblyconstants.h" #include "webassemblydevice.h" -#include <projectexplorer/devicesupport/deviceprocess.h> #include <projectexplorer/runcontrol.h> using namespace ProjectExplorer; diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.cpp b/src/plugins/webassembly/webassemblyrunconfiguration.cpp index f06da53c093..54f6d5cb47e 100644 --- a/src/plugins/webassembly/webassemblyrunconfiguration.cpp +++ b/src/plugins/webassembly/webassemblyrunconfiguration.cpp @@ -124,7 +124,7 @@ public: r.command = emrunCommand(runControl->runConfiguration(), browserId, QString::number(portsGatherer->findEndPoint().port())); - SimpleTargetRunner::doStart(r, {}); + SimpleTargetRunner::doStart(r); }); } }; diff --git a/src/plugins/webassembly/webassemblytoolchain.cpp b/src/plugins/webassembly/webassemblytoolchain.cpp index 780e48e165f..bebf2866fb9 100644 --- a/src/plugins/webassembly/webassemblytoolchain.cpp +++ b/src/plugins/webassembly/webassemblytoolchain.cpp @@ -111,6 +111,43 @@ const QVersionNumber &WebAssemblyToolChain::minimumSupportedEmSdkVersion() return number; } +static Toolchains doAutoDetect(const ToolchainDetector &detector) +{ + const FilePath sdk = WebAssemblyEmSdk::registeredEmSdk(); + if (!WebAssemblyEmSdk::isValid(sdk)) + return {}; + + if (detector.device) { + // Only detect toolchains from the emsdk installation device + const FilePath deviceRoot = detector.device->mapToGlobalPath({}); + if (deviceRoot.host() != sdk.host()) + return {}; + } + + Environment env = sdk.deviceEnvironment(); + WebAssemblyEmSdk::addToEnvironment(sdk, env); + + Toolchains result; + for (auto languageId : {ProjectExplorer::Constants::C_LANGUAGE_ID, + ProjectExplorer::Constants::CXX_LANGUAGE_ID}) { + auto toolChain = new WebAssemblyToolChain; + toolChain->setLanguage(languageId); + toolChain->setDetection(ToolChain::AutoDetection); + const bool cLanguage = languageId == ProjectExplorer::Constants::C_LANGUAGE_ID; + const QString script = QLatin1String(cLanguage ? "emcc" : "em++") + + QLatin1String(sdk.osType() == OsTypeWindows ? ".bat" : ""); + const FilePath scriptFile = sdk.withNewPath(script).searchInDirectories(env.path()); + toolChain->setCompilerCommand(scriptFile); + + const QString displayName = WebAssemblyToolChain::tr("Emscripten Compiler %1 for %2") + .arg(toolChain->version(), QLatin1String(cLanguage ? "C" : "C++")); + toolChain->setDisplayName(displayName); + result.append(toolChain); + } + + return result; +} + void WebAssemblyToolChain::registerToolChains() { // Remove old toolchains @@ -121,12 +158,9 @@ void WebAssemblyToolChain::registerToolChains() }; // Create new toolchains and register them - ToolChainFactory *factory = - findOrDefault(ToolChainFactory::allToolChainFactories(), [](ToolChainFactory *f){ - return f->supportedToolChainType() == Constants::WEBASSEMBLY_TOOLCHAIN_TYPEID; - }); - QTC_ASSERT(factory, return); - for (auto toolChain : factory->autoDetect(ToolchainDetector({}, {}))) + ToolchainDetector detector({}, {}); + const Toolchains toolchains = doAutoDetect(detector); + for (auto toolChain : toolchains) ToolChainManager::registerToolChain(toolChain); // Let kits pick up the new toolchains @@ -157,39 +191,7 @@ WebAssemblyToolChainFactory::WebAssemblyToolChainFactory() Toolchains WebAssemblyToolChainFactory::autoDetect(const ToolchainDetector &detector) const { - const FilePath sdk = WebAssemblyEmSdk::registeredEmSdk(); - if (!WebAssemblyEmSdk::isValid(sdk)) - return {}; - - if (detector.device) { - // Only detect toolchains from the emsdk installation device - const FilePath deviceRoot = detector.device->mapToGlobalPath({}); - if (deviceRoot.host() != sdk.host()) - return {}; - } - - Environment env = sdk.deviceEnvironment(); - WebAssemblyEmSdk::addToEnvironment(sdk, env); - - Toolchains result; - for (auto languageId : {ProjectExplorer::Constants::C_LANGUAGE_ID, - ProjectExplorer::Constants::CXX_LANGUAGE_ID}) { - auto toolChain = new WebAssemblyToolChain; - toolChain->setLanguage(languageId); - toolChain->setDetection(ToolChain::AutoDetection); - const bool cLanguage = languageId == ProjectExplorer::Constants::C_LANGUAGE_ID; - const QString script = QLatin1String(cLanguage ? "emcc" : "em++") - + QLatin1String(sdk.osType() == OsTypeWindows ? ".bat" : ""); - const FilePath scriptFile = sdk.withNewPath(script).searchInDirectories(env.path()); - toolChain->setCompilerCommand(scriptFile); - - const QString displayName = WebAssemblyToolChain::tr("Emscripten Compiler %1 for %2") - .arg(toolChain->version(), QLatin1String(cLanguage ? "C" : "C++")); - toolChain->setDisplayName(displayName); - result.append(toolChain); - } - - return result; + return doAutoDetect(detector); } } // namespace Internal diff --git a/src/plugins/webassembly/webassemblytoolchain.h b/src/plugins/webassembly/webassemblytoolchain.h index 41eeb72e94b..95291af7f41 100644 --- a/src/plugins/webassembly/webassemblytoolchain.h +++ b/src/plugins/webassembly/webassemblytoolchain.h @@ -37,6 +37,8 @@ class WebAssemblyToolChain final : public ProjectExplorer::GccToolChain Q_DECLARE_TR_FUNCTIONS(WebAssembly::Internal::WebAssemblyToolChain) public: + WebAssemblyToolChain(); + void addToEnvironment(Utils::Environment &env) const override; Utils::FilePath makeCommand(const Utils::Environment &environment) const override; @@ -45,11 +47,6 @@ public: static const QVersionNumber &minimumSupportedEmSdkVersion(); static void registerToolChains(); static bool areToolChainsRegistered(); - -private: - WebAssemblyToolChain(); - - friend class WebAssemblyToolChainFactory; }; class WebAssemblyToolChainFactory : public ProjectExplorer::ToolChainFactory diff --git a/src/plugins/welcome/introductionwidget.cpp b/src/plugins/welcome/introductionwidget.cpp index 6fc31990d20..f6b5d82c916 100644 --- a/src/plugins/welcome/introductionwidget.cpp +++ b/src/plugins/welcome/introductionwidget.cpp @@ -143,7 +143,7 @@ IntroductionWidget::IntroductionWidget(QWidget *parent) "<li>click on the magnifier icon for a complete list of possible options</li>" "</ul>")}, {QLatin1String("OutputPaneButtons"), - tr("Output Panes"), + tr("Output"), tr("Find compile and application output here, " "as well as a list of configuration and build issues, " "and the panel for global searches."), @@ -155,7 +155,7 @@ IntroductionWidget::IntroductionWidget(QWidget *parent) {{}, tr("Escape to Editor"), tr("Pressing the Escape key brings you back to the editor. Press it " - "multiple times to also hide output panes and context help, giving the editor more " + "multiple times to also hide context help and output, giving the editor more " "space."), {}}, {{}, diff --git a/src/plugins/winrt/CMakeLists.txt b/src/plugins/winrt/CMakeLists.txt deleted file mode 100644 index 693b5446121..00000000000 --- a/src/plugins/winrt/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -add_qtc_plugin(WinRt - PLUGIN_DEPENDS Core Debugger ProjectExplorer QtSupport - SOURCES - winrt.qrc - winrtconstants.h - winrtdebugsupport.cpp winrtdebugsupport.h - winrtdeployconfiguration.cpp winrtdeployconfiguration.h - winrtdevice.cpp winrtdevice.h - winrtpackagedeploymentstep.cpp winrtpackagedeploymentstep.h - winrtphoneqtversion.cpp winrtphoneqtversion.h - winrtplugin.cpp winrtplugin.h - winrtqtversion.cpp winrtqtversion.h - winrtrunconfiguration.cpp winrtrunconfiguration.h - winrtruncontrol.cpp winrtruncontrol.h - winrtrunnerhelper.cpp winrtrunnerhelper.h -) diff --git a/src/plugins/winrt/WinRt.json.in b/src/plugins/winrt/WinRt.json.in deleted file mode 100644 index e776ddf9616..00000000000 --- a/src/plugins/winrt/WinRt.json.in +++ /dev/null @@ -1,20 +0,0 @@ -{ - \"Name\" : \"WinRt\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Platform\" : \"Windows (8.1|10|11)\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\" - ], - \"Category\" : \"Device Support\", - \"Description\" : \"Helper for Windows Runtime projects.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList -} diff --git a/src/plugins/winrt/images/winrtdevice.png b/src/plugins/winrt/images/winrtdevice.png Binary files differdeleted file mode 100644 index 2b45adb69a1..00000000000 --- a/src/plugins/winrt/images/winrtdevice.png +++ /dev/null diff --git a/src/plugins/winrt/images/[email protected] b/src/plugins/winrt/images/[email protected] Binary files differdeleted file mode 100644 index a57f2b49783..00000000000 --- a/src/plugins/winrt/images/[email protected] +++ /dev/null diff --git a/src/plugins/winrt/images/winrtdevicesmall.png b/src/plugins/winrt/images/winrtdevicesmall.png Binary files differdeleted file mode 100644 index 42749765745..00000000000 --- a/src/plugins/winrt/images/winrtdevicesmall.png +++ /dev/null diff --git a/src/plugins/winrt/images/[email protected] b/src/plugins/winrt/images/[email protected] Binary files differdeleted file mode 100644 index 87f90b29ec5..00000000000 --- a/src/plugins/winrt/images/[email protected] +++ /dev/null diff --git a/src/plugins/winrt/winrt.qbs b/src/plugins/winrt/winrt.qbs deleted file mode 100644 index f511a9c31a2..00000000000 --- a/src/plugins/winrt/winrt.qbs +++ /dev/null @@ -1,38 +0,0 @@ -import qbs 1.0 - -QtcPlugin { - name: "WinRt" - - Depends { name: "Core" } - Depends { name: "Debugger" } - Depends { name: "ProjectExplorer" } - Depends { name: "QtSupport" } - Depends { name: "QmakeProjectManager" } - Depends { name: "Qt.gui" } - Depends { name: "app_version_header" } - - files: [ - "winrt.qrc", - "winrtconstants.h", - "winrtdebugsupport.cpp", - "winrtdebugsupport.h", - "winrtdeployconfiguration.cpp", - "winrtdeployconfiguration.h", - "winrtdevice.cpp", - "winrtdevice.h", - "winrtpackagedeploymentstep.cpp", - "winrtpackagedeploymentstep.h", - "winrtphoneqtversion.cpp", - "winrtphoneqtversion.h", - "winrtplugin.cpp", - "winrtplugin.h", - "winrtqtversion.cpp", - "winrtqtversion.h", - "winrtrunconfiguration.cpp", - "winrtrunconfiguration.h", - "winrtruncontrol.cpp", - "winrtruncontrol.h", - "winrtrunnerhelper.cpp", - "winrtrunnerhelper.h" - ] -} diff --git a/src/plugins/winrt/winrt.qrc b/src/plugins/winrt/winrt.qrc deleted file mode 100644 index 0771e1d0aa1..00000000000 --- a/src/plugins/winrt/winrt.qrc +++ /dev/null @@ -1,8 +0,0 @@ -<RCC> - <qresource prefix="/winrt"> - <file>images/winrtdevice.png</file> - <file>images/[email protected]</file> - <file>images/winrtdevicesmall.png</file> - <file>images/[email protected]</file> - </qresource> -</RCC> diff --git a/src/plugins/winrt/winrtconstants.h b/src/plugins/winrt/winrtconstants.h deleted file mode 100644 index 03a6fbd36a0..00000000000 --- a/src/plugins/winrt/winrtconstants.h +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -namespace WinRt { -namespace Internal { -namespace Constants { - -const char WINRT_DEVICE_TYPE_LOCAL[] = "WinRt.Device.Local"; -const char WINRT_DEVICE_TYPE_EMULATOR[] = "WinRt.Device.Emulator"; -const char WINRT_DEVICE_TYPE_PHONE[] = "WinRt.Device.Phone"; -const char WINRT_BUILD_STEP_DEPLOY[] = "WinRt.BuildStep.Deploy"; -const char WINRT_WINRTQT[] = "WinRt.QtVersion.WindowsRuntime"; -const char WINRT_WINPHONEQT[] = "WinRt.QtVersion.WindowsPhone"; -const char WINRT_QTMAP_SUBKEYNAME[] = "WinRt"; -const char WINRT_QTMAP_OSFLAVOR[] = "OsFlavor"; -const char WINRT_MANIFEST_EDITOR_ID[] = "WinRTManifestEditorID"; - -} // Constants -} // Internal -} // WinRt diff --git a/src/plugins/winrt/winrtdebugsupport.cpp b/src/plugins/winrt/winrtdebugsupport.cpp deleted file mode 100644 index 5ae70764b33..00000000000 --- a/src/plugins/winrt/winrtdebugsupport.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtdebugsupport.h" -#include "winrtrunconfiguration.h" -#include "winrtrunnerhelper.h" - -#include <app/app_version.h> - -#include <projectexplorer/target.h> -#include <projectexplorer/toolchain.h> - -#include <qmldebug/qmldebugcommandlinearguments.h> - -#include <QFileInfo> -#include <QLocalServer> -#include <QLocalSocket> -#include <QTcpServer> - -#include <utils/qtcprocess.h> -#include <utils/url.h> - -using namespace Debugger; -using namespace ProjectExplorer; - -namespace WinRt { -namespace Internal { - -WinRtDebugSupport::WinRtDebugSupport(RunControl *runControl) - : DebuggerRunTool(runControl) -{ - // FIXME: This is just working for local debugging; - setStartMode(AttachToLocalProcess); - // The first Thread needs to be resumed manually. - setCommandsAfterConnect("~0 m"); - - QFileInfo debuggerHelper(QCoreApplication::applicationDirPath() - + QLatin1String("/winrtdebughelper.exe")); - if (!debuggerHelper.isExecutable()) { - reportFailure(tr("The WinRT debugging helper is missing from your %1 " - "installation. It was assumed to be located at %2") - .arg(Core::Constants::IDE_DISPLAY_NAME) - .arg(debuggerHelper.absoluteFilePath())); - return; - } - - if (isQmlDebugging()) { - QUrl qmlServer = Utils::urlFromLocalHostAndFreePort(); - if (qmlServer.port() <= 0) { - reportFailure(tr("Not enough free ports for QML debugging.")); - return; - } - setQmlServer(qmlServer); - } - - setSymbolFile(runControl->targetFilePath()); - QString errorMessage; - m_runner = new WinRtRunnerHelper(this, &errorMessage); - if (!errorMessage.isEmpty()) { - reportFailure(errorMessage); - return; - } - - QLocalServer server; - server.listen(QLatin1String("QtCreatorWinRtDebugPIDPipe")); - - m_runner->debug(debuggerHelper.absoluteFilePath()); - if (!m_runner->waitForStarted()) { - reportFailure(tr("Cannot start the WinRT Runner Tool.")); - return; - } - - if (!server.waitForNewConnection(10000)) { - reportFailure(tr("Cannot establish connection to the WinRT debugging helper.")); - return; - } - - while (server.hasPendingConnections()) { - QLocalSocket *connection = server.nextPendingConnection(); - if (connection->waitForReadyRead(1000)) { - const QByteArray &output = connection->readAll(); - QList<QByteArray> arg = output.split(':'); - if (arg.first() == "PID") { - bool ok =false; - int pid = arg.last().toInt(&ok); - if (!ok) { - reportFailure(tr("Cannot extract the PID from the WinRT debugging helper. " - "(output: %1)").arg(QString::fromLocal8Bit(output))); - return; - } - setAttachPid(Utils::ProcessHandle(pid)); - server.close(); - return; - } - } - } - - server.close(); - - reportFailure(tr("Cannot create an appropriate run control for " - "the current run configuration.")); -} - -WinRtDebugSupport::~WinRtDebugSupport() -{ - delete m_runner; -} - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtdebugsupport.h b/src/plugins/winrt/winrtdebugsupport.h deleted file mode 100644 index b8e3c6846ac..00000000000 --- a/src/plugins/winrt/winrtdebugsupport.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <debugger/debuggerruncontrol.h> - -namespace WinRt { -namespace Internal { - -class WinRtRunConfiguration; -class WinRtRunnerHelper; - -class WinRtDebugSupport : public Debugger::DebuggerRunTool -{ - Q_OBJECT - -public: - explicit WinRtDebugSupport(ProjectExplorer::RunControl *runControl); - ~WinRtDebugSupport(); - -private: - WinRtRunnerHelper *m_runner = nullptr; -}; - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtdeployconfiguration.cpp b/src/plugins/winrt/winrtdeployconfiguration.cpp deleted file mode 100644 index f42d48c924e..00000000000 --- a/src/plugins/winrt/winrtdeployconfiguration.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtdeployconfiguration.h" -#include "winrtpackagedeploymentstep.h" -#include "winrtconstants.h" - -#include <projectexplorer/project.h> -#include <projectexplorer/target.h> -#include <projectexplorer/buildsteplist.h> -#include <projectexplorer/projectexplorerconstants.h> - -#include <QCoreApplication> - -using namespace ProjectExplorer; - -namespace WinRt { -namespace Internal { - -WinRtAppDeployConfigurationFactory::WinRtAppDeployConfigurationFactory() -{ - setConfigBaseId("WinRTAppxDeployConfiguration"); - setDefaultDisplayName(QCoreApplication::translate("WinRt::Internal::WinRtDeployConfiguration", - "Run windeployqt")); - addSupportedTargetDeviceType(Constants::WINRT_DEVICE_TYPE_LOCAL); - addInitialStep(Constants::WINRT_BUILD_STEP_DEPLOY); -} - -WinRtPhoneDeployConfigurationFactory::WinRtPhoneDeployConfigurationFactory() -{ - setConfigBaseId("WinRTPhoneDeployConfiguration"); - setDefaultDisplayName(QCoreApplication::translate("WinRt::Internal::WinRtDeployConfiguration", - "Deploy to Windows Phone")); - addSupportedTargetDeviceType(Constants::WINRT_DEVICE_TYPE_PHONE); - addInitialStep(Constants::WINRT_BUILD_STEP_DEPLOY); -} - -WinRtEmulatorDeployConfigurationFactory::WinRtEmulatorDeployConfigurationFactory() -{ - setConfigBaseId("WinRTEmulatorDeployConfiguration"); - setDefaultDisplayName(QCoreApplication::translate("WinRt::Internal::WinRtDeployConfiguration", - "Deploy to Windows Phone Emulator")); - addSupportedTargetDeviceType(Constants::WINRT_DEVICE_TYPE_EMULATOR); - addInitialStep(Constants::WINRT_BUILD_STEP_DEPLOY); -} - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtdeployconfiguration.h b/src/plugins/winrt/winrtdeployconfiguration.h deleted file mode 100644 index 1ea45eb8e91..00000000000 --- a/src/plugins/winrt/winrtdeployconfiguration.h +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <projectexplorer/deployconfiguration.h> -#include <projectexplorer/buildstep.h> - -namespace WinRt { -namespace Internal { - -class WinRtAppDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory -{ -public: - WinRtAppDeployConfigurationFactory(); -}; - -class WinRtPhoneDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory -{ -public: - WinRtPhoneDeployConfigurationFactory(); -}; - -class WinRtEmulatorDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory -{ -public: - WinRtEmulatorDeployConfigurationFactory(); -}; - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtdevice.cpp b/src/plugins/winrt/winrtdevice.cpp deleted file mode 100644 index 9c10c113a80..00000000000 --- a/src/plugins/winrt/winrtdevice.cpp +++ /dev/null @@ -1,365 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtdevice.h" -#include "winrtconstants.h" -#include "winrtqtversion.h" - -#include <coreplugin/messagemanager.h> - -#include <projectexplorer/devicesupport/desktopprocesssignaloperation.h> -#include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/devicesupport/deviceprocesslist.h> -#include <projectexplorer/projectexplorerconstants.h> - -#include <qtsupport/qtversionmanager.h> - -#include <utils/qtcassert.h> -#include <utils/portlist.h> - -#include <QFileInfo> -#include <QIcon> -#include <QLoggingCategory> -#include <QWizard> - -using QtSupport::QtVersion; -using QtSupport::QtVersionManager; - - -using namespace Core; -using namespace ProjectExplorer; -using namespace Utils; - -namespace WinRt { -namespace Internal { - -static Q_LOGGING_CATEGORY(winrtDeviceLog, "qtc.winrt.deviceParser", QtWarningMsg) - -WinRtDevice::WinRtDevice() -{ - setDisplayType(displayNameForType(type())); - setOsType(OsTypeWindows); - - Utils::PortList portList; - portList.addRange(Utils::Port(ProjectExplorer::Constants::DESKTOP_PORT_START), - Utils::Port(ProjectExplorer::Constants::DESKTOP_PORT_END)); - setFreePorts(portList); -} - -IDeviceWidget *WinRtDevice::createWidget() -{ - return nullptr; -} - -DeviceProcessSignalOperation::Ptr WinRtDevice::signalOperation() const -{ - class WinRtDesktopSignalOperation : public DesktopProcessSignalOperation - { - public: - WinRtDesktopSignalOperation() {} - ~WinRtDesktopSignalOperation() {} - }; - - return DeviceProcessSignalOperation::Ptr(new WinRtDesktopSignalOperation()); -} - -void WinRtDevice::fromMap(const QVariantMap &map) -{ - IDevice::fromMap(map); - m_deviceId = map.value(QStringLiteral("WinRtRunnerDeviceId")).toInt(); -} - -QVariantMap WinRtDevice::toMap() const -{ - QVariantMap map = IDevice::toMap(); - map.insert(QStringLiteral("WinRtRunnerDeviceId"), m_deviceId); - return map; -} - -QString WinRtDevice::displayNameForType(Utils::Id type) -{ - if (type == Constants::WINRT_DEVICE_TYPE_LOCAL) - return QCoreApplication::translate("WinRt::Internal::WinRtDevice", - "Windows Runtime (Local)"); - if (type == Constants::WINRT_DEVICE_TYPE_PHONE) - return QCoreApplication::translate("WinRt::Internal::WinRtDevice", - "Windows Phone"); - if (type == Constants::WINRT_DEVICE_TYPE_EMULATOR) - return QCoreApplication::translate("WinRt::Internal::WinRtDevice", - "Windows Phone Emulator"); - return QString(); -} - - -// Factory - -WinRtDeviceFactory::WinRtDeviceFactory(Utils::Id deviceType) - : ProjectExplorer::IDeviceFactory(deviceType) -{ - if (allPrerequisitesLoaded()) { - onPrerequisitesLoaded(); - } else { - connect(DeviceManager::instance(), &DeviceManager::devicesLoaded, - this, &WinRtDeviceFactory::onPrerequisitesLoaded, Qt::QueuedConnection); - connect(QtVersionManager::instance(), - &QtVersionManager::qtVersionsLoaded, - this, &WinRtDeviceFactory::onPrerequisitesLoaded, Qt::QueuedConnection); - } - setDisplayName(WinRtDevice::displayNameForType(deviceType)); - setConstructionFunction(&WinRtDevice::create); - setCombinedIcon(":/winrt/images/winrtdevicesmall.png", - ":/winrt/images/winrtdevice.png"); -} - -void WinRtDeviceFactory::autoDetect() -{ - qCDebug(winrtDeviceLog) << __FUNCTION__; - const QString runnerFilePath = findRunnerFilePath(); - if (runnerFilePath.isEmpty()) { - qCDebug(winrtDeviceLog) << "No winrtrunner.exe found."; - return; - } - - if (!m_process) { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Creating process"; - m_process = new Utils::QtcProcess(this); - connect(m_process, &QtcProcess::errorOccurred, this, &WinRtDeviceFactory::onProcessError); - connect(m_process, &QtcProcess::finished, this, &WinRtDeviceFactory::onProcessFinished); - } - - const CommandLine cmd{FilePath::fromString(runnerFilePath), {"--list-devices"}}; - m_process->setCommand(cmd); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Starting process" << cmd.toUserOutput(); - m_process->start(); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Process started"; -} - -void WinRtDeviceFactory::onPrerequisitesLoaded() -{ - if (!allPrerequisitesLoaded() || m_initialized) - return; - - qCDebug(winrtDeviceLog) << __FUNCTION__; - m_initialized = true; - disconnect(DeviceManager::instance(), &DeviceManager::devicesLoaded, - this, &WinRtDeviceFactory::onPrerequisitesLoaded); - disconnect(QtVersionManager::instance(), &QtVersionManager::qtVersionsLoaded, - this, &WinRtDeviceFactory::onPrerequisitesLoaded); - autoDetect(); - connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged, - this, &WinRtDeviceFactory::autoDetect); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Done"; -} - -void WinRtDeviceFactory::onProcessError() -{ - MessageManager::writeDisrupting( - tr("Error while executing winrtrunner: %1").arg(m_process->errorString())); -} - -void WinRtDeviceFactory::onProcessFinished() -{ - int exitCode = m_process->exitCode(); - QProcess::ExitStatus exitStatus = m_process->exitStatus(); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Exit code:" << exitCode <<"\tExit status:" - << exitStatus; - if (exitStatus == QProcess::CrashExit) { - // already handled in onProcessError - return; - } - - if (exitCode != 0) { - MessageManager::writeFlashing(tr("winrtrunner returned with exit code %1.").arg(exitCode)); - return; - } - - const QByteArray stdOut = m_process->readAllStandardOutput(); - const QByteArray stdErr = m_process->readAllStandardError(); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "winrtrunner's stdout:" << stdOut; - if (!stdErr.isEmpty()) - qCDebug(winrtDeviceLog) << __FUNCTION__ << "winrtrunner's stderr:" << stdErr; - parseRunnerOutput(stdOut); -} - -bool WinRtDeviceFactory::allPrerequisitesLoaded() -{ - return QtVersionManager::isLoaded() && DeviceManager::instance()->isLoaded(); -} - -QString WinRtDeviceFactory::findRunnerFilePath() const -{ - qCDebug(winrtDeviceLog) << __FUNCTION__; - const QString winRtRunnerExe = QStringLiteral("/winrtrunner.exe"); - const QList<QtVersion *> winrtVersions - = QtVersionManager::sortVersions( - QtVersionManager::versions(QtVersion::isValidPredicate([](const QtVersion *v) { - return v->type() == QLatin1String(Constants::WINRT_WINRTQT) - || v->type() == QLatin1String(Constants::WINRT_WINPHONEQT); - }))); - QString filePath; - QtVersion *qt = nullptr; - for (QtVersion *v : winrtVersions) { - if (!qt || qt->qtVersion() < v->qtVersion()) { - QFileInfo fi(v->hostBinPath().toString() + winRtRunnerExe); - if (fi.isFile() && fi.isExecutable()) { - qt = v; - filePath = fi.absoluteFilePath(); - } - } - } - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Found" << filePath; - return filePath; -} - -static int extractDeviceId(QByteArray *line) -{ - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Line:" << *line; - int pos = line->indexOf(' '); - if (pos < 0) { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Could not find space, returning -1"; - return -1; - } - bool ok; - int id = line->left(pos).toInt(&ok); - if (!ok) { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Could not extract ID"; - return -1; - } - line->remove(0, pos + 1); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Found ID" << id; - return id; -} - -static IDevice::MachineType machineTypeFromLine(const QByteArray &line) -{ - return line.contains("Emulator ") ? IDevice::Emulator : IDevice::Hardware; -} - -/* - * The output of "winrtrunner --list-devices" looks like this: - * - * Available devices: - * Appx: - * 0 local - * Phone: - * 0 Device - * 1 Emulator 8.1 WVGA 4 inch 512MB - * 2 Emulator 8.1 WVGA 4 inch - * 3 Emulator 8.1 WXGA 4 inch - * 4 Emulator 8.1 720P 4.7 inch - * 5 Emulator 8.1 1080P 5.5 inch - * 6 Emulator 8.1 1080P 6 inch - * 7 WE8.1H Emulator WVGA 512MB - * 8 WE8.1H Emulator WVGA - * 9 WE8.1H Emulator WXGA - * 10 WE8.1H Emulator 720P - * 11 WE8.1H Emulator 1080P - * Xap: - * 0 Device - * 1 Emulator WVGA 512MB - * 2 Emulator WVGA - * 3 Emulator WXGA - * 4 Emulator 720P - */ -void WinRtDeviceFactory::parseRunnerOutput(const QByteArray &output) const -{ - qCDebug(winrtDeviceLog) << __FUNCTION__; - ProjectExplorer::DeviceManager *deviceManager = ProjectExplorer::DeviceManager::instance(); - enum State { StartState, AppxState, PhoneState, XapState }; - State state = StartState; - int numFound = 0; - int numSkipped = 0; - foreach (QByteArray line, output.split('\n')) { - line = line.trimmed(); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Line:" << line; - if (line == "Appx:") { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "state = AppxState"; - state = AppxState; - } else if (line == "Phone:") { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "state = PhoneState"; - state = PhoneState; - } else if (line == "Xap:") { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "state = XapState"; - state = XapState; - } else { - const int deviceId = extractDeviceId(&line); - if (deviceId < 0) { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Could not extract device ID"; - continue; - } - - const IDevice::MachineType machineType = machineTypeFromLine(line); - Utils::Id deviceType; - QString name; - QString internalName = QStringLiteral("WinRT."); - if (state == AppxState) { - internalName += QStringLiteral("appx."); - deviceType = Constants::WINRT_DEVICE_TYPE_LOCAL; - name = tr("Windows Runtime local UI"); - } else if (state == PhoneState) { - internalName += QStringLiteral("phone."); - name = QString::fromLocal8Bit(line); - if (machineType == IDevice::Emulator) - deviceType = Constants::WINRT_DEVICE_TYPE_EMULATOR; - else - deviceType = Constants::WINRT_DEVICE_TYPE_PHONE; - } else if (state == XapState) { - internalName += QStringLiteral("xap."); - name = QString::fromLocal8Bit(line); - if (machineType == IDevice::Emulator) - deviceType = Constants::WINRT_DEVICE_TYPE_EMULATOR; - else - deviceType = Constants::WINRT_DEVICE_TYPE_PHONE; - } - internalName += QString::number(deviceId); - const Utils::Id internalId = Utils::Id::fromString(internalName); - ++numFound; - if (DeviceManager::instance()->find(internalId)) { - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Skipping device with ID" << deviceId; - ++numSkipped; - continue; - } - - auto device = WinRtDevice::create(); - device->setupId(IDevice::AutoDetected, internalId); - device->setDeviceId(deviceId); - device->setType(deviceType); - device->setMachineType(machineType); - device->setDefaultDisplayName(name); - deviceManager->addDevice(ProjectExplorer::IDevice::ConstPtr(device)); - qCDebug(winrtDeviceLog) << __FUNCTION__ << "Added device" << name << "(internal name:" - << internalName << ")"; - } - } - QString message = tr("Found %n Windows Runtime devices.", nullptr, numFound); - if (const int numNew = numFound - numSkipped) { - message += QLatin1Char(' '); - message += tr("%n of them are new.", nullptr, numNew); - } - qCDebug(winrtDeviceLog) << message; -} - -} // Internal -} // WinRt diff --git a/src/plugins/winrt/winrtdevice.h b/src/plugins/winrt/winrtdevice.h deleted file mode 100644 index 37d81131557..00000000000 --- a/src/plugins/winrt/winrtdevice.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/devicesupport/idevicefactory.h> - -#include <utils/qtcprocess.h> - -namespace WinRt { -namespace Internal { - -class WinRtDevice final : public ProjectExplorer::IDevice -{ -public: - typedef QSharedPointer<WinRtDevice> Ptr; - typedef QSharedPointer<const WinRtDevice> ConstPtr; - - static Ptr create() { return Ptr(new WinRtDevice); } - - ProjectExplorer::IDeviceWidget *createWidget() override; - ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override; - - static QString displayNameForType(Utils::Id type); - int deviceId() const { return m_deviceId; } - void setDeviceId(int deviceId) { m_deviceId = deviceId; } - -protected: - void fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; - -private: - WinRtDevice(); - - int m_deviceId = -1; -}; - -class WinRtDeviceFactory final : public QObject, public ProjectExplorer::IDeviceFactory -{ - Q_OBJECT -public: - explicit WinRtDeviceFactory(Utils::Id deviceType); - - void autoDetect(); - void onPrerequisitesLoaded(); - -private: - void onProcessError(); - void onProcessFinished(); - - static bool allPrerequisitesLoaded(); - QString findRunnerFilePath() const; - void parseRunnerOutput(const QByteArray &output) const; - - Utils::QtcProcess *m_process = nullptr; - bool m_initialized = false; -}; - -} // Internal -} // WinRt diff --git a/src/plugins/winrt/winrtpackagedeploymentstep.cpp b/src/plugins/winrt/winrtpackagedeploymentstep.cpp deleted file mode 100644 index 067c04c361b..00000000000 --- a/src/plugins/winrt/winrtpackagedeploymentstep.cpp +++ /dev/null @@ -1,408 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtpackagedeploymentstep.h" - -#include "winrtconstants.h" - -#include <projectexplorer/abstractprocessstep.h> -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/buildtargetinfo.h> -#include <projectexplorer/deployablefile.h> -#include <projectexplorer/deploymentdata.h> -#include <projectexplorer/processparameters.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/project.h> -#include <projectexplorer/runconfiguration.h> -#include <projectexplorer/target.h> - -#include <qtsupport/qtkitinformation.h> - -#include <utils/aspects.h> -#include <utils/qtcassert.h> -#include <utils/qtcprocess.h> -#include <utils/fancylineedit.h> -#include <utils/layoutbuilder.h> - -#include <QLabel> -#include <QLayout> -#include <QRegularExpression> -#include <QToolButton> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace WinRt { -namespace Internal { - -const char ARGUMENTS_KEY[] = "WinRt.BuildStep.Deploy.Arguments"; -const char DEFAULTARGUMENTS_KEY[] = "WinRt.BuildStep.Deploy.DefaultArguments"; - -class WinRtArgumentsAspect final : public BaseAspect -{ - Q_DECLARE_TR_FUNCTIONS(WinRt::Internal::WinRtArgumentsAspect) - -public: - WinRtArgumentsAspect() = default; - - void addToLayout(LayoutBuilder &builder) final; - - void fromMap(const QVariantMap &map) final; - void toMap(QVariantMap &map) const final; - - void setValue(const QString &value); - QString value() const { return m_value; } - - void setDefaultValue(const QString &value) { m_defaultValue = value; } - QString defaultValue() const { return m_defaultValue; } - - void restoreDefaultValue(); - -private: - FancyLineEdit *m_lineEdit = nullptr; - QString m_value; - QString m_defaultValue; -}; - -class WinRtPackageDeploymentStep final : public AbstractProcessStep -{ - Q_DECLARE_TR_FUNCTIONS(WinRt::Internal::WinRtPackageDeploymentStep) - -public: - WinRtPackageDeploymentStep(BuildStepList *bsl, Utils::Id id); - - QString defaultWinDeployQtArguments() const; - - void raiseError(const QString &errorMessage); - void raiseWarning(const QString &warningMessage); - -private: - bool init() override; - void doRun() override; - bool processSucceeded(int exitCode, QProcess::ExitStatus status) override; - void stdOutput(const QString &line) override; - - bool parseIconsAndExecutableFromManifest(QString manifestFileName, QStringList *items, QString *executable); - - WinRtArgumentsAspect *m_argsAspect = nullptr; - QString m_targetFilePath; - QString m_targetDirPath; - QString m_executablePathInManifest; - QString m_mappingFileContent; - QString m_manifestFileName; - bool m_createMappingFile = false; -}; - -void WinRtArgumentsAspect::addToLayout(LayoutBuilder &builder) -{ - QTC_CHECK(!m_lineEdit); - auto label = new QLabel(tr("Arguments:")); - label->setTextInteractionFlags(Qt::TextSelectableByMouse); - builder.addItem(label); - - auto *layout = new QHBoxLayout(); - m_lineEdit = new Utils::FancyLineEdit(); - if (!m_value.isEmpty()) - m_lineEdit->setText(m_value); - else if (!m_defaultValue.isEmpty()) - m_lineEdit->setText(m_defaultValue); - connect(m_lineEdit, &Utils::FancyLineEdit::textEdited, - this, &WinRtArgumentsAspect::setValue); - layout->addWidget(m_lineEdit); - - auto restoreDefaultButton = new QToolButton(); - restoreDefaultButton->setText(tr("Restore Default Arguments")); - connect(restoreDefaultButton, &QToolButton::clicked, - this, &WinRtArgumentsAspect::restoreDefaultValue); - layout->addWidget(restoreDefaultButton); - builder.addItem(layout); -} - -void WinRtArgumentsAspect::fromMap(const QVariantMap &map) -{ - m_defaultValue = map.value(DEFAULTARGUMENTS_KEY).toString(); - m_value = map.value(ARGUMENTS_KEY).toString(); -} - -void WinRtArgumentsAspect::toMap(QVariantMap &map) const -{ - map.insert(DEFAULTARGUMENTS_KEY, m_defaultValue); - map.insert(ARGUMENTS_KEY, m_value); -} - -void WinRtArgumentsAspect::setValue(const QString &value) -{ - if (value == m_value) - return; - - m_value = value; - if (m_lineEdit) - m_lineEdit->setText(value); - emit changed(); -} - -void WinRtArgumentsAspect::restoreDefaultValue() -{ - if (m_defaultValue == m_value) - return; - - setValue(m_defaultValue); -} - -WinRtPackageDeploymentStep::WinRtPackageDeploymentStep(BuildStepList *bsl, Utils::Id id) - : AbstractProcessStep(bsl, id) -{ - setDisplayName(tr("Run windeployqt")); - - m_argsAspect = addAspect<WinRtArgumentsAspect>(); - m_argsAspect->setDefaultValue(defaultWinDeployQtArguments()); - m_argsAspect->setValue(defaultWinDeployQtArguments()); -} - -bool WinRtPackageDeploymentStep::init() -{ - if (!AbstractProcessStep::init()) - return false; - - RunConfiguration *rc = target()->activeRunConfiguration(); - QTC_ASSERT(rc, return false); - - const BuildTargetInfo bti = rc->buildTargetInfo(); - Utils::FilePath appTargetFilePath = bti.targetFilePath; - - m_targetFilePath = appTargetFilePath.toString(); - if (m_targetFilePath.isEmpty()) { - raiseError(tr("No executable to deploy found in %1.").arg(bti.projectFilePath.toString())); - return false; - } - - // ### Ideally, the file paths in applicationTargets() should already have the .exe suffix. - // Whenever this will eventually work, we can drop appending the .exe suffix here. - if (!m_targetFilePath.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) - m_targetFilePath.append(QLatin1String(".exe")); - - m_targetDirPath = appTargetFilePath.parentDir().toString(); - if (!m_targetDirPath.endsWith(QLatin1Char('/'))) - m_targetDirPath += QLatin1Char('/'); - - const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(kit()); - if (!qt) - return false; - - const FilePath windeployqtPath = qt->hostBinPath().resolvePath(QString("windeployqt.exe")); - - CommandLine windeployqt{windeployqtPath}; - windeployqt.addArg(QDir::toNativeSeparators(m_targetFilePath)); - windeployqt.addArgs(m_argsAspect->value(), CommandLine::Raw); - - if (qt->type() == Constants::WINRT_WINPHONEQT) { - m_createMappingFile = true; - windeployqt.addArgs({"-list", "mapping"}); - } - - ProcessParameters *params = processParameters(); - if (!windeployqtPath.exists()) { - raiseError(tr("Cannot find windeployqt.exe in \"%1\".") - .arg(QDir::toNativeSeparators(qt->hostBinPath().toString()))); - return false; - } - params->setCommandLine(windeployqt); - params->setEnvironment(target()->activeBuildConfiguration() - ? target()->activeBuildConfiguration()->environment() - : Environment::systemEnvironment()); - - return true; -} - -void WinRtPackageDeploymentStep::doRun() -{ - const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(kit()); - if (!qt) - return; - - m_manifestFileName = QStringLiteral("AppxManifest"); - - if (m_createMappingFile) { - m_mappingFileContent = QLatin1String("[Files]\n"); - - QDir assetDirectory(m_targetDirPath + QLatin1String("assets")); - if (assetDirectory.exists()) { - QStringList iconsToDeploy; - const QString fullManifestPath = m_targetDirPath + m_manifestFileName - + QLatin1String(".xml"); - if (!parseIconsAndExecutableFromManifest(fullManifestPath, &iconsToDeploy, - &m_executablePathInManifest)) { - raiseError(tr("Cannot parse manifest file %1.").arg(fullManifestPath)); - return; - } - foreach (const QString &icon, iconsToDeploy) { - m_mappingFileContent += QLatin1Char('"') - + QDir::toNativeSeparators(m_targetDirPath + icon) + QLatin1String("\" \"") - + QDir::toNativeSeparators(icon) + QLatin1String("\"\n"); - } - } - } - - AbstractProcessStep::doRun(); -} - -bool WinRtPackageDeploymentStep::processSucceeded(int exitCode, QProcess::ExitStatus status) -{ - if (m_createMappingFile) { - QString targetInstallationPath; - // The list holds the local file paths and the "remote" file paths - QList<QPair<QString, QString> > installableFilesList; - foreach (DeployableFile file, target()->deploymentData().allFiles()) { - QString remoteFilePath = file.remoteFilePath(); - while (remoteFilePath.startsWith(QLatin1Char('/'))) - remoteFilePath.remove(0, 1); - QString localFilePath = file.localFilePath().toString(); - if (localFilePath == m_targetFilePath) { - if (!m_targetFilePath.endsWith(QLatin1String(".exe"))) { - remoteFilePath += QLatin1String(".exe"); - localFilePath += QLatin1String(".exe"); - } - targetInstallationPath = remoteFilePath; - } - installableFilesList.append(QPair<QString, QString>(localFilePath, remoteFilePath)); - } - - // if there are no INSTALLS set we just deploy the files from windeployqt, - // the icons referenced in the manifest file and the actual build target - QString baseDir; - if (targetInstallationPath.isEmpty()) { - if (!m_targetFilePath.endsWith(QLatin1String(".exe"))) - m_targetFilePath.append(QLatin1String(".exe")); - m_mappingFileContent - += QLatin1Char('"') + QDir::toNativeSeparators(m_targetFilePath) - + QLatin1String("\" \"") - + QDir::toNativeSeparators(m_executablePathInManifest) + QLatin1String("\"\n"); - baseDir = m_targetDirPath; - } else { - baseDir = targetInstallationPath.left(targetInstallationPath.lastIndexOf(QLatin1Char('/')) + 1); - } - - typedef QPair<QString, QString> QStringPair; - foreach (const QStringPair &pair, installableFilesList) { - // For the mapping file we need the remote paths relative to the application's executable - QString relativeRemotePath; - if (QDir(pair.second).isRelative()) - relativeRemotePath = pair.second; - else - relativeRemotePath = QDir(baseDir).relativeFilePath(pair.second); - - if (QDir(relativeRemotePath).isAbsolute() || relativeRemotePath.startsWith(QLatin1String(".."))) { - raiseWarning(tr("File %1 is outside of the executable's directory. These files cannot be installed.").arg(relativeRemotePath)); - continue; - } - - m_mappingFileContent += QLatin1Char('"') + QDir::toNativeSeparators(pair.first) - + QLatin1String("\" \"") + QDir::toNativeSeparators(relativeRemotePath) - + QLatin1String("\"\n"); - } - - const QString mappingFilePath = m_targetDirPath + m_manifestFileName - + QLatin1String(".map"); - QFile mappingFile(mappingFilePath); - if (!mappingFile.open(QFile::WriteOnly | QFile::Text)) { - raiseError(tr("Cannot open mapping file %1 for writing.").arg(mappingFilePath)); - return false; - } - mappingFile.write(m_mappingFileContent.toUtf8()); - } - - return AbstractProcessStep::processSucceeded(exitCode, status); -} - -void WinRtPackageDeploymentStep::stdOutput(const QString &line) -{ - if (m_createMappingFile) - m_mappingFileContent += line; - AbstractProcessStep::stdOutput(line); -} - -QString WinRtPackageDeploymentStep::defaultWinDeployQtArguments() const -{ - QString args; - ProcessArgs::addArg(&args, QStringLiteral("--qmldir")); - ProcessArgs::addArg(&args, project()->projectDirectory().toUserOutput()); - return args; -} - -void WinRtPackageDeploymentStep::raiseError(const QString &errorMessage) -{ - emit addOutput(errorMessage, BuildStep::OutputFormat::ErrorMessage); - emit addTask(DeploymentTask(Task::Error, errorMessage), 1); -} - -void WinRtPackageDeploymentStep::raiseWarning(const QString &warningMessage) -{ - emit addOutput(warningMessage, BuildStep::OutputFormat::NormalMessage); - emit addTask(DeploymentTask(Task::Warning, warningMessage), 1); -} - -bool WinRtPackageDeploymentStep::parseIconsAndExecutableFromManifest(QString manifestFileName, QStringList *icons, QString *executable) -{ - if (!icons->isEmpty()) - icons->clear(); - QFile manifestFile(manifestFileName); - if (!manifestFile.open(QFile::ReadOnly)) - return false; - const QString contents = QString::fromUtf8(manifestFile.readAll()); - - QRegularExpression iconPattern(QStringLiteral("[\\\\/a-zA-Z0-9_\\-\\!]*\\.(png|jpg|jpeg)")); - QRegularExpressionMatchIterator iterator = iconPattern.globalMatch(contents); - while (iterator.hasNext()) { - QRegularExpressionMatch match = iterator.next(); - const QString icon = match.captured(0); - icons->append(icon); - } - - const QLatin1String executablePrefix(manifestFileName.contains(QLatin1String("AppxManifest")) ? "Executable=" : "ImagePath="); - QRegularExpression executablePattern(executablePrefix + QStringLiteral("\"([a-zA-Z0-9_-]*\\.exe)\"")); - QRegularExpressionMatch match = executablePattern.match(contents); - if (!match.hasMatch()) - return false; - *executable = match.captured(1); - - return true; -} - -// WinRtDeployStepFactory - -WinRtDeployStepFactory::WinRtDeployStepFactory() -{ - registerStep<WinRtPackageDeploymentStep>(Constants::WINRT_BUILD_STEP_DEPLOY); - setDisplayName(QCoreApplication::translate("WinRt::Internal::WinRtDeployStepFactory", "Run windeployqt")); - setFlags(BuildStepInfo::Unclonable); - setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY); - setSupportedDeviceTypes({Constants::WINRT_DEVICE_TYPE_LOCAL, - Constants::WINRT_DEVICE_TYPE_EMULATOR, - Constants::WINRT_DEVICE_TYPE_PHONE}); - setRepeatable(false); -} - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtpackagedeploymentstep.h b/src/plugins/winrt/winrtpackagedeploymentstep.h deleted file mode 100644 index c20898ade90..00000000000 --- a/src/plugins/winrt/winrtpackagedeploymentstep.h +++ /dev/null @@ -1,40 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <projectexplorer/buildstep.h> - -namespace WinRt { -namespace Internal { - -class WinRtDeployStepFactory final : public ProjectExplorer::BuildStepFactory -{ -public: - WinRtDeployStepFactory(); -}; - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtphoneqtversion.cpp b/src/plugins/winrt/winrtphoneqtversion.cpp deleted file mode 100644 index de0289bdd11..00000000000 --- a/src/plugins/winrt/winrtphoneqtversion.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtphoneqtversion.h" - -#include "winrtconstants.h" - -#include <qtsupport/qtsupportconstants.h> - -#include <utils/id.h> - -#include <QSet> - -namespace WinRt { -namespace Internal { - -QString WinRtPhoneQtVersion::description() const -{ - return tr("Windows Phone"); -} - -QSet<Utils::Id> WinRtPhoneQtVersion::targetDeviceTypes() const -{ - return {Constants::WINRT_DEVICE_TYPE_PHONE, Constants::WINRT_DEVICE_TYPE_EMULATOR}; -} - -QSet<Utils::Id> WinRtPhoneQtVersion::availableFeatures() const -{ - QSet<Utils::Id> features = QtSupport::QtVersion::availableFeatures(); - features.insert(QtSupport::Constants::FEATURE_MOBILE); - features.remove(QtSupport::Constants::FEATURE_QT_CONSOLE); - features.remove(Utils::Id::versionedId(QtSupport::Constants::FEATURE_QT_QUICK_CONTROLS_PREFIX, 1)); - features.remove(QtSupport::Constants::FEATURE_QT_WEBKIT); - return features; -} - -// Factory - -WinRtPhoneQtVersionFactory::WinRtPhoneQtVersionFactory() -{ - setQtVersionCreator([] { return new WinRtPhoneQtVersion; }); - setSupportedType(Constants::WINRT_WINPHONEQT); - setRestrictionChecker([](const SetupData &setup) { return setup.platforms.contains("winphone"); }); - setPriority(10); -} - -} // Internal -} // WinRt diff --git a/src/plugins/winrt/winrtphoneqtversion.h b/src/plugins/winrt/winrtphoneqtversion.h deleted file mode 100644 index 060d551c6f0..00000000000 --- a/src/plugins/winrt/winrtphoneqtversion.h +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "winrtqtversion.h" - -namespace WinRt { -namespace Internal { - -class WinRtPhoneQtVersion : public QtSupport::QtVersion -{ - Q_DECLARE_TR_FUNCTIONS(WinRt::Internal::WinRtQtVersion) -public: - WinRtPhoneQtVersion() = default; - - QSet<Utils::Id> availableFeatures() const override; - - QString description() const override; - QSet<Utils::Id> targetDeviceTypes() const override; -}; - -class WinRtPhoneQtVersionFactory : public QtSupport::QtVersionFactory -{ -public: - WinRtPhoneQtVersionFactory(); -}; - -} // Internal -} // WinRt diff --git a/src/plugins/winrt/winrtplugin.cpp b/src/plugins/winrt/winrtplugin.cpp deleted file mode 100644 index 2b603bff266..00000000000 --- a/src/plugins/winrt/winrtplugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtplugin.h" - -#include "winrtconstants.h" -#include "winrtdebugsupport.h" -#include "winrtdeployconfiguration.h" -#include "winrtdevice.h" -#include "winrtpackagedeploymentstep.h" -#include "winrtphoneqtversion.h" -#include "winrtqtversion.h" -#include "winrtrunconfiguration.h" -#include "winrtruncontrol.h" - -#include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/target.h> - -using namespace ProjectExplorer; - -namespace WinRt { -namespace Internal { - -class WinRtPluginPrivate -{ -public: - WinRtRunConfigurationFactory runConfigFactory; - WinRtQtVersionFactory qtVersionFactory; - WinRtPhoneQtVersionFactory phoneQtVersionFactory; - WinRtAppDeployConfigurationFactory appDeployConfigFactory; - WinRtPhoneDeployConfigurationFactory phoneDeployConfigFactory; - WinRtEmulatorDeployConfigurationFactory emulatorDeployFactory; - WinRtDeployStepFactory deployStepFactory; - WinRtDeviceFactory localDeviceFactory{Constants::WINRT_DEVICE_TYPE_LOCAL}; - WinRtDeviceFactory phoneDeviceFactory{Constants::WINRT_DEVICE_TYPE_PHONE}; - WinRtDeviceFactory emulatorDeviceFactory{Constants::WINRT_DEVICE_TYPE_EMULATOR}; - - RunWorkerFactory runWorkerFactory{ - RunWorkerFactory::make<WinRtRunner>(), - {ProjectExplorer::Constants::NORMAL_RUN_MODE}, - {runConfigFactory.runConfigurationId()} - }; - - RunWorkerFactory debugWorkerFactory{ - RunWorkerFactory::make<WinRtDebugSupport>(), - {ProjectExplorer::Constants::DEBUG_RUN_MODE}, - {runConfigFactory.runConfigurationId()}, - {Internal::Constants::WINRT_DEVICE_TYPE_LOCAL} - }; -}; - -WinRtPlugin::~WinRtPlugin() -{ - delete d; -} - -bool WinRtPlugin::initialize(const QStringList &arguments, QString *errorMessage) -{ - Q_UNUSED(arguments) - Q_UNUSED(errorMessage) - - d = new WinRtPluginPrivate; - - return true; -} - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtplugin.h b/src/plugins/winrt/winrtplugin.h deleted file mode 100644 index bdfc0f09f1e..00000000000 --- a/src/plugins/winrt/winrtplugin.h +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <extensionsystem/iplugin.h> - -namespace WinRt { -namespace Internal { - -class WinRtPlugin final : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "WinRt.json") - -public: - ~WinRtPlugin() final; - -private: - bool initialize(const QStringList &arguments, QString *errorMessage) final; - - class WinRtPluginPrivate *d = nullptr; -}; - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtqtversion.cpp b/src/plugins/winrt/winrtqtversion.cpp deleted file mode 100644 index 4b3ce1facd9..00000000000 --- a/src/plugins/winrt/winrtqtversion.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtqtversion.h" - -#include "winrtconstants.h" - -#include <coreplugin/featureprovider.h> -#include <qtsupport/qtsupportconstants.h> - -namespace WinRt { -namespace Internal { - -QString WinRtQtVersion::description() const -{ - return tr("Windows Runtime"); -} - -QSet<Utils::Id> WinRtQtVersion::availableFeatures() const -{ - QSet<Utils::Id> features = QtSupport::QtVersion::availableFeatures(); - features.insert(QtSupport::Constants::FEATURE_MOBILE); - features.remove(QtSupport::Constants::FEATURE_QT_CONSOLE); - features.remove(Utils::Id::versionedId(QtSupport::Constants::FEATURE_QT_QUICK_CONTROLS_PREFIX, 1)); - features.remove(QtSupport::Constants::FEATURE_QT_WEBKIT); - return features; -} - -QSet<Utils::Id> WinRtQtVersion::targetDeviceTypes() const -{ - return {Constants::WINRT_DEVICE_TYPE_LOCAL, Constants::WINRT_DEVICE_TYPE_EMULATOR}; -} - - -// Factory - -WinRtQtVersionFactory::WinRtQtVersionFactory() -{ - setQtVersionCreator([] { return new WinRtQtVersion; }); - setSupportedType(Constants::WINRT_WINRTQT); - setRestrictionChecker([](const SetupData &setup) { return setup.platforms.contains("winrt"); }); - setPriority(10); -} - -} // Internal -} // WinRt diff --git a/src/plugins/winrt/winrtqtversion.h b/src/plugins/winrt/winrtqtversion.h deleted file mode 100644 index 2c767ff3d01..00000000000 --- a/src/plugins/winrt/winrtqtversion.h +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <qtsupport/baseqtversion.h> -#include <qtsupport/qtversionfactory.h> - -namespace WinRt { -namespace Internal { - -class WinRtQtVersion : public QtSupport::QtVersion -{ - Q_DECLARE_TR_FUNCTIONS(WinRt::Internal::WinRtQtVersion) -public: - WinRtQtVersion() = default; - - QString description() const override; - QSet<Utils::Id> availableFeatures() const override; - - QSet<Utils::Id> targetDeviceTypes() const override; -}; - -class WinRtQtVersionFactory : public QtSupport::QtVersionFactory -{ -public: - WinRtQtVersionFactory(); -}; - -} // Internal -} // WinRt diff --git a/src/plugins/winrt/winrtrunconfiguration.cpp b/src/plugins/winrt/winrtrunconfiguration.cpp deleted file mode 100644 index 6103a469b18..00000000000 --- a/src/plugins/winrt/winrtrunconfiguration.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtrunconfiguration.h" -#include "winrtconstants.h" - -#include <projectexplorer/target.h> -#include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> - -using namespace ProjectExplorer; - -namespace WinRt { -namespace Internal { - -// UninstallAfterStopAspect - -UninstallAfterStopAspect::UninstallAfterStopAspect() - : BoolAspect("WinRtRunConfigurationUninstallAfterStopId") -{ - setLabel(WinRtRunConfiguration::tr("Uninstall package after application stops"), - LabelPlacement::AtCheckBox); -} - -// LoopbackExemptClientAspect - -LoopbackExemptClientAspect::LoopbackExemptClientAspect() - : BoolAspect("WinRtRunConfigurationLoopbackExemptClient") -{ - setLabel(WinRtRunConfiguration::tr("Enable localhost communication for clients"), - LabelPlacement::AtCheckBox); -} - -// LoopbackExemptServerAspect - -LoopbackExemptServerAspect::LoopbackExemptServerAspect() - : BoolAspect("WinRtRunConfigurationLoopbackExemptServer") -{ - setLabel(WinRtRunConfiguration::tr("Enable localhost communication for " - "servers (requires elevated rights)"), - LabelPlacement::AtCheckBox); -} - -// WinRtRunConfiguration - -WinRtRunConfiguration::WinRtRunConfiguration(Target *target, Utils::Id id) - : RunConfiguration(target, id) -{ - setDisplayName(tr("Run App Package")); - addAspect<ArgumentsAspect>(); - addAspect<UninstallAfterStopAspect>(); - - const QtSupport::QtVersion *qt - = QtSupport::QtKitAspect::qtVersion(target->kit()); - if (qt && qt->qtVersion() >= QtSupport::QtVersionNumber(5, 12, 0)) { - addAspect<LoopbackExemptClientAspect>(); - addAspect<LoopbackExemptServerAspect>(); - } -} - -// WinRtRunConfigurationFactory - -WinRtRunConfigurationFactory::WinRtRunConfigurationFactory() -{ - registerRunConfiguration<WinRtRunConfiguration>("WinRt.WinRtRunConfiguration:"); - addSupportedTargetDeviceType(Constants::WINRT_DEVICE_TYPE_LOCAL); - addSupportedTargetDeviceType(Constants::WINRT_DEVICE_TYPE_PHONE); - addSupportedTargetDeviceType(Constants::WINRT_DEVICE_TYPE_EMULATOR); -} - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtrunconfiguration.h b/src/plugins/winrt/winrtrunconfiguration.h deleted file mode 100644 index 8e9cf4f0c1e..00000000000 --- a/src/plugins/winrt/winrtrunconfiguration.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <projectexplorer/runconfigurationaspects.h> - -namespace WinRt { -namespace Internal { - -class UninstallAfterStopAspect : public Utils::BoolAspect -{ - Q_OBJECT - -public: - UninstallAfterStopAspect(); -}; - -class LoopbackExemptClientAspect : public Utils::BoolAspect -{ - Q_OBJECT - -public: - LoopbackExemptClientAspect(); -}; - -class LoopbackExemptServerAspect : public Utils::BoolAspect -{ - Q_OBJECT - -public: - LoopbackExemptServerAspect(); -}; - -class WinRtRunConfiguration : public ProjectExplorer::RunConfiguration -{ - Q_OBJECT - -public: - WinRtRunConfiguration(ProjectExplorer::Target *target, Utils::Id id); -}; - -class WinRtRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory -{ -public: - WinRtRunConfigurationFactory(); -}; - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtruncontrol.cpp b/src/plugins/winrt/winrtruncontrol.cpp deleted file mode 100644 index 53099cec5cf..00000000000 --- a/src/plugins/winrt/winrtruncontrol.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtruncontrol.h" - -#include "winrtdevice.h" -#include "winrtrunconfiguration.h" -#include "winrtrunnerhelper.h" - -#include <coreplugin/idocument.h> -#include <extensionsystem/pluginmanager.h> -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/buildtargetinfo.h> -#include <projectexplorer/target.h> -#include <projectexplorer/project.h> -#include <projectexplorer/kitinformation.h> -#include <qtsupport/qtkitinformation.h> -#include <utils/utilsicons.h> - -#include <QTimer> - -using ProjectExplorer::DeviceKitAspect; -using ProjectExplorer::IDevice; -using ProjectExplorer::RunControl; -using Utils::Id; -using ProjectExplorer::Target; - -namespace WinRt { -namespace Internal { - -WinRtRunner::WinRtRunner(RunControl *runControl) - : RunWorker(runControl) -{ - runControl->setIcon(Utils::Icons::RUN_SMALL_TOOLBAR); -} - -void WinRtRunner::start() -{ - if (m_state != StoppedState) - return; - - QTC_ASSERT(!m_runner, m_state = StoppedState; reportFailure(); return); - QString errorMessage; - m_runner = new WinRtRunnerHelper(this, &errorMessage); - if (!errorMessage.isEmpty()) { - reportFailure(errorMessage); - return; - } - connect(m_runner, &WinRtRunnerHelper::started, this, &WinRtRunner::onProcessStarted); - connect(m_runner, &WinRtRunnerHelper::finished, this, &WinRtRunner::onProcessFinished); - connect(m_runner, &WinRtRunnerHelper::error, this, &WinRtRunner::onProcessError); - m_state = StartingState; - m_runner->start(); -} - -void WinRtRunner::stop() -{ - if (m_state == StoppedState) - return; - - m_runner->stop(); -} - -void WinRtRunner::onProcessStarted() -{ - QTC_CHECK(m_state == StartingState); - m_state = StartedState; - reportStarted(); -} - -void WinRtRunner::onProcessFinished() -{ - QTC_CHECK(m_state == StartedState); - onProcessError(); -} - -void WinRtRunner::onProcessError() -{ - QTC_ASSERT(m_runner, return); - m_runner->disconnect(); - m_runner->deleteLater(); - m_runner = nullptr; - m_state = StoppedState; - reportStopped(); -} - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtruncontrol.h b/src/plugins/winrt/winrtruncontrol.h deleted file mode 100644 index 6105525d15d..00000000000 --- a/src/plugins/winrt/winrtruncontrol.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "winrtdevice.h" - -#include <projectexplorer/runcontrol.h> -#include <utils/qtcprocess.h> - -namespace WinRt { -namespace Internal { - -class WinRtRunnerHelper; - -class WinRtRunner : public ProjectExplorer::RunWorker -{ - Q_OBJECT -public: - explicit WinRtRunner(ProjectExplorer::RunControl *runControl); - - void start() override; - void stop() override; - -private: - enum State { StartingState, StartedState, StoppedState }; - - void onProcessStarted(); - void onProcessFinished(); - void onProcessError(); - - State m_state = StoppedState; - Utils::QtcProcess *m_process = nullptr; - WinRtRunnerHelper *m_runner = nullptr; -}; - -} // namespace Internal -} // namespace WinRt diff --git a/src/plugins/winrt/winrtrunnerhelper.cpp b/src/plugins/winrt/winrtrunnerhelper.cpp deleted file mode 100644 index 4340c2260c6..00000000000 --- a/src/plugins/winrt/winrtrunnerhelper.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "winrtrunnerhelper.h" - -#include "winrtconstants.h" -#include "winrtrunconfiguration.h" - -#include <coreplugin/idocument.h> - -#include <projectexplorer/buildtargetinfo.h> -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/project.h> -#include <projectexplorer/runcontrol.h> -#include <projectexplorer/target.h> - -#include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> -#include <utils/qtcprocess.h> - -#include <QDir> - -using namespace ProjectExplorer; - -using namespace WinRt; -using namespace WinRt::Internal; - -WinRtRunnerHelper::WinRtRunnerHelper(ProjectExplorer::RunWorker *runWorker, QString *errorMessage) - : QObject(runWorker) - , m_worker(runWorker) -{ - auto runControl = runWorker->runControl(); - - m_device = runWorker->device().dynamicCast<const WinRtDevice>(); - - const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(runControl->kit()); - if (!qt) { - *errorMessage = tr("The current kit has no Qt version."); - return; - } - - m_runnerFilePath = qt->hostBinPath().toString() + QStringLiteral("/winrtrunner.exe"); - if (!QFile::exists(m_runnerFilePath)) { - *errorMessage = tr("Cannot find winrtrunner.exe in \"%1\".").arg( - QDir::toNativeSeparators(qt->hostBinPath().toString())); - return; - } - - m_executableFilePath = runControl->targetFilePath(); - - if (m_executableFilePath.isEmpty()) { - *errorMessage = tr("Cannot determine the executable file path for \"%1\".") - .arg(runControl->projectFilePath().toUserOutput()); - return; - } - - // ### we should not need to append ".exe" here. - if (!m_executableFilePath.endsWith(QLatin1String(".exe"))) - m_executableFilePath = m_executableFilePath + QStringLiteral(".exe"); - - bool loopbackExemptClient = false; - bool loopbackExemptServer = false; - if (auto aspect = runControl->aspect<ArgumentsAspect>()) - m_arguments = aspect->arguments(runControl->macroExpander()); - if (auto aspect = runControl->aspect<UninstallAfterStopAspect>()) - m_uninstallAfterStop = aspect->value(); - if (auto aspect = runControl->aspect<LoopbackExemptClientAspect>()) - loopbackExemptClient = aspect->value(); - if (auto aspect = runControl->aspect<LoopbackExemptServerAspect>()) - loopbackExemptServer = aspect->value(); - if (loopbackExemptClient && loopbackExemptServer) - m_loopbackArguments = QStringList{"--loopbackexempt", "clientserver"}; - else if (loopbackExemptClient) - m_loopbackArguments = QStringList{"--loopbackexempt", "client"}; - else if (loopbackExemptServer) - m_loopbackArguments = QStringList{"--loopbackexempt", "server"}; -} - -void WinRtRunnerHelper::appendMessage(const QString &message, Utils::OutputFormat format) -{ - QTC_ASSERT(m_worker, return); - m_worker->appendMessage(message, format); -} - -void WinRtRunnerHelper::debug(const QString &debuggerExecutable, const QString &debuggerArguments) -{ - m_debuggerExecutable = debuggerExecutable; - m_debuggerArguments = debuggerArguments; - startWinRtRunner(Debug); -} - -void WinRtRunnerHelper::start() -{ - startWinRtRunner(Start); -} - -void WinRtRunnerHelper::stop() -{ - if (m_process) - m_process->interrupt(); - else - startWinRtRunner(Stop); -} - -bool WinRtRunnerHelper::waitForStarted(int msecs) -{ - QTC_ASSERT(m_process, return false); - return m_process->waitForStarted(msecs); -} - -void WinRtRunnerHelper::onProcessReadyReadStdOut() -{ - QTC_ASSERT(m_process, return); - appendMessage(QString::fromLocal8Bit(m_process->readAllStandardOutput()), Utils::StdOutFormat); -} - -void WinRtRunnerHelper::onProcessReadyReadStdErr() -{ - QTC_ASSERT(m_process, return); - appendMessage(QString::fromLocal8Bit(m_process->readAllStandardError()), Utils::StdErrFormat); -} - -void WinRtRunnerHelper::onProcessFinished() -{ - QTC_ASSERT(m_process, return); - m_process->disconnect(); - m_process->deleteLater(); - m_process = nullptr; - emit finished(); -} - -void WinRtRunnerHelper::onProcessError(QProcess::ProcessError processError) -{ - QTC_ASSERT(m_process, return); - appendMessage(tr("Error while executing the WinRT Runner Tool: %1\n").arg( - m_process->errorString()), Utils::ErrorMessageFormat); - m_process->disconnect(); - m_process->deleteLater(); - m_process = nullptr; - emit error(processError); -} - -void WinRtRunnerHelper::startWinRtRunner(const RunConf &conf) -{ - using namespace Utils; - CommandLine cmdLine(FilePath::fromString(m_runnerFilePath), {}); - if (m_device) { - cmdLine.addArg("--device"); - cmdLine.addArg(QString::number(m_device->deviceId())); - } - - QtcProcess *process = nullptr; - bool connectProcess = false; - - switch (conf) { - case Debug: - cmdLine.addArg("--debug"); - cmdLine.addArg(m_debuggerExecutable); - if (!m_debuggerArguments.isEmpty()) { - cmdLine.addArg("--debugger-arguments"); - cmdLine.addArg(m_debuggerArguments); - } - Q_FALLTHROUGH(); - case Start: - cmdLine.addArgs({"--start", "--stop", "--wait", "0"}); - connectProcess = true; - QTC_ASSERT(!m_process, m_process->deleteLater()); - m_process = new QtcProcess(this); - process = m_process; - break; - case Stop: - cmdLine.addArg("--stop"); - process = new QtcProcess(this); - break; - } - - if (m_device->type() == Constants::WINRT_DEVICE_TYPE_LOCAL) - cmdLine.addArgs({"--profile", "appx"}); - else if (m_device->type() == Constants::WINRT_DEVICE_TYPE_PHONE || - m_device->type() == Constants::WINRT_DEVICE_TYPE_EMULATOR) - cmdLine.addArgs({"--profile", "appxphone"}); - - cmdLine.addArgs(m_loopbackArguments); - cmdLine.addArg(m_executableFilePath.toString()); - cmdLine.addArgs(m_arguments, CommandLine::Raw); - - appendMessage(cmdLine.toUserOutput(), NormalMessageFormat); - - if (connectProcess) { - connect(process, &QtcProcess::started, this, &WinRtRunnerHelper::started); - connect(process, &QtcProcess::finished, this, &WinRtRunnerHelper::onProcessFinished); - connect(process, &QtcProcess::errorOccurred, this, &WinRtRunnerHelper::onProcessError); - connect(process, &QtcProcess::readyReadStandardOutput, this, &WinRtRunnerHelper::onProcessReadyReadStdOut); - connect(process, &QtcProcess::readyReadStandardError, this, &WinRtRunnerHelper::onProcessReadyReadStdErr); - } - - process->setUseCtrlCStub(true); - process->setCommand(cmdLine); - process->setEnvironment(m_worker->runControl()->buildEnvironment()); - process->setWorkingDirectory(m_executableFilePath.absolutePath()); - process->start(); -} diff --git a/src/plugins/winrt/winrtrunnerhelper.h b/src/plugins/winrt/winrtrunnerhelper.h deleted file mode 100644 index 1e790de56aa..00000000000 --- a/src/plugins/winrt/winrtrunnerhelper.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "winrtdevice.h" - -#include <utils/environment.h> -#include <utils/filepath.h> -#include <utils/outputformat.h> - -#include <QObject> -#include <QProcess> - - -namespace Utils { class QtcProcess; } -namespace ProjectExplorer { class RunWorker; } - -namespace WinRt { -namespace Internal { - -class WinRtRunnerHelper : public QObject -{ - Q_OBJECT -public: - WinRtRunnerHelper(ProjectExplorer::RunWorker *runWorker, QString *errorMessage); - - void debug(const QString &debuggerExecutable, const QString &debuggerArguments = QString()); - void start(); - - void stop(); - - bool waitForStarted(int msecs = 10000); - -signals: - void started(); - void finished(); - void error(QProcess::ProcessError error); - -private: - enum RunConf { Start, Stop, Debug }; - - void onProcessReadyReadStdOut(); - void onProcessReadyReadStdErr(); - void onProcessFinished(); - void onProcessError(QProcess::ProcessError processError); - - void startWinRtRunner(const RunConf &conf); - void appendMessage(const QString &message, Utils::OutputFormat format); - - ProjectExplorer::RunWorker *m_worker = nullptr; - WinRtDevice::ConstPtr m_device; - QString m_runnerFilePath; - Utils::FilePath m_executableFilePath; - QString m_debuggerExecutable; - QString m_debuggerArguments; - QString m_arguments; - QStringList m_loopbackArguments; - bool m_uninstallAfterStop = false; - Utils::QtcProcess *m_process = nullptr; -}; - -} // namespace WinRt -} // namespace Internal diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 79878f18d3a..c9ee827bd9e 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -46,7 +46,6 @@ add_subdirectory(screenshotcropper) add_subdirectory(sdktool) add_subdirectory(valgrindfake) add_subdirectory(wininterrupt) ## windows only -add_subdirectory(winrtdebughelper) ## windows only if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/perfparser/CMakeLists.txt) add_subdirectory(perfparser) diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index a6116294e1f..8e9524b453a 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -9921,43 +9921,6 @@ id="path6333" /> </g> <g - id="src/plugins/winrt/images/winrtdevice" - transform="translate(-206,-74)"> - <use - transform="translate(561,337)" - style="display:inline" - x="0" - y="0" - xlink:href="#backgroundRect_32_28" - id="use5913-0-8-1-5-6-8-11" - width="100%" - height="100%" /> - <path - sodipodi:nodetypes="cccccccccccccccccccc" - style="fill:#000000;fill-opacity:1" - inkscape:connector-curvature="0" - d="m 607,391 h 10 v -10.16502 l -10,1.1313 z m 12,-10.3911 V 391 h 12 v -11.74108 z m 0,22.75938 12,1.34794 V 393 h -12 z m -12,-1.35943 10,1.12952 V 393 h -10 z" - id="path5333-0" /> - </g> - <g - id="src/plugins/winrt/images/winrtdevicesmall" - transform="translate(-238,-58)"> - <use - x="0" - y="0" - xlink:href="#backgroundRect" - id="use6054-3" - width="100%" - height="100%" - transform="translate(650,-41)" /> - <path - sodipodi:nodetypes="cccccccccccccccccccc" - style="fill:#000000;fill-opacity:1" - inkscape:connector-curvature="0" - d="M 636.00003,403 H 641 v -4.67861 l -4.99997,0.67433 z M 642,398.18129 V 403 h 6.00003 v -5.6287 z m 0,10.72061 6.00003,0.81345 V 404 H 642 Z M 636.00003,408.09093 641,408.7667 V 404 h -4.99997 z" - id="path5333" /> - </g> - <g id="src/plugins/debugger/images/debugger_stop_mask" transform="translate(-711,333)" style="display:inline"> diff --git a/src/tools/processlauncher/CMakeLists.txt b/src/tools/processlauncher/CMakeLists.txt index e0064c62b34..47acf9c1e68 100644 --- a/src/tools/processlauncher/CMakeLists.txt +++ b/src/tools/processlauncher/CMakeLists.txt @@ -12,6 +12,7 @@ add_qtc_executable(qtcreator_processlauncher processlauncher-main.cpp ${UTILSDIR}/launcherpackets.cpp ${UTILSDIR}/launcherpackets.h + ${UTILSDIR}/processenums.h ${UTILSDIR}/processreaper.cpp ${UTILSDIR}/processreaper.h ${UTILSDIR}/processutils.cpp diff --git a/src/tools/processlauncher/launchersockethandler.cpp b/src/tools/processlauncher/launchersockethandler.cpp index 21e7e7624e0..952d6516f2b 100644 --- a/src/tools/processlauncher/launchersockethandler.cpp +++ b/src/tools/processlauncher/launchersockethandler.cpp @@ -59,13 +59,20 @@ LauncherSocketHandler::LauncherSocketHandler(QString serverPath, QObject *parent LauncherSocketHandler::~LauncherSocketHandler() { + for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) { + Process *p = it.value(); + if (p->state() != QProcess::NotRunning) + logWarn(QStringLiteral("Shutting down while process %1 is running").arg(p->program())); + ProcessReaper::reap(p); + } + m_socket->disconnect(); - if (m_socket->state() != QLocalSocket::UnconnectedState) { - logWarn("socket handler destroyed while connection was active"); + m_socket->disconnectFromServer(); + if (m_socket->state() != QLocalSocket::UnconnectedState + && !m_socket->waitForDisconnected()) { + logWarn("Could not disconnect from server"); m_socket->close(); } - for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) - ProcessReaper::reap(it.value()); } void LauncherSocketHandler::start() @@ -74,11 +81,7 @@ void LauncherSocketHandler::start() this, &LauncherSocketHandler::handleSocketClosed); connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocketHandler::handleSocketData); connect(m_socket, -#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) - static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error), -#else &QLocalSocket::errorOccurred, -#endif this, &LauncherSocketHandler::handleSocketError); m_socket->connectToServer(m_serverPath); } @@ -89,7 +92,7 @@ void LauncherSocketHandler::handleSocketData() if (!m_packetParser.parse()) return; } catch (const PacketParser::InvalidPacketSizeException &e) { - logWarn(QStringLiteral("Internal protocol error: invalid packet size %1.") + logWarn(QStringLiteral("Internal protocol error: Invalid packet size %1") .arg(e.size)); return; } @@ -107,7 +110,7 @@ void LauncherSocketHandler::handleSocketData() handleShutdownPacket(); return; default: - logWarn(QStringLiteral("Internal protocol error: invalid packet type %1.") + logWarn(QStringLiteral("Internal protocol error: Invalid packet type %1") .arg(static_cast<int>(m_packetParser.type()))); return; } @@ -117,7 +120,7 @@ void LauncherSocketHandler::handleSocketData() void LauncherSocketHandler::handleSocketError() { if (m_socket->error() != QLocalSocket::PeerClosedError) { - logError(QStringLiteral("socket error: %1").arg(m_socket->errorString())); + logError(QStringLiteral("Socket error: %1").arg(m_socket->errorString())); m_socket->disconnect(); qApp->quit(); } @@ -125,12 +128,7 @@ void LauncherSocketHandler::handleSocketError() void LauncherSocketHandler::handleSocketClosed() { - for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) { - if (it.value()->state() != QProcess::NotRunning) { - logWarn("client closed connection while process still running"); - break; - } - } + logWarn("The connection has closed unexpectedly, shutting down"); m_socket->disconnect(); qApp->quit(); } @@ -194,7 +192,7 @@ void LauncherSocketHandler::handleStartPacket() if (!process) process = setupProcess(m_packetParser.token()); if (process->state() != QProcess::NotRunning) { - logWarn("got start request while process was running"); + logWarn("Got start request while process was running"); return; } const auto packet = LauncherPacket::extractPacket<StartProcessPacket>( @@ -203,8 +201,8 @@ void LauncherSocketHandler::handleStartPacket() process->setEnvironment(packet.env); process->setWorkingDirectory(packet.workingDir); // Forwarding is handled by the LauncherInterface - process->setProcessChannelMode(packet.channelMode == QProcess::MergedChannels ? - QProcess::MergedChannels : QProcess::SeparateChannels); + process->setProcessChannelMode(packet.processChannelMode == QProcess::MergedChannels + ? QProcess::MergedChannels : QProcess::SeparateChannels); process->setStandardInputFile(packet.standardInputFile); ProcessStartHandler *handler = process->processStartHandler(); handler->setProcessMode(packet.processMode); @@ -224,11 +222,11 @@ void LauncherSocketHandler::handleWritePacket() { Process * const process = m_processes.value(m_packetParser.token()); if (!process) { - logWarn("got write request for unknown process"); + logWarn("Got write request for unknown process"); return; } if (process->state() != QProcess::Running) { - logDebug("can't write into not running process"); + logDebug("Can't write into not running process"); return; } const auto packet = LauncherPacket::extractPacket<WritePacket>( @@ -243,13 +241,13 @@ void LauncherSocketHandler::handleStopPacket() if (!process) { // This can happen when the process finishes on its own at about the same time the client // sends the request. In this case the process was already deleted. - logDebug("got stop request for unknown process"); + logDebug("Got stop request for unknown process"); return; } if (process->state() == QProcess::NotRunning) { // This shouldn't happen, since as soon as process finishes or error occurrs // the process is being removed. - logWarn("got stop request when process was not running"); + logWarn("Got stop request when process was not running"); } else { // We got the client request to stop the starting / running process. // We report process exit to the client. @@ -266,14 +264,7 @@ void LauncherSocketHandler::handleStopPacket() void LauncherSocketHandler::handleShutdownPacket() { - logDebug("got shutdown request, closing down"); - for (auto it = m_processes.cbegin(); it != m_processes.cend(); ++it) { - it.value()->disconnect(); - if (it.value()->state() != QProcess::NotRunning) { - logWarn("got shutdown request while process was running"); - it.value()->terminate(); - } - } + logDebug("Got shutdown request, closing down"); m_socket->disconnect(); qApp->quit(); } @@ -299,8 +290,8 @@ Process *LauncherSocketHandler::setupProcess(quintptr token) void LauncherSocketHandler::removeProcess(quintptr token) { - const auto it = m_processes.find(token); - if (it == m_processes.end()) + const auto it = m_processes.constFind(token); + if (it == m_processes.constEnd()) return; Process *process = it.value(); diff --git a/src/tools/processlauncher/processlauncher.qbs b/src/tools/processlauncher/processlauncher.qbs index bdcde4ac34d..2b00bf89718 100644 --- a/src/tools/processlauncher/processlauncher.qbs +++ b/src/tools/processlauncher/processlauncher.qbs @@ -29,6 +29,7 @@ QtcTool { files: [ "launcherpackets.cpp", "launcherpackets.h", + "processenums.h", "processreaper.cpp", "processreaper.h", "processutils.cpp", diff --git a/src/tools/tools.qbs b/src/tools/tools.qbs index bcd94e0f24c..2ebd01f9c93 100644 --- a/src/tools/tools.qbs +++ b/src/tools/tools.qbs @@ -17,7 +17,6 @@ Project { "sdktool/sdktool.qbs", "valgrindfake/valgrindfake.qbs", "iostool/iostool.qbs", - "winrtdebughelper/winrtdebughelper.qbs" ].concat(project.additionalTools) Project { diff --git a/src/tools/winrtdebughelper/CMakeLists.txt b/src/tools/winrtdebughelper/CMakeLists.txt deleted file mode 100644 index d979f16f7b1..00000000000 --- a/src/tools/winrtdebughelper/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -if (NOT WIN32) - return() -endif() - -add_qtc_executable(winrtdebughelper - SOURCES - winrtdebughelper.cpp -) diff --git a/src/tools/winrtdebughelper/winrtdebughelper.cpp b/src/tools/winrtdebughelper/winrtdebughelper.cpp deleted file mode 100644 index f871dbf345d..00000000000 --- a/src/tools/winrtdebughelper/winrtdebughelper.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include <windows.h> -#include <stdio.h> -#include <stdlib.h> - -int main(int argc, char *argv[]) -{ - int pid = -1; - const size_t maxPipeNameSize = 256; - wchar_t pipeName[maxPipeNameSize] = {0}; - - for (int i = 0; i < argc - 1; ++i) { - if (!strcmp(argv[i], "-t")) { - ++i; - if (swprintf(pipeName, maxPipeNameSize, L"%hs", argv[i]) < 0) - return 0; // Pipe name too long - } else if (!strcmp(argv[i], "-p")) { - ++i; - - // check if -p is followed by a number - const char *pidString = argv[i]; - char *end; - pid = strtoul(pidString, &end, 0); - if (*end != 0) - return 0; - } - } - - if (pid < 0) - return 0; - - if (*pipeName == 0) - swprintf(pipeName, maxPipeNameSize, L"\\\\.\\pipe\\QtCreatorWinRtDebugPIDPipe"); - HANDLE pipe; - while (true) { - pipe = CreateFile(pipeName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (pipe != INVALID_HANDLE_VALUE) - break; - if ((GetLastError() != ERROR_PIPE_BUSY) || (!WaitNamedPipe(pipeName, 10000))) - return 0; - } - - const size_t msgBufferSize = 15; - char pidMessageBuffer[msgBufferSize]; - int length = sprintf_s(pidMessageBuffer, msgBufferSize, "PID:%d", pid); - if (length >= 0) - WriteFile(pipe, pidMessageBuffer, length, NULL, NULL); - - return 0; -} diff --git a/src/tools/winrtdebughelper/winrtdebughelper.qbs b/src/tools/winrtdebughelper/winrtdebughelper.qbs deleted file mode 100644 index d4d27a12ae4..00000000000 --- a/src/tools/winrtdebughelper/winrtdebughelper.qbs +++ /dev/null @@ -1,12 +0,0 @@ -import qbs 1.0 - -QtcTool { - name: "winrtdebughelper" - condition: qbs.targetOS.contains("windows") - - Depends { name: "Qt.network" } - - files: [ - "winrtdebughelper.cpp", - ] -} diff --git a/tests/auto/android/tst_avdmanageroutputparser.cpp b/tests/auto/android/tst_avdmanageroutputparser.cpp index ca3dcda19c5..61157f75ebb 100644 --- a/tests/auto/android/tst_avdmanageroutputparser.cpp +++ b/tests/auto/android/tst_avdmanageroutputparser.cpp @@ -58,13 +58,10 @@ void tst_AvdManagerOutputParser::parse_data() << AndroidDeviceInfoList({{"", "Test", {"x86"}, - "Google APIs (Google Inc.)", - "Galaxy Nexus (Google)", - "", - "512 MB", -1, IDevice::DeviceConnected, - IDevice::Emulator}}) + IDevice::Emulator, + Utils::FilePath::fromString(":Test.avd")}}) << QStringList(); QTest::newRow("two") << "Available Android Virtual Devices:\n" @@ -84,23 +81,18 @@ void tst_AvdManagerOutputParser::parse_data() << AndroidDeviceInfoList({{"", "Test", {"x86"}, - "Google APIs (Google Inc.)", - "Galaxy Nexus (Google)", - "", - "512 MB", -1, IDevice::DeviceConnected, - IDevice::Emulator}, + IDevice::Emulator, + Utils::FilePath::fromString(":Test.avd")}, {"", "TestTablet", {"x86"}, - "Google APIs (Google Inc.)", - "7in WSVGA (Tablet) (Generic)", - "", - "256 MB", -1, IDevice::DeviceConnected, - IDevice::Emulator}}) + IDevice::Emulator, + Utils::FilePath::fromString(":TestTablet.avd")}} + ) << QStringList(); } diff --git a/tests/auto/debugger/tst_gdb.cpp b/tests/auto/debugger/tst_gdb.cpp index 6de43aa3da8..57f6858a2ea 100644 --- a/tests/auto/debugger/tst_gdb.cpp +++ b/tests/auto/debugger/tst_gdb.cpp @@ -114,6 +114,10 @@ void tst_gdb::version_data() << "GNU gdb (GDB) 7.3 qnx (rev. 613)" << 70300 << 613 << false << true; + QTest::newRow("QNX71") + << "GNU gdb (GDB) 8.2.1 [qnx710 r1469] (STABLE)" + << 80201 << 710 << false << true; + QTest::newRow("rubenvb") << "GNU gdb (rubenvb-4.7.2-release) 7.5.50.20120920-cvs" << 70550 << 20120920 << false << false; diff --git a/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp b/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp index 20fb60cd538..406718ac279 100644 --- a/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp +++ b/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp @@ -26,8 +26,6 @@ #include <languageserverprotocol/basemessage.h> #include <languageserverprotocol/jsonobject.h> #include <languageserverprotocol/jsonrpcmessages.h> -#include <utils/mimetypes/mimetype.h> -#include <utils/mimetypes/mimedatabase.h> #include <QTextCodec> #include <QtTest> diff --git a/tests/auto/ssh/tst_ssh.cpp b/tests/auto/ssh/tst_ssh.cpp index df26047d5bd..cce90e79ba5 100644 --- a/tests/auto/ssh/tst_ssh.cpp +++ b/tests/auto/ssh/tst_ssh.cpp @@ -182,8 +182,8 @@ void tst_Ssh::remoteProcess() SshRemoteProcessRunner runner; QEventLoop loop; connect(&runner, &SshRemoteProcessRunner::connectionError, &loop, &QEventLoop::quit); - connect(&runner, &SshRemoteProcessRunner::processStarted, &loop, &QEventLoop::quit); - connect(&runner, &SshRemoteProcessRunner::processClosed, &loop, &QEventLoop::quit); + connect(&runner, &SshRemoteProcessRunner::started, &loop, &QEventLoop::quit); + connect(&runner, &SshRemoteProcessRunner::finished, &loop, &QEventLoop::quit); connect(&runner, &SshRemoteProcessRunner::readyReadStandardOutput, [&remoteStdout, &runner] { remoteStdout += runner.readAllStandardOutput(); }); connect(&runner, &SshRemoteProcessRunner::readyReadStandardError, @@ -199,8 +199,8 @@ void tst_Ssh::remoteProcess() timer.stop(); QVERIFY2(runner.lastConnectionErrorString().isEmpty(), qPrintable(runner.lastConnectionErrorString())); - QVERIFY2(runner.processErrorString().isEmpty(), qPrintable(runner.processErrorString())); - QVERIFY(runner.isProcessRunning()); // Event loop exit should have been triggered by started(). + QVERIFY2(runner.errorString().isEmpty(), qPrintable(runner.errorString())); + QVERIFY(runner.isRunning()); // Event loop exit should have been triggered by started(). QVERIFY2(remoteStdout.isEmpty(), remoteStdout.constData()); QVERIFY2(remoteStderr.isEmpty(), remoteStderr.constData()); @@ -212,14 +212,14 @@ void tst_Ssh::remoteProcess() loop.exec(); QVERIFY(timer.isActive()); timer.stop(); - QVERIFY(!runner.isProcessRunning()); + QVERIFY(!runner.isRunning()); QVERIFY2(runner.lastConnectionErrorString().isEmpty(), qPrintable(runner.lastConnectionErrorString())); if (isBlocking) { - QVERIFY(runner.processExitStatus() == QProcess::CrashExit - || runner.processExitCode() != 0); + QVERIFY(runner.exitStatus() == QProcess::CrashExit + || runner.exitCode() != 0); } else { - QCOMPARE(successExpected, runner.processExitCode() == 0); + QCOMPARE(successExpected, runner.exitCode() == 0); } QCOMPARE(stdoutExpected, !remoteStdout.isEmpty()); QCOMPARE(stderrExpected, !remoteStderr.isEmpty()); @@ -240,7 +240,7 @@ void tst_Ssh::remoteProcessChannels() SshRemoteProcessPtr echoProcess = connection.createRemoteProcess("printf " + QString::fromUtf8(testString) + " >&2"); QEventLoop loop; - connect(echoProcess.get(), &SshRemoteProcess::done, &loop, &QEventLoop::quit); + connect(echoProcess.get(), &SshRemoteProcess::finished, &loop, &QEventLoop::quit); connect(echoProcess.get(), &Utils::QtcProcess::readyReadStandardError, [&remoteData, p = echoProcess.get()] { remoteData += p->readAllStandardError(); }); connect(echoProcess.get(), &SshRemoteProcess::readyReadStandardOutput, @@ -272,11 +272,11 @@ void tst_Ssh::remoteProcessInput() SshConnection connection(params); QVERIFY(waitForConnection(connection)); - SshRemoteProcessPtr catProcess = connection.createRemoteProcess("/bin/cat", - Utils::ProcessMode::Writer); + SshRemoteProcessPtr catProcess = connection.createRemoteProcess("/bin/cat"); + catProcess->setProcessMode(Utils::ProcessMode::Writer); QEventLoop loop; connect(catProcess.get(), &SshRemoteProcess::started, &loop, &QEventLoop::quit); - connect(catProcess.get(), &SshRemoteProcess::done, &loop, &QEventLoop::quit); + connect(catProcess.get(), &SshRemoteProcess::finished, &loop, &QEventLoop::quit); QTimer timer; QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); timer.setSingleShot(true); diff --git a/tests/auto/utils/qtcprocess/CMakeLists.txt b/tests/auto/utils/qtcprocess/CMakeLists.txt index daa4dea7d12..5d7985a2822 100644 --- a/tests/auto/utils/qtcprocess/CMakeLists.txt +++ b/tests/auto/utils/qtcprocess/CMakeLists.txt @@ -3,6 +3,6 @@ file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_L add_qtc_test(tst_qtcprocess DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\"" - DEPENDS Utils + DEPENDS Utils app_version SOURCES tst_qtcprocess.cpp ) diff --git a/tests/auto/utils/qtcprocess/qtcprocess.qbs b/tests/auto/utils/qtcprocess/qtcprocess.qbs index 966cf6cbe55..04ffafef1c7 100644 --- a/tests/auto/utils/qtcprocess/qtcprocess.qbs +++ b/tests/auto/utils/qtcprocess/qtcprocess.qbs @@ -3,6 +3,8 @@ import qbs.FileInfo QtcAutotest { name: "QtcProcess autotest" Depends { name: "Utils" } + Depends { name: "app_version_header" } + files: "tst_qtcprocess.cpp" cpp.defines: { var defines = base; diff --git a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp b/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp index ac0bf2f862a..c4ad1563c14 100644 --- a/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp +++ b/tests/auto/utils/qtcprocess/tst_qtcprocess.cpp @@ -23,13 +23,17 @@ ** ****************************************************************************/ +#include <app/app_version.h> + #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/launcherinterface.h> #include <utils/porting.h> +#include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/singleton.h> #include <utils/stringutils.h> +#include <utils/temporarydirectory.h> #include <QElapsedTimer> #include <QRegularExpression> @@ -43,29 +47,41 @@ #include <fcntl.h> #endif - using namespace Utils; -// Many tests in this file need to start a new subprocess with custom code. -// In order to simplify things we don't produce separate executables, but invoke -// the same test process recursively and prior to the execution we set one of the -// following environment variables: - -const char kExitCodeSubProcessCode[] = "QTC_TST_QTCPROCESS_EXITCODE_CODE"; -const char kRunBlockingStdOutSubProcessWithEndl[] = "QTC_TST_QTCPROCESS_RUNBLOCKINGSTDOUT_WITHENDL"; -const char kLineCallback[] = "QTC_TST_QTCPROCESS_LINECALLBACK"; -const char kTestProcess[] = "QTC_TST_TEST_PROCESS"; -const char kForwardProcess[] = "QTC_TST_FORWARD_PROCESS"; -const char kForwardSubProcess[] = "QTC_TST_FORWARD_SUB_PROCESS"; -const char kBlockingProcess[] = "QTC_TST_BLOCKING_PROCESS"; - -// The variables above are meant to be different custom executables. Inside initTestCase() -// we are detecting if one of these variables is set and invoke directly a respective custom -// executable code, which always ends up with a call to exit(). In this case we need to stop -// the recursion, as from the test point of view we meant to execute only our custom code -// without further execution of the test itself. - -const char testProcessData[] = "Test process successfully executed."; +using SubProcessMain = std::function<void ()>; +static QMap<const char *, SubProcessMain> s_subProcesses = {}; + +static void invokeSubProcessIfRequired() +{ + for (auto it = s_subProcesses.constBegin(); it != s_subProcesses.constEnd(); ++it) { + if (qEnvironmentVariableIsSet(it.key())) + it.value()(); + } +} + +static void registerSubProcess(const char *envVar, const SubProcessMain &main) +{ + s_subProcesses.insert(envVar, main); +} + +#define SUB_CREATOR_PROCESS(SubProcessClass)\ +class SubProcessClass\ +{\ +public:\ + SubProcessClass()\ + {\ + registerSubProcess(envVar(), &SubProcessClass::main);\ + }\ + static const char *envVar() { return m_envVar; }\ +private:\ + static void main();\ + static constexpr char m_envVar[] = "TST_QTC_PROCESS_" QTC_ASSERT_STRINGIFY(SubProcessClass);\ +};\ +\ +SubProcessClass m_ ## SubProcessClass + +const char simpleTestData[] = "Test process successfully executed."; const char forwardedOutputData[] = "This is the output message."; const char forwardedErrorData[] = "This is the error message."; const char runBlockingStdOutSubProcessMagicWord[] = "42"; @@ -78,83 +94,58 @@ const char lineCallbackData[] = "Rebasing (1/10)\r| <delay> Rebasing (2/10)\r| <delay> ...\r\n|" "And no end"; -static void exitCodeSubProcessMain() -{ - const int exitCode = qEnvironmentVariableIntValue(kExitCodeSubProcessCode); - std::cout << "Exiting with code:" << exitCode << std::endl; - exit(exitCode); -} - -static void blockingStdOutSubProcessMain() -{ - std::cout << "Wait for the Answer to the Ultimate Question of Life, " - "The Universe, and Everything..." << std::endl; - QThread::msleep(300); - std::cout << runBlockingStdOutSubProcessMagicWord << "...Now wait for the question..."; - if (qEnvironmentVariable(kRunBlockingStdOutSubProcessWithEndl) == "true") - std::cout << std::endl; - QThread::msleep(5000); - exit(0); -} - -static void lineCallbackMain() +static Environment subEnvironment(const char *envVar, const QString &envVal) { -#ifdef Q_OS_WIN - // Prevent \r\n -> \r\r\n translation. - setmode(fileno(stderr), O_BINARY); -#endif - fprintf(stderr, "%s", QByteArray(lineCallbackData).replace('|', "").data()); - exit(0); + Environment env = Environment::systemEnvironment(); + env.set(envVar, envVal); + return env; } -static void testProcessSubProcessMain() +static CommandLine subCommandLine() { - std::cout << testProcessData << std::endl; - exit(0); -} - -// Since we want to test whether the process forwards its channels or not, we can't just create -// a process and start it, because in this case there is no way on how to check whether something -// went into out output channels or not. - -// So we start two processes in chain instead. On the beginning the processChannelForwarding() -// test starts the "testForwardProcessMain" - this one will start another process -// "testForwardSubProcessMain" with forwarding options. The "testForwardSubProcessMain" -// is very simple - it just puts something to the output and the error channels. -// Then "testForwardProcessMain" either forwards these channels or not - we check it in the outer -// processChannelForwarding() test. -static void testForwardProcessMain() -{ - Environment env = Environment::systemEnvironment(); - env.set(kForwardSubProcess, {}); QStringList args = QCoreApplication::arguments(); const QString binary = args.takeFirst(); - const CommandLine command(FilePath::fromString(binary), args); - - QtcProcess process; - const QProcess::ProcessChannelMode channelMode - = QProcess::ProcessChannelMode(qEnvironmentVariableIntValue(kForwardProcess)); - process.setProcessChannelMode(channelMode); - process.setCommand(command); - process.setEnvironment(env); - process.start(); - process.waitForFinished(); - exit(0); + const FilePath filePath = FilePath::fromString(QDir::currentPath()).resolvePath(binary); + return CommandLine(filePath, args); } -static void testForwardSubProcessMain() +class SubCreatorConfig { - std::cout << forwardedOutputData << std::endl; - std::cerr << forwardedErrorData << std::endl; - exit(0); -} +public: + SubCreatorConfig(const char *envVar, const QString &envVal) + : m_environment(subEnvironment(envVar, envVal)) + , m_commandLine(subCommandLine()) {} -static void blockingProcessSubProcessMain() + void setupSubProcess(QtcProcess *subProcess) + { + subProcess->setEnvironment(m_environment); + subProcess->setCommand(m_commandLine); + } +private: + const Environment m_environment; + const CommandLine m_commandLine; +}; + +static std::atomic_int s_processCounter = 0; +static const bool s_removeSingletonsOnLastProcess = false; +// The use of this class (when s_removeSingletonsOnLastProcess = true) guarantees that if all +// instances of QtcProcess are destructed at some point (i.e. after each test function ends) +// we also run process reaper's and process launcher's destructors. Otherwise +// (when s_removeSingletonsOnLastProcess = true) this is a no-op wrapper around QtcProcess. +class TestProcess : public QtcProcess { - std::cout << "Blocking process successfully executed." << std::endl; - while (true) - ; -} +public: + class TestProcessDeleter : public QObject + { + public: + TestProcessDeleter(QObject *parent) : QObject(parent) { s_processCounter.fetch_add(1); } + ~TestProcessDeleter() { + if ((s_processCounter.fetch_sub(1) == 1) && s_removeSingletonsOnLastProcess) + Utils::Singleton::deleteAll(); + } + }; + TestProcess() { new TestProcessDeleter(this); } +}; class MacroMapExpander : public AbstractMacroExpander { public: @@ -213,6 +204,29 @@ private slots: void cleanupTestCase(); private: + // Many tests in this file need to start a new subprocess with custom code. + // In order to simplify things we don't produce separate executables, but invoke + // the same test process recursively. These tests are embedded in classes, + // defined by the SUB_CREATOR_PROCESS macro. The macro defines a class alongside of the + // corresponding environment variable which is set prior to the execution of the subprocess. + // The following subprocess classes are defined: + + SUB_CREATOR_PROCESS(SimpleTest); + SUB_CREATOR_PROCESS(ExitCode); + SUB_CREATOR_PROCESS(RunBlockingStdOut); + SUB_CREATOR_PROCESS(LineCallback); + SUB_CREATOR_PROCESS(ProcessChannelForwarding); + SUB_CREATOR_PROCESS(SubProcessChannelForwarding); + SUB_CREATOR_PROCESS(KillBlockingProcess); + + // In order to get a value associated with the certain subprocess use SubProcessClass::envVar(). + // The classes above define different custom executables. Inside initTestCase() + // we are detecting if one of these variables is set (by a call to + // invokeSubProcessIfRequired()) and invoke directly a respective custom + // executable code, which always ends up with a call to exit(). In this way (by calling exit() + // by the end of each subprocess executable) we stop the recursion, as from the test point of + // view we meant to execute only our custom code without further execution of the test itself. + void iteratorEditsHelper(OsType osType); Environment envWindows; @@ -224,24 +238,20 @@ private: QString home; }; +void tst_QtcProcess::SimpleTest::main() +{ + std::cout << simpleTestData << std::endl; + exit(0); +} + void tst_QtcProcess::initTestCase() { + Utils::TemporaryDirectory::setMasterTemporaryDirectory(QDir::tempPath() + "/" + + Core::Constants::IDE_CASED_ID + "-XXXXXX"); Utils::LauncherInterface::setPathToLauncher(qApp->applicationDirPath() + '/' + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); - if (qEnvironmentVariableIsSet(kExitCodeSubProcessCode)) - exitCodeSubProcessMain(); - if (qEnvironmentVariableIsSet(kRunBlockingStdOutSubProcessWithEndl)) - blockingStdOutSubProcessMain(); - if (qEnvironmentVariableIsSet(kLineCallback)) - lineCallbackMain(); - if (qEnvironmentVariableIsSet(kTestProcess)) - testProcessSubProcessMain(); - if (qEnvironmentVariableIsSet(kForwardSubProcess)) - testForwardSubProcessMain(); - else if (qEnvironmentVariableIsSet(kForwardProcess)) - testForwardProcessMain(); - if (qEnvironmentVariableIsSet(kBlockingProcess)) - blockingProcessSubProcessMain(); + + invokeSubProcessIfRequired(); homeStr = QLatin1String("@HOME@"); home = QDir::homePath(); @@ -291,6 +301,7 @@ void tst_QtcProcess::cleanupTestCase() Q_DECLARE_METATYPE(ProcessArgs::SplitError) Q_DECLARE_METATYPE(Utils::OsType) +Q_DECLARE_METATYPE(Utils::ProcessResult) void tst_QtcProcess::splitArgs_data() { @@ -902,6 +913,13 @@ void tst_QtcProcess::iteratorEditsLinux() iteratorEditsHelper(OsTypeLinux); } +void tst_QtcProcess::ExitCode::main() +{ + const int exitCode = qEnvironmentVariableIntValue(envVar()); + std::cout << "Exiting with code:" << exitCode << std::endl; + exit(exitCode); +} + void tst_QtcProcess::exitCode_data() { QTest::addColumn<int>("exitCode"); @@ -920,94 +938,114 @@ void tst_QtcProcess::exitCode() { QFETCH(int, exitCode); - Environment env = Environment::systemEnvironment(); - env.set(kExitCodeSubProcessCode, QString::number(exitCode)); - QStringList args = QCoreApplication::arguments(); - const QString binary = args.takeFirst(); - const CommandLine command(FilePath::fromString(binary), args); - + SubCreatorConfig subConfig(ExitCode::envVar(), QString::number(exitCode)); { - QtcProcess qtcP; - qtcP.setCommand(command); - qtcP.setEnvironment(env); - qtcP.start(); - const bool finished = qtcP.waitForFinished(); + TestProcess process; + subConfig.setupSubProcess(&process); + process.start(); + const bool finished = process.waitForFinished(); QVERIFY(finished); - QCOMPARE(qtcP.exitCode(), exitCode); - QCOMPARE(qtcP.exitCode() == 0, qtcP.result() == QtcProcess::FinishedWithSuccess); + QCOMPARE(process.exitCode(), exitCode); + QCOMPARE(process.exitCode() == 0, process.result() == ProcessResult::FinishedWithSuccess); } { - QtcProcess sP; - sP.setCommand(command); - sP.setEnvironment(env); - sP.runBlocking(); + TestProcess process; + subConfig.setupSubProcess(&process); + process.runBlocking(); - QCOMPARE(sP.exitCode(), exitCode); - QCOMPARE(sP.exitCode() == 0, sP.result() == QtcProcess::FinishedWithSuccess); + QCOMPARE(process.exitCode(), exitCode); + QCOMPARE(process.exitCode() == 0, process.result() == ProcessResult::FinishedWithSuccess); } } +void tst_QtcProcess::RunBlockingStdOut::main() +{ + std::cout << "Wait for the Answer to the Ultimate Question of Life, " + "The Universe, and Everything..." << std::endl; + QThread::msleep(300); + std::cout << runBlockingStdOutSubProcessMagicWord << "...Now wait for the question..."; + if (qEnvironmentVariable(envVar()) == "true") + std::cout << std::endl; + else + std::cout << std::flush; // otherwise it won't reach the original process (will be buffered) + QThread::msleep(5000); + exit(0); +} + void tst_QtcProcess::runBlockingStdOut_data() { QTest::addColumn<bool>("withEndl"); QTest::addColumn<int>("timeOutS"); + QTest::addColumn<ProcessResult>("expectedResult"); + + // TerminatedAbnormally, since it didn't time out and callback stopped the process forcefully. + QTest::newRow("Short timeout with end of line") + << true << 2 << ProcessResult::TerminatedAbnormally; + + // Hang, since it times out, calls the callback handler and stops the process forcefully. + QTest::newRow("Short timeout without end of line") + << false << 2 << ProcessResult::Hang; - QTest::newRow("Terminated stdout delivered instantly") - << true - << 2; - QTest::newRow("Unterminated stdout lost: early timeout") - << false - << 2; - QTest::newRow("Unterminated stdout lost: hanging") - << false - << 20; + // FinishedWithSuccess, since it doesn't time out, it finishes process normally, + // calls the callback handler and tries to stop the process forcefully what is no-op + // at this point in time since the process is already finished. + QTest::newRow("Long timeout without end of line") + << false << 20 << ProcessResult::FinishedWithSuccess; } void tst_QtcProcess::runBlockingStdOut() { QFETCH(bool, withEndl); QFETCH(int, timeOutS); + QFETCH(ProcessResult, expectedResult); - QtcProcess sp; - QStringList args = QCoreApplication::arguments(); - const QString binary = args.takeFirst(); - sp.setCommand(CommandLine(FilePath::fromString(binary), args)); - Environment env = Environment::systemEnvironment(); - env.set(kRunBlockingStdOutSubProcessWithEndl, withEndl ? "true" : "false"); - sp.setEnvironment(env); - sp.setTimeoutS(timeOutS); + SubCreatorConfig subConfig(RunBlockingStdOut::envVar(), withEndl ? "true" : "false"); + TestProcess process; + subConfig.setupSubProcess(&process); + + process.setTimeoutS(timeOutS); bool readLastLine = false; - sp.setStdOutCallback([&readLastLine, &sp](const QString &out) { + process.setStdOutCallback([&readLastLine, &process](const QString &out) { if (out.startsWith(runBlockingStdOutSubProcessMagicWord)) { readLastLine = true; - sp.kill(); + process.kill(); } }); - sp.runBlocking(); + process.runBlocking(); // See also QTCREATORBUG-25667 for why it is a bad idea to use QtcProcess::runBlocking // with interactive cli tools. - QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue); - QVERIFY2(sp.result() != QtcProcess::Hang, "Process run did not time out."); - QEXPECT_FAIL("Unterminated stdout lost: early timeout", "", Continue); + QCOMPARE(process.result(), expectedResult); QVERIFY2(readLastLine, "Last line was read."); } +void tst_QtcProcess::LineCallback::main() +{ +#ifdef Q_OS_WIN + // Prevent \r\n -> \r\r\n translation. + _setmode(_fileno(stderr), O_BINARY); +#endif + fprintf(stderr, "%s", QByteArray(lineCallbackData).replace('|', "").data()); + exit(0); +} + void tst_QtcProcess::lineCallback() { - QtcProcess process; - QStringList args = QCoreApplication::arguments(); - const QString binary = args.takeFirst(); - process.setCommand(CommandLine(FilePath::fromString(binary), args)); - Environment env = Environment::systemEnvironment(); - env.set(kLineCallback, "Yes"); - process.setEnvironment(env); + SubCreatorConfig subConfig(LineCallback::envVar(), {}); + TestProcess process; + subConfig.setupSubProcess(&process); + QStringList lines = QString(lineCallbackData).split('|'); int lineNumber = 0; process.setStdErrLineCallback([lines, &lineNumber](const QString &actual) { - QString expected = lines.at(lineNumber++); + QString expected = lines.at(lineNumber); expected.replace("\r\n", "\n"); + // Omit some initial lines generated by Qt, e.g. + // Warning: Ignoring WAYLAND_DISPLAY on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway. + if (lineNumber == 0 && actual != expected) + return; + ++lineNumber; QCOMPARE(actual, expected); }); process.start(); @@ -1017,12 +1055,17 @@ void tst_QtcProcess::lineCallback() void tst_QtcProcess::lineCallbackIntern() { - QtcProcess process; + TestProcess process; QStringList lines = QString(lineCallbackData).split('|'); int lineNumber = 0; process.setStdOutLineCallback([lines, &lineNumber](const QString &actual) { - QString expected = lines.at(lineNumber++); + QString expected = lines.at(lineNumber); expected.replace("\r\n", "\n"); + // Omit some initial lines generated by Qt, e.g. + // Warning: Ignoring WAYLAND_DISPLAY on Gnome. Use QT_QPA_PLATFORM=wayland to run on Wayland anyway. + if (lineNumber == 0 && actual != expected) + return; + ++lineNumber; QCOMPARE(actual, expected); }); process.beginFeed(); @@ -1033,15 +1076,10 @@ void tst_QtcProcess::lineCallbackIntern() void tst_QtcProcess::waitForStartedAndFinished() { - Environment env = Environment::systemEnvironment(); - env.set(kTestProcess, {}); - QStringList args = QCoreApplication::arguments(); - const QString binary = args.takeFirst(); - const CommandLine command(FilePath::fromString(binary), args); + SubCreatorConfig subConfig(SimpleTest::envVar(), {}); + TestProcess process; + subConfig.setupSubProcess(&process); - QtcProcess process; - process.setCommand(command); - process.setEnvironment(env); process.start(); QThread::msleep(1000); // long enough for process to finish QVERIFY(process.waitForStarted()); @@ -1052,7 +1090,7 @@ void tst_QtcProcess::waitForStartedAndFinished() void tst_QtcProcess::notRunningAfterStartingNonExistingProgram() { - QtcProcess process; + TestProcess process; process.setCommand({ FilePath::fromString( "there_is_a_big_chance_that_executable_with_that_name_does_not_exists"), {} }); @@ -1081,6 +1119,40 @@ void tst_QtcProcess::notRunningAfterStartingNonExistingProgram() } } +// Since we want to test whether the process forwards its channels or not, we can't just create +// a process and start it, because in this case there is no way on how to check whether something +// went into our output channels or not. + +// So we start two processes in chain instead. On the beginning the processChannelForwarding() +// test starts the ProcessChannelForwarding::main() - this one will start another process +// SubProcessChannelForwarding::main() with forwarding options. +// The SubProcessChannelForwarding::main() is very simple - it just puts something to the output +// and the error channels. Then ProcessChannelForwarding::main() either forwards these channels +// or not - we check it in the outer processChannelForwarding() test. + +void tst_QtcProcess::SubProcessChannelForwarding::main() +{ + std::cout << forwardedOutputData << std::endl; + std::cerr << forwardedErrorData << std::endl; + exit(0); +} + +void tst_QtcProcess::ProcessChannelForwarding::main() +{ + const QProcess::ProcessChannelMode channelMode + = QProcess::ProcessChannelMode(qEnvironmentVariableIntValue(envVar())); + qunsetenv(envVar()); + + SubCreatorConfig subConfig(SubProcessChannelForwarding::envVar(), {}); + TestProcess process; + subConfig.setupSubProcess(&process); + + process.setProcessChannelMode(channelMode); + process.start(); + process.waitForFinished(); + exit(0); +} + void tst_QtcProcess::processChannelForwarding_data() { QTest::addColumn<QProcess::ProcessChannelMode>("channelMode"); @@ -1099,15 +1171,10 @@ void tst_QtcProcess::processChannelForwarding() QFETCH(bool, outputForwarded); QFETCH(bool, errorForwarded); - Environment env = Environment::systemEnvironment(); - env.set(kForwardProcess, QString::number(int(channelMode))); - QStringList args = QCoreApplication::arguments(); - const QString binary = args.takeFirst(); - const CommandLine command(FilePath::fromString(binary), args); + SubCreatorConfig subConfig(ProcessChannelForwarding::envVar(), QString::number(int(channelMode))); + TestProcess process; + subConfig.setupSubProcess(&process); - QtcProcess process; - process.setCommand(command); - process.setEnvironment(env); process.start(); QVERIFY(process.waitForFinished()); @@ -1156,19 +1223,21 @@ protected: bool MessageHandler::ok = true; QtMessageHandler MessageHandler::oldMessageHandler = 0; +void tst_QtcProcess::KillBlockingProcess::main() +{ + std::cout << "Blocking process successfully executed." << std::endl; + while (true) + ; +} + void tst_QtcProcess::killBlockingProcess() { - Environment env = Environment::systemEnvironment(); - env.set(kBlockingProcess, {}); - QStringList args = QCoreApplication::arguments(); - const QString binary = args.takeFirst(); - const CommandLine command(FilePath::fromString(binary), args); + SubCreatorConfig subConfig(KillBlockingProcess::envVar(), {}); MessageHandler msgHandler; { - QtcProcess process; - process.setCommand(command); - process.setEnvironment(env); + TestProcess process; + subConfig.setupSubProcess(&process); process.start(); QVERIFY(process.waitForStarted()); QVERIFY(!process.waitForFinished(1000)); @@ -1179,15 +1248,10 @@ void tst_QtcProcess::killBlockingProcess() void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead() { - Environment env = Environment::systemEnvironment(); - env.set(kTestProcess, {}); - QStringList args = QCoreApplication::arguments(); - const QString binary = args.takeFirst(); - const CommandLine command(FilePath::fromString(binary), args); + SubCreatorConfig subConfig(SimpleTest::envVar(), {}); + TestProcess process; + subConfig.setupSubProcess(&process); - QtcProcess process; - process.setCommand(command); - process.setEnvironment(env); process.start(); QVERIFY(process.waitForStarted()); @@ -1204,7 +1268,7 @@ void tst_QtcProcess::flushFinishedWhileWaitingForReadyRead() QCOMPARE(process.state(), QProcess::NotRunning); QVERIFY(!timer.hasExpired()); - QVERIFY(reply.contains(testProcessData)); + QVERIFY(reply.contains(simpleTestData)); } QTEST_MAIN(tst_QtcProcess) diff --git a/tests/manual/ssh/shell/shell.cpp b/tests/manual/ssh/shell/shell.cpp index a588a17c908..e7e96174fd6 100644 --- a/tests/manual/ssh/shell/shell.cpp +++ b/tests/manual/ssh/shell/shell.cpp @@ -75,7 +75,7 @@ void Shell::handleConnected() this, &Shell::handleRemoteStdout); connect(m_shell.get(), &SshRemoteProcess::readyReadStandardError, this, &Shell::handleRemoteStderr); - connect(m_shell.get(), &SshRemoteProcess::done, this, &Shell::handleChannelClosed); + connect(m_shell.get(), &SshRemoteProcess::finished, this, &Shell::handleChannelClosed); m_shell->start(); } @@ -95,10 +95,10 @@ void Shell::handleRemoteStderr() std::cerr << m_shell->readAllStandardError().data() << std::flush; } -void Shell::handleChannelClosed(const QString &error) +void Shell::handleChannelClosed() { std::cerr << "Shell closed. Exit code was " << m_shell->exitCode() << "." << std::endl; - QCoreApplication::exit(error.isEmpty() && m_shell->exitCode() == 0 + QCoreApplication::exit(m_shell->errorString().isEmpty() && m_shell->exitCode() == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/tests/manual/ssh/shell/shell.h b/tests/manual/ssh/shell/shell.h index afebee0de40..db8bcce9cac 100644 --- a/tests/manual/ssh/shell/shell.h +++ b/tests/manual/ssh/shell/shell.h @@ -50,7 +50,7 @@ private: void handleRemoteStdout(); void handleRemoteStderr(); void handleShellMessage(const QString &message); - void handleChannelClosed(const QString &error); + void handleChannelClosed(); void handleShellStarted(); void handleStdin(); |