From 175679ae608f0b295d761588d332f635b21bdf30 Mon Sep 17 00:00:00 2001 From: giy <giy@omp-system.ru> Date: Mon, 13 Jun 2022 16:17:03 +0300 Subject: [PATCH] Инициализация исходников QtVsTools v2.8.1 --- QtVsTools.Package/Package/TranslationItem.cs | 73 Templates/quick/quick.vcxproj.filters | 33 QrcEditor/qrceditor.rc | 1 QtVsTools.RegExpr/expression/RegExprSequence.cs | 67 QMakeFileReader/evalhandler.h | 46 QtVsTest/MacroClient.h | 249 QtVsTest/csmacro.tmLanguage_TT | 529 VsQml/vsqml.cpp | 369 QtVsTools.Core/ImageButton.cs | 75 QtVsTools.Package/Package/FormProjectQtSettings.Designer.cs | 157 QtVsTools.Package/Package/QtHelpLinkChooser.xaml.cs | 93 QtVsTools.Package/VSPackage.resx | 174 QtVsTools.Package/Package/ChangeFor.cs | 32 QtVsTools.Wizards/Wizards/ProjectWizard/Empty/EmptyWizard.cs | 73 QtMSBuild/Tasks/ListQrc.cs | 79 Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.qrc | 4 QtVsTools.Core/CommandLineParser.cs | 589 QtVsTools.Package/QtVsTools.ico | 0 Templates/widget/QtTemplate.Item.Widget.csproj | 114 QtVsTools.Package/Package/AddTranslationDialog.cs | 213 doc/tutorial/AddressBook/addressbook.h | 61 QtVsTools.Package/QML/Parser/QmlParserInterop.cs | 567 QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL.cs | 107 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Break.cs | 83 QtVsTools.Core/QtModules.cs | 113 QtVsTools.Core/QrcPrefix.cs | 49 Templates/dialogbuttonright/dialogbuttonright.vstemplate | 49 QtVsTools.Core/LinkerToolWrapper.cs | 173 QtVsTools.Wizards/Wizards/ClassWizard/AddClassWizard.cs | 108 QtVsTools.Package/QML/Classification/QmlClassificationFormat.cs | 108 Templates/console/QtTemplate.Project.Console.csproj | 131 Templates/console/console.vcxproj.filters | 26 QMakeFileReader/evaluator/ioutils.cpp | 155 QtVsTools.Package/Package/FormProjectQtSettings.resx | 120 Templates/empty/empty.ico | 0 QMakeFileReader/evaluator/qmakeevaluator.cpp | 2027 + QrcEditor/qrceditor.vcxproj.filters | 80 Tests/Test_QtMsBuild.Tasks/Test_QtMsBuild.Tasks.csproj | 112 Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.h | 43 QtMSBuild/QtMsBuild/qt_tasks.targets_TT | 212 QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml.cs | 478 Templates/lib/global.h | 13 QtMSBuild/QtMsBuild/qml/qtqml.targets | 80 QtVsTools.Core/QtVSIPSettings.cs | 566 Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj | 115 QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs | 257 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Frame.cs | 180 QtVsTools.Package/Package/ProjectQtSettings.cs | 324 QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml | 111 Templates/lib/Properties/AssemblyInfo.cs | 67 Tests/SampleTest/Test_QtVsToolsLoaded.csmacro | 35 QrcEditor/shared/qrceditor.h | 105 QtVsTools.Core/ProFileContent.cs | 80 Templates/server/QtTemplate.Project.Server.csproj | 138 Tests/BigSolution/generator/Properties/AssemblyInfo.cs | 36 QtMSBuild/QtMsBuild/deploy/qtdeploy.xml | 366 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsObject.cs | 218 QtMSBuild/Tasks/Expand.cs | 100 QtVsTools.Core/Properties/AssemblyInfo.cs | 67 QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassWizard.cs | 225 QtMSBuild/QtMsBuild/uic/qtuic.props | 82 QrcEditor/shared/resourcefile.cpp | 992 QtVsTools.Wizards/Resources/small.png | 0 VsQml/README | 35 QtVsTools.Package/Package/FormChangeQtVersion.Designer.cs | 87 QtVsTools.Core/QMakeQuery.cs | 122 QtVsTools.RegExpr/Properties/AssemblyInfo.cs | 36 QtMSBuild/Tasks/HostExec_LinuxWSL_Error.cs | 60 QtVsTools.Core/FilesToList.cs | 42 QtMSBuild/QtMsBuild/repc/qtrepc_cl.targets_TT | 121 VsQml/vsqml.vcxproj | 196 Tests/concurrency/Solution1/QtWidgetsApplication2/main.cpp | 10 QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml | 259 QMakeFileReader/evaluator/evaluator.pri | 22 QtMSBuild/QtMsBuild/qml/qtqml.props | 59 QtVsTools.Core/Observable.cs | 77 QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml | 298 QMakeFileReader/evaluator/qmakeevaluator.h | 294 QMakeFileReader/evaluator/qmakeglobals.cpp | 355 QtVsTools.Package/Marketplace/Overview.html_TT | 162 LICENSE.GPL3-EXCEPT | 704 QtVsTools.Core/MsBuildProject.cs | 1800 + QtMSBuild/QtMsBuild/repc/qtrepc.xml | 237 QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL_Error.cs | 52 doc/images/qtvstools-widgets-app-modules.png | 0 Tests/Test_QtVsTools.RegExpr/Properties/AssemblyInfo.cs | 47 QtMSBuild/QtMsBuild/moc/qtmoc.props | 83 QtMSBuild/QtMsBuild/qt_settings.xml | 148 Templates/lib/QtTemplate.Project.Lib.csproj | 136 QtMSBuild/QtMsBuild/moc/qtmoc_cl.targets_TT | 157 QtVsTools.Package/QML/Syntax/QmlSyntax.cs | 283 Templates/designer/Properties/AssemblyInfo.cs | 67 QrcEditor/mainwindow.h | 65 QtVsTools.Wizards/Wizards/WizardData.cs | 69 QtMSBuild/QtMsBuild/qt.targets | 77 QtTmLanguage/qt/pri.pro.tmLanguage | 346 QtVsTools.Package/Resources/menuimages.png | 0 Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj | 112 QtMSBuild/Tasks/HostExec_LinuxWSL.cs | 154 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7CodeContext.cs | 206 QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml.cs | 66 Templates/qmldir/QtTemplate.Item.QMLDir.csproj | 119 QtCppConfig.props | 27 QtVsTools.Package/Language/LICENSE.APACHE | 13 QtVsTools.Package/Common/Timestamp.cs | 55 QtVsTools.Package/Package/QtItemContextMenu.cs | 143 Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.cpp | 5 Tests/concurrency/Solution1/StaticLib1/Header.h | 4 doc/qtvstools-online.qdocconf | 19 QtMSBuild/QtMsBuild/uic/qtuic.targets | 406 QMakeFileReader/qmakefilereader.vcxproj.filters | 86 QtVsTools.Package/Common/Disposable.cs | 149 Templates/resource/resource.vstemplate | 49 QtVsTools.Package/QML/Parser/QmlParserDiagnostics.cs | 52 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Connect.cs | 52 doc/qtvstools.qdocconf | 2 QtMSBuild/QtMsBuild/rcc/qtrcc_v3.xml_TT | 447 QtVsTools.Core/Resources/delete.png | 0 Templates/gui/gui.vcxproj.filters | 48 Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj | 104 QtVsTest/Properties/AssemblyInfo.cs | 65 QtVsTools.Package/Package/QtProjectContextMenu.cs | 324 QtVsTools.Package/QtMsBuild/QtVersionProvider.cs | 84 QMakeFileReader/evaluator/proitems.h | 384 QtVsTools.Package/Editors/Editor.QtLinguist.cs | 56 QrcEditor/shared/qrceditor.pri | 19 QtMSBuild/QtMsBuild/repc/qtrepc.targets | 387 doc/tutorial/AddressBook/AddressBook.pro | 13 QtVsTools.Core/QMakeConf.cs | 166 Templates/dialogbuttonbottom/Properties/AssemblyInfo.cs | 67 QtVsTools.Core/ProSolution.cs | 61 doc/tutorial/AddressBook/adddialog.h | 53 Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.cpp | 11 QtVsTools.Core/ProjectMacros.cs | 37 Tests/Test_QtVsTools.RegExpr/Test_SubTokens.cs | 60 QtMSBuild/QtMsBuild/qt_settings.targets | 42 QtMSBuild/QtMsBuild/uic/qtuic.xml | 235 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Evaluate.cs | 91 Templates/dialogbuttonbottom/dialogbuttonbottom.vstemplate | 49 Tests/concurrency/Solution1/QtWidgetsApplication1/main.cpp | 10 QtMSBuild/QtMsBuild/rcc/qtrcc.targets | 595 Tests/concurrency/Solution1/QtClassLibrary1/QtClass.cpp | 10 QtVsTools.Wizards/Wizards/ProjectWizard/Console/ConsoleWizard.cs | 88 QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml | 310 QtVsTools.Package/QtVsTools.Package.csproj | 631 Templates/server/Properties/AssemblyInfo.cs | 67 doc/images/qtvstools-addressbook-mainwindow.png | 0 Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj.filters | 44 doc/tutorial/AddressBook/AddressBook.ico | 0 QtVsTools.Core/VisualStudio/IProjectTracker.cs | 35 QtMSBuild/Properties/AssemblyInfo.cs | 68 QtVsTools.Core/QtVsTools.Core.csproj | 191 QtVsTools.Core/Resources/newitem_d.png | 0 QtVsTools.Wizards/Wizards/Util/VCLanguageManagerValidationRule.cs | 50 QtMSBuild/Tasks/HostTranslatePaths_LinuxSSL.cs | 118 Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.h | 18 QtVsTools.Core/Resources/newitem.png | 0 Templates/mainwindow/mainwindow.vstemplate | 49 QtVsTools.Wizards/Wizards/ClassWizard/Class.cs | 63 Templates/resource/resource.ico | 0 Tests/Test_QtMsBuild.Tasks/Test_Join.cs | 237 QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerWizard.cs | 182 QtVsTools.Core/QtMsBuild.cs | 1567 Templates/dialogbuttonright/dialogbuttonright.ico | 0 QtVsTools.RegExpr/utils/Utils.cs | 97 QtMSBuild/QtMsBuild/moc/qtmoc.targets | 510 Templates/gui/gui.vcxproj | 46 QtVsTools.Core/QtVsTools.Core.ico | 0 Templates/widget/widget.ico | 0 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Engine.cs | 334 QtVsTools.RegExpr/expression/RegExprToken.cs | 388 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetBreakpoint.cs | 136 QrcEditor/mainwindow.cpp | 183 Templates/server/server.vcxproj | 47 Templates/dialogbuttonright/QtTemplate.Item.DialogButtonRight.csproj | 114 Templates/lib/header.h | 9 QtMSBuild/QtMsBuild/qml/qt_import.props | 50 Tests/BigSolution/template/BigProjectNNN/BigClassNNN.cpp | 118 doc/images/qtvstools-qt-versions.png | 0 Templates/designer/designer.vstemplate_TT | 84 Templates/empty/Properties/AssemblyInfo.cs | 67 QtVsTools.Package/qt.ico | 0 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Exception.cs | 78 QtVsTools.Package/QML/Classification/QmlTag.cs | 313 QtMSBuild/QtMsBuild/Qt.props | 59 QtMSBuild/QtMsBuild/repc/qtrepc_v3.xml_TT | 282 Tests/BigSolution/generator/Program.cs | 120 QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml.cs | 64 Tests/concurrency/Solution1/Solution1.sln | 43 Templates/designer/plugin.json | 1 Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.qrc | 4 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Backtrace.cs | 103 QtVsTools.Core/QtVSException.cs | 46 QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs | 116 doc/images/qtvstools-widgets-app-class.png | 0 QtVsTools.Core/Resources/delete_d.png | 0 Templates/server/stdafx.cpp | 1 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Disconnect.cs | 65 QtMSBuild/QtMsBuild/translation/qttranslation.targets | 626 QtVsTools.Package/QtMsBuild/QtProjectTracker.cs | 344 QtVsTools.Core/Resources.resx | 306 Templates/console/main.cpp | 8 QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml.cs | 62 QtVsTools.Package/Package/QtHelpLinkChooser.xaml | 107 Templates/mainwindow/QtTemplate.Item.MainWindow.csproj | 115 Templates/lib/source.cpp | 5 Templates/quick/QtTemplate.Project.Quick.csproj | 128 Tests/BigSolution/template/BigSolution.sln | 25 QtVsTools.Package/Common/Json/DeferredObject.cs | 114 QtVsTools.Core/QrcItem.cs | 36 QtVsTools.Package/VisualStudio/VsShell.cs | 88 QrcEditor/qrceditor.vcxproj | 253 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Continue.cs | 97 VsQml/astvisitor.h | 47 VsQml/vsqmldebugclient.cpp | 148 version.targets | 6 QtMSBuild/QtMsBuild/qml/qtqml_cache.targets | 625 QtMSBuild/QtMsBuild/qt_private.props | 233 QtVsTools.Package/QtMenus.vsct_TT | 725 QtMSBuild/QtMsBuild/qml/qtqml_static.targets | 342 QtVsTools.Package/Common/Prototyped.cs | 222 QMakeFileReader/evalhandler.cpp | 54 QtVsTools.Package/qtmodules.xml | 600 Tests/Test_QtVsTools.RegExpr/Test_MacroParser.cs | 66 QtVsTools.RegExpr/production/Production.cs | 312 QMakeFileReader/evaluator/qmakeevaluator_p.h | 94 QtMSBuild/Tasks/Flatten.cs | 118 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Program.cs | 602 QtVsTools.Package/Package/ExtLoader.cs | 192 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Expression.cs | 103 Tests/SampleTest/Macros.qrc | 9 QtVsTools.Package/Common/PriorityQueue.cs | 177 Templates/designer/widget.cpp | 6 QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml | 298 Templates/quick/quick.ico | 0 Templates/lib/lib.ico | 0 Tests/SampleTest/main.cpp | 140 QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerWizard.cs | 167 Templates/widget/widget.vstemplate | 49 Templates/server/server.vcxproj.filters | 25 QtVsTools.Wizards/Wizards/WizardIntroPage.xaml | 102 .editorconfig | 219 QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml.cs | 102 QMakeFileReader/qmakedataprovider.h | 56 Templates/console/console.vstemplate_TT | 74 Templates/qml/QtTemplate.Item.QMLFile.csproj | 119 QtMSBuild/QtMsBuild/rcc/qt_import.props | 46 QtVsTools.Core/QrcParser.cs | 96 README.md | 156 QtVsTest/QtVsTest.cs | 107 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetExceptionBreak.cs | 96 QtVsTools.Wizards/Wizards/Util/FileNameValidationRule.cs | 55 Tests/SampleTest/Test_RebuildSolution.csmacro | 41 QMakeFileReader/qmakedataprovider.cpp | 142 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Message.cs | 363 QMakeFileReader/evaluator/qmakeparser.cpp | 1212 Tests/concurrency/loop_msbuild.bat | 36 Templates/dialogbuttonright/dialogbuttonright.ui | 100 QtVsTest/csmacro.tmTheme_TT | 68 doc/doc.pri | 42 QtVsTools.Package/Package/FormProjectQtSettings.cs | 180 QtVsTools.Wizards/Wizards/ProjectWizard/ProjectTemplateWizard.cs | 723 Templates/quick/main.qml | 9 Templates/server/source.cpp | 17 Templates/resource/resource.qrc | 4 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7InfoHelpers.cs | 198 QtVsTools.Core/FakeFilter.cs | 44 Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.h | 18 Tests/Test_QtMsBuild.Tasks/TestTaskLoggingHelper.cs | 364 QtVsTest/source.extension.vsixmanifest_TT | 122 QtMSBuild/QtMsBuild/qml/qtqml_static.props | 56 QtVsTools.Wizards/Wizards/WizardWindow.xaml.cs | 134 QrcEditor/shared/undocommands_p.h | 160 QtVsTools.Package/Common/Json/Serializable.cs | 339 Templates/lib/stdafx.cpp | 1 QtVsTools.Wizards/Wizards/Util/ClassNameValidationRule.cs | 67 Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.ui | 28 QtMSBuild/QtMsBuild/repc/qt_import.props | 46 QtVsTools.Core/ProjectImporter.cs | 285 QtVsTools.Core/ProFileOption.cs | 143 QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml.cs | 253 QMakeFileReader/evaluator/README | 6 QtVsTools.Core/Resources.cs | 141 VsQml/vsqmldebugclient.h | 56 QtVsTools.Core/RccOptions.cs | 108 QtMSBuild/QtMsBuild/moc/qtmoc.xml | 316 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Lookup.cs | 85 vstools.bat | 417 Templates/gui/widget.ui | 25 Templates/widget/widget.ui | 24 QtVsTools.Package/Package/FormChangeQtVersion.resx | 120 Templates/qml/Properties/AssemblyInfo.cs | 67 QrcEditor/shared/qrceditor.ui | 127 QtVsTools.Package/qt5.natvis.xml | 808 QtVsTools.Package/Properties/AssemblyInfo.cs | 67 QtVsTools.Package/QtMsBuild/QtProjectLogger.cs | 128 Templates/quick/main.cpp | 18 QMakeFileReader/main.cpp | 87 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7BoilerPlate.cs | 547 Templates/empty/empty.vcxproj | 41 QtMSBuild/QtMsBuild/qt_vars.targets | 594 Templates/resource/QtTemplate.Item.Resource.csproj | 119 Tests/Test_QtMsBuild.Tasks/Properties/AssemblyInfo.cs | 20 Templates/dialogbuttonright/Properties/AssemblyInfo.cs | 67 common.tt | 158 QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryWizard.cs | 142 QtMSBuild/QtMsBuild/deploy/qt_import.props | 46 vstools.sln | 609 Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.qrc | 4 QtVsTools.Package/QtVsTools.Qml.Debug.pkgdef | 21 QtVsTools.Package/QtVsToolsPackage.cs | 473 Templates/dialogbuttonbottom/dialogbuttonbottom.ico | 0 QtTmLanguage/qt/LICENSE | 4 Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj.filters | 42 Tests/SampleTest/SampleTest.vcxproj | 172 QtVsTools.Package/source.extension.vsixmanifest_TT | 200 QtMSBuild/Tasks/DumpItems.cs | 120 QtVsTools.Core/WaitDialog.cs | 184 QtVsTools.Wizards/Wizards/WizardWindow.xaml | 44 QtVsTools.Package/Resources.resx | 211 Templates/qmldir/qmldir.vstemplate | 49 QtVsTools.Package/Editors/Editor.QtDesigner.cs | 88 QtVsTools.Core/BuildConfig.cs | 49 QrcEditor/qrceditor.ico | 0 QtVsTools.RegExpr/expression/CharClassSet.cs | 352 QtMSBuild/QtMsBuild/uic/qtuic_v3.xml | 218 QtMSBuild/Tasks/ParseVarDefs.cs | 79 QtMSBuild/QtMsBuild/moc/qtmoc_v3.xml_TT | 356 QtVsTools.Core/QMake.cs | 251 QtMSBuild/QtMsBuild/qml/qtqml_cache.props | 84 QtVsTools.Wizards/Wizards/Util/UnsafeNativeMethods.cs | 41 QrcEditor/shared/resourceview.cpp | 663 QtVsTools.Package/Package/QMakeWrapper.cs | 111 Templates/gui/stdafx.h | 1 QtVsTools.Core/HelperFunctions.cs | 1918 + QtVsTools.RegExpr/expression/RegExprChoice.cs | 87 QtVsTools.Core/ExportProjectDialog.cs | 440 QtVsTools.Core/VisualStudio/VsServiceProvider.cs | 95 Templates/console/console.vcxproj | 41 Templates/gui/gui.vstemplate_TT | 82 Templates/console/Properties/AssemblyInfo.cs | 67 Templates/mainwindow/mainwindow.ico | 0 QtMSBuild/QtMsBuild/qt_defaults.props | 127 transform.targets | 80 Templates/gui/widget.cpp | 7 doc/config/style/qt5-sidebar.html | 15 QtVsTools.Package/QtMsBuild/QtProjectBuild.cs | 384 QtVsTools.Package/Editors/Editor.cs | 525 QtVsTools.Package/QML/Debugging/QmlDebugger.cs | 545 QtVsTools.Core/CompilerToolWrapper.cs | 465 Templates/designer/widget.h | 11 doc/images/qtvstools-qt-project-settings.png | 0 QtMSBuild/Tasks/GetVarsFromMSBuild.cs | 96 doc/src/externallinks.qdoc | 37 QtMSBuild/Tasks/GetVarsFromMakefile.cs | 92 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Version.cs | 82 Templates/server/server.rc | 2 QtMSBuild/QtMsBuild/deploy/qtdeploy.props | 73 Templates/designer/designer.ico | 0 Templates/lib/lib.vcxproj | 44 QtVsTools.Wizards/Properties/AssemblyInfo.cs | 67 doc/images/qtvstools-export-project.png | 0 QtVsTools.Wizards/Resources/QtProjectWizard.ico | 0 QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml | 301 QtVsTools.Package/preview.png | 0 Templates/lib/stdafx.h | 0 QtVsTools.Package/Common/ConcurrentStopwatch.cs | 61 doc/tutorial/AddressBook/adddialog.ui | 126 QtMSBuild/QtMsBuild/rcc/qtrcc_cl.targets_TT | 113 Templates/designer/plugin.cpp | 77 Templates/dialogbuttonbottom/dialogbuttonbottom.ui | 100 QtVsTest/Macro.cs | 1049 QMakeFileReader/qmakefilereader.ico | 0 Templates/gui/gui.ico | 0 QMakeFileReader/qmakefilereader.rc | 1 Templates/server/stdafx.h | 1 QtVsTools.Package/Options/QtVersionsTable.xaml | 286 Tests/BigSolution/template/BigProjectNNN/bigprojectnnn_global.h | 41 Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.cpp | 9 QtVsTools.RegExpr/expression/CharClassLiteral.cs | 88 QtVsTools.Core/ProjectExporter.cs | 853 Templates/server/header.h | 17 QtVsTools.Core/Messages.cs | 241 Tests/concurrency/Solution1/MyPropertySheet.props | 10 QtVsTools.RegExpr/production/ProductionRule.cs | 409 Tests/BigSolution/template/BigProjectNNN/BigClassNNN.h | 38 QtMSBuild/QtMsBuild/rcc/qtrcc.xml | 384 QtVsTools.Wizards/Wizards/Util/SortComboBoxItem.cs | 41 QtVsTools.Package/Package/FormChangeQtVersion.cs | 103 QtVsTools.Wizards/QtVsTools.Wizards.csproj | 250 Templates/lib/lib.vstemplate_TT | 82 doc/images/qtvstools-qt-widget-class-wizard.png | 0 Templates/server/server.vstemplate_TT | 86 references.props | 125 Tests/SampleTest/Test_DebugGuiApp.csmacro | 37 doc/config/macros.qdocconf | 4 VsQml/vsqml.h | 120 QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml | 238 Templates/qmldir/Properties/AssemblyInfo.cs | 67 Tests/SampleTest/Test_CreateGuiApp.csmacro | 49 QrcEditor/shared/resourceview.h | 180 QtMSBuild/Tasks/GetItemHash.cs | 76 QtVsTools.Package/QML/Syntax/QmlAst.cs | 239 VsQml/vsqml_global.h | 38 Templates/mainwindow/mainwindow.ui | 51 QtVsTools.Package/Options/QtLegacyOptionsPage.cs | 162 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsValue.cs | 289 QtVsTools.Wizards/Wizards/ProjectWizard/Quick/QuickWizard.cs | 73 Tests/concurrency/Solution1/QtClassLibrary1/QtClass.h | 13 QtVsTest/QtVsTest.csproj | 256 QtVsTools.Package/Common/PunisherQueue.cs | 53 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Events.cs | 274 QtVsTools.RegExpr/expression/RegExprAssert.cs | 192 QtVsTools.Wizards/Wizards/ClassWizard/ClassKind.cs | 36 Tests/Test_QtVsTools.RegExpr/Test_XmlIntParser.cs | 344 .gitignore | 177 QtVsTools.Core/CxxStreamReader.cs | 199 QtVsTools.Core/MocCmdChecker.cs | 170 QrcEditor/shared/undocommands.cpp | 185 Templates/designer/designer.vcxproj | 46 Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.cpp | 35 QtVsTools.RegExpr/expression/RegExprRepeat.cs | 120 QtVsTools.Package/Common/Concurrent.cs | 222 QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml | 356 QtVsTools.Package/Package/Version.cs | 41 QtVsTools.Wizards/Wizards/Util/NativeMethods.cs | 41 doc/tutorial/AddressBook/AddressBook.rc | 2 QtVsTools.Core/Extensions.cs | 62 QtVsTools.RegExpr/production/ProductionRuleAction.cs | 345 Tests/SampleTest/SampleTest.pro | 10 QtVsTools.RegExpr/expression/CharClassRange.cs | 98 QtVsTools.Wizards/Resources/medium.png | 0 QtVsTools.RegExpr/utils/Consts.cs | 202 doc/images/qtvstools-minus.png | 0 Templates/server/server.def | 8 QtVsTools.Wizards/Wizards/WizardIntroPage.xaml.cs | 39 QtMSBuild/Tasks/Join.cs | 141 Templates/gui/Properties/AssemblyInfo.cs | 67 QtVsTools.Package/Common/Json/SerializableEnum.cs | 116 QtVsTools.Package/Options/QtOptionsPage.cs | 346 QtVsTools.Package/qt6.natvis.xml | 379 Templates/resource/Properties/AssemblyInfo.cs | 67 doc/images/qtvstools-addressbook-adddialog.png | 0 QMakeFileReader/evaluator/qmake_global.h | 55 QtVsTools.Package/Options/QtVersionsPage.cs | 143 QtMSBuild/QtMsBuild/moc/qt_import.props | 46 QtMSBuild/QtMsBuild/rcc/qtrcc.props | 85 QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs | 87 Templates/empty/QtTemplate.Project.Empty.csproj | 129 QMakeFileReader/qmakefilereader.vcxproj | 257 Templates/lib/lib.vcxproj.filters | 33 QtVsTools.RegExpr/expression/RegExprLiteral.cs | 72 Tests/SampleTest/Test_ImportProFile.csmacro | 68 Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.ui | 28 QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs | 651 Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj.filters | 27 QtVsTools.Package/QML/Debugging/QmlFileSystem.cs | 217 QtMSBuild/Tasks/QtRunWork.cs | 320 Templates/qml/NewFile.qml | 12 Templates/server/server.ico | 0 Templates/designer/stdafx.cpp | 1 Tests/BigSolution/generator/generator.csproj | 53 QtVsTools.RegExpr/expression/RegExpr.cs | 114 QtMSBuild/QtMsBuild/uic/qt_import.props | 46 Templates/gui/stdafx.cpp | 1 QtVsTools.Core/QtConfig.cs | 127 QMakeFileReader/evaluator/ioutils.h | 70 doc/images/qtvstools-qrc-editor.png | 0 QtVsTools.Core/QtVersionManager.cs | 558 QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml | 261 QrcEditor/shared/resourcefile_p.h | 262 Templates/server/widget.ui | 20 QtMSBuild/QtMsBuild/qt_globals.targets | 636 QtVsTools.Wizards/Resources/Qt-logo-small.png | 0 Templates/designer/QtTemplate.Project.Designer.csproj | 138 Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj.filters | 48 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Breakpoint.cs | 346 Templates/empty/empty.vstemplate_TT | 71 doc/tutorial/AddressBook/addressbook.cpp | 97 Templates/designer/stdafx.h | 1 Tests/Test_QtVsTools.PriorityQueue/Test_QtVsTools.PriorityQueue.csproj | 107 Templates/designer/plugin.h | 28 QtVsTools.Package/QtMsBuild/QtProjectIntelliSense.cs | 93 Tests/SampleTest/SampleTest.sln | 25 QtVsTools.Core/QtProject.cs | 4126 ++ QtVsTools.Package/QML/Debugging/QmlDebugLauncher.cs | 313 Templates/qml/qml.vstemplate | 49 vstools.pri_TT | 39 Changelog | 724 Tests/concurrency/Solution1/QtClassLibrary1/qtclasslibrary1_global.h | 13 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Property.cs | 304 Tests/Test_QtVsTools.PriorityQueue/Properties/AssemblyInfo.cs | 48 QtVsTools.Core/MainWinWrapper.cs | 57 version.tt | 81 QtVsTools.Core/Common/EnumExt.cs | 218 QtVsTools.Core/TemplateType.cs | 45 QtVsTools.RegExpr/expression/Renderer.cs | 132 QtVsTools.Wizards/Wizards/ClassWizard/IClassWizard.cs | 35 doc/tutorial/AddressBook/adddialog.cpp | 51 Tests/SampleTest/SampleTest.vcxproj.filters | 60 VsQml/astvisitor.cpp | 563 vstools.pro | 4 QtVsTools.RegExpr/parser/Parser.cs | 148 Templates/quick/qml.qrc | 5 QtVsTools.Package/Package/QtSolutionContextMenu.cs | 182 Templates/mainwindow/Properties/AssemblyInfo.cs | 67 Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj | 93 Templates/gui/main.cpp | 10 Tests/concurrency/Solution1/StaticLib1/StaticLib1.cpp | 3 QtVsTools.Core/Filters.cs | 105 QtVsTools.Core/SR.cs | 91 QtVsTools.Package/Package/QtHelp.cs | 287 QMakeFileReader/evaluator/proitems.cpp | 450 QtVsTools.Wizards/Wizards/Util/FileExistsInFilterValidationRule.cs | 67 QtVsTools.Package/QtMsBuild/QtModulesEditor.cs | 84 QtMSBuild/Tasks/CriticalSection.cs | 113 Templates/quick/quick.vstemplate_TT | 77 QtMSBuild/QtMsBuild/translation/qt_import.props | 46 QtVsTools.Package/Common/NativeAPI.cs | 179 QtVsTools.RegExpr/parser/ParseTree.cs | 240 QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiWizard.cs | 300 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Enums.cs | 237 doc/config/qtvstools-project.qdocconf | 45 QtVsTools.Package/Language/qml.qmlproject.tmLanguage | 317 QtVsTools.Wizards/Wizards/WizardResult.cs | 37 QtVsTest/MacroParser.cs | 250 QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassWizard.cs | 383 Templates/qml/qml.ico | 0 Templates/dialogbuttonbottom/QtTemplate.Item.DialogButtonBottom.csproj | 115 QtMSBuild/ITaskLoggingHelper.cs | 263 QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml.cs | 82 QtVsTools.Package/Package/QtMainMenu.cs | 283 Tests/BigSolution/generator/generator.sln | 25 Templates/qmldir/qml.ico | 0 QtMSBuild/Tasks/HostExec_Windows.cs | 125 QtMSBuild/QtMsBuild/translation/qttranslation.xml | 149 QtVsTools.Core/QtModule.cs | 110 QtVsTools.Package/Common/VsToolsDialogWindow.cs | 35 doc/tutorial/AddressBook/addressbook.ui | 86 QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml.cs | 62 QtVsTools.Wizards/Wizards/WizardPage.cs | 115 Templates/designer/designer.vcxproj.filters | 39 Tests/Test_QtVsTools.PriorityQueue/Test_PriorityQueue.cs | 230 doc/images/qtvstools-plus.png | 0 QtVsTools.Package/Package/DteEventsHandler.cs | 586 QtVsTools.RegExpr/QtVsTools.RegExpr.csproj | 117 QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Client.cs | 421 Tests/BigSolution/generator/App.config | 6 VsQml/vsqml.vcxproj.filters | 47 QtMSBuild/QtMsBuild/repc/qtrepc.props | 73 Templates/widget/Properties/AssemblyInfo.cs | 67 QrcEditor/main.cpp | 40 QtVsTest/MacroServer.cs | 137 Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.h | 9 QMakeFileReader/evaluator/qmakeglobals.h | 160 QMakeFileReader/evaluator/qmakeparser.h | 210 QtMSBuild/Tasks/HostExec_LinuxSSL.cs | 188 QtMSBuild/QtMSBuild.csproj | 432 QtVsTools.Package/Package/AddTranslationDialog.resx | 120 Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.ui | 28 QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7StackFrame.cs | 308 QtVsTools.Wizards/Wizards/ClassWizard/UiClassInclusion.cs | 37 QtVsTest/Resources/QtVsTest.ico | 0 QtVsTools.Package/Package/Translation.cs | 282 QtVsTools.Wizards/Resources/ExpanderStyle.xaml | 173 QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml.cs | 68 QMakeFileReader/evaluator/qmakebuiltins.cpp | 1635 + Templates/empty/empty.vcxproj.filters | 25 QrcEditor/shared/qrceditor.cpp | 423 QtMSBuild/QtMsBuild/deploy/qtdeploy.targets | 342 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4ClearBreakpoint.cs | 66 Templates/quick/quick.vcxproj | 44 QtVsTools.Core/ExportProjectDialog.resx | 301 Templates/quick/Properties/AssemblyInfo.cs | 67 Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj.filters | 44 doc/src/qtvstools.qdoc | 827 QtVsTools.Package/Common/Json/Serializer.cs | 334 Templates/console/console.ico | 0 doc/tutorial/AddressBook/main.cpp | 50 QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Scope.cs | 139 QtVsTools.Package/Options/QtVersionsTable.cs | 526 QtVsTools.Package/Resources/qt.ico | 0 QtVsTools.Core/VersionInformation.cs | 304 QtVsTools.Package/Package/SR.cs | 101 Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj | 115 QtVsTools.RegExpr/README | 317 Templates/qmldir/qmldir | 1 QtVsTools.Package/Editors/Editor.QtResourceEditor.cs | 62 Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj | 119 QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Protocol.cs | 269 QtMSBuild/QtMsBuild/translation/qttranslation.props | 96 QtVsTools.Package/Package/QtMsBuildConverter.cs | 217 QtMSBuild/Tasks/HostTranslatePaths_Windows.cs | 76 Templates/gui/QtTemplate.Project.Gui.csproj | 142 Templates/gui/widget.h | 15 595 files changed, 98,037 insertions(+), 23 deletions(-) diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e8c7721 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,219 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = false +csharp_new_line_before_else = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = accessors,anonymous_methods,anonymous_types,lambdas,methods,object_collection_array_initializers,properties,types +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents =true +csharp_indent_case_contents_when_block =true +csharp_indent_labels = flush_left +csharp_indent_switch_labels =false + +# Space preferences +csharp_space_after_cast =false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = ignore +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/.gitignore b/.gitignore index 1c38a68..30aff55 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,63 @@ +*~ +ui_*.h +*.swp +*.idb +*.pdb +*.ib_pdb_index +*.ncb +*.suo +*.user +Makefile +Makefile.* +debug/ +Debug/ +release/ +Release/ +GeneratedFiles/ +help/test +*.UnmanagedRegistration.cache +Trolltech.Qt4VS200?Base.xml +*_wrapper.bat +*_plugin_import.cpp +qrceditor/bin/ +qmakefilereader/bin/ +*.metaproj* +*.binlog + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user +*.userosscache *.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ -x64/ -build/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ bld/ [Bb]in/ [Oo]bj/ -# Roslyn cache directories -*.ide/ +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -#NUNIT +# NUNIT *.VisualState.xml TestResult.xml @@ -32,6 +65,10 @@ [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c + +# DNX +project.lock.json +artifacts/ *_i.c *_p.c @@ -65,14 +102,17 @@ ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf *.cachefile +*.VC.db # Visual Studio profiler *.psess *.vsp *.vspx +*.sap # TFS 2012 Local Workspace $tf/ @@ -85,7 +125,7 @@ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding addin-in +# JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in @@ -97,6 +137,7 @@ # NCrunch _NCrunch_* .*crunch*.local.xml +nCrunchTemp_* # MightyMoose *.mm.* @@ -124,34 +165,47 @@ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -## TODO: Comment the next line if you want to checkin your -## web deploy settings but do note that will include unencrypted -## passwords -*.pubxml + +# TODO: Un-comment the next line if you do not want to checkin +# your web deploy settings because they may include unencrypted +# passwords +#*.pubxml +*.publishproj # NuGet Packages -packages/* *.nupkg -## TODO: If the tool you use requires repositories.config -## uncomment the next line -#!packages/repositories.config +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets -# Enable "build/" folder in the NuGet Packages folder since -# NuGet packages use it for MSBuild targets. -# This line needs to be after the ignore of the build folder -# (and the packages folder if the line above has been uncommented) -!packages/build/ - -# Windows Azure Build Output +# Microsoft Azure Build Output csx/ *.build.csdef +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + # Windows Store app package directory AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ # Others -sql/ -*.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* @@ -161,6 +215,7 @@ *.pfx *.publishsettings node_modules/ +orleans.codegen.cs # RIA/Silverlight projects Generated_Code/ @@ -184,3 +239,79 @@ # Microsoft Fakes FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# LightSwitch generated files +GeneratedArtifacts/ +ModelManifest.xml + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ + +# Generated Files +MSBuild.MetaInfo.cs +qtvstools.package/package/Version.tt.cs +qtvstools.package/Properties/AssemblyInfo.tt.cs +qtvstools.package/QtMenus.vsct +qtvstools.package/Marketplace/Overview.html +qtvstools.core/Properties/AssemblyInfo.tt.cs +qtvstools.wizards/Properties/AssemblyInfo.tt.cs +templates/qmldir/Properties/AssemblyInfo.tt.cs +templates/qml/Properties/AssemblyInfo.tt.cs +templates/console/Properties/AssemblyInfo.tt.cs +templates/designer/Properties/AssemblyInfo.tt.cs +templates/dialogbuttonbottom/Properties/AssemblyInfo.tt.cs +templates/dialogbuttonright/Properties/AssemblyInfo.tt.cs +templates/empty/Properties/AssemblyInfo.tt.cs +templates/quick/Properties/AssemblyInfo.tt.cs +templates/gui/Properties/AssemblyInfo.tt.cs +templates/lib/Properties/AssemblyInfo.tt.cs +templates/mainwindow/Properties/AssemblyInfo.tt.cs +templates/resource/Properties/AssemblyInfo.tt.cs +templates/server/Properties/AssemblyInfo.tt.cs +templates/widget/Properties/AssemblyInfo.tt.cs +templates/console/console.vstemplate +templates/designer/designer.vstemplate +templates/empty/empty.vstemplate +templates/quick/quick.vstemplate +templates/gui/gui.vstemplate +templates/lib/lib.vstemplate +templates/server/server.vstemplate +qtmsbuild/Properties/AssemblyInfo.tt.cs +qtmsbuild/QtMsBuild/moc/qtmoc_v3.xml +qtmsbuild/QtMsBuild/moc/qtmoc_cl.targets +qtmsbuild/QtMsBuild/rcc/qtrcc_v3.xml +qtmsbuild/QtMsBuild/rcc/qtrcc_cl.targets +qtmsbuild/QtMsBuild/repc/qtrepc_v3.xml +qtmsbuild/QtMsBuild/repc/qtrepc_cl.targets +qtmsbuild/QtMSBuild/qt_tasks.targets +qtvstest/csmacro.tmLanguage +qtvstest/csmacro.tmTheme +qtvstest/Properties/AssemblyInfo.tt.cs +qtvstest/source.extension.vsixmanifest +QtVsTools.Package/source.extension.vsixmanifest +QtNatvisPoC/*.txt +Tests/BigSolution/generated/ +vstools.pri diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..84198b6 --- /dev/null +++ b/Changelog @@ -0,0 +1,724 @@ +Qt Visual Studio Tools version 2.8.1: + +Changes +------- + - Fixed QTVSADDINBUG-340: QML files are not updated with lupdate + - Fixed QTVSADDINBUG-431: Difficult to find feature to change Qt Version + - Fixed QTVSADDINBUG-801, QTVSADDINBUG-498: Error in qt5.natvis: "identifier "$T1" is undefined" + - Fixed QTVSADDINBUG-628: Change Qt Project settings lead to an error message + - Fixed QTVSADDINBUG-659: Missing Qurl debug visualizer + - Fixed QTVSADDINBUG-700: In debug, a QStandardItem will not be resolved unless inside the class + - Fixed QTVSADDINBUG-701: "Qt Project Settings" incorrect behavior + - Fixed QTVSADDINBUG-831: Error creating new Qt project when "application icon" option is enabled + - Fixed QTVSADDINBUG-883: Background build error when using common props file + - Fixed QTVSADDINBUG-919: Missing QML/QtQuick plugins when using a static Qt build + - Fixed QTVSADDINBUG-924: New Qt projects use C++14 standard by default + - Fixed QTVSADDINBUG-926: Qt module selection can't be confirmed + - Fixed QTVSADDINBUG-930: "Finish" button always disabled in Qt Class Wizard + - Fixed QTVSADDINBUG-936: Generated C++ source file fails to compile with precompiled header + - Fixed QTVSADDINBUG-941: "Translation disabled during build; skipping" + - Fixed QTVSADDINBUG-943: Document Qt 6 Support + - Fixed QTVSADDINBUG-950: Get Qt build settings from 'vcapp' qmake template + - Fixed QTVSADDINBUG-952: lupdate is not run during build + + +Qt Visual Studio Tools version 2.8.0: + +Changes +------- + - Added support for Visual Studio 2022 + - Fixed QTVSADDINBUG-927: Post-build refresh of IntelliSense data slows down/freezes the VS IDE + + +Qt Visual Studio Tools version 2.7.2: + +Changes +------- + - Fixed QTVSADDINBUG-805: Q_NAMESPACE needs mocing + - Fixed QTVSADDINBUG-871: lupdate/lrelease not working on .ts file context menu + - Fixed QTVSADDINBUG-873: Update list of Qt modules + - Fixed QTVSADDINBUG-874: Add option to open Designer in detached window + - Fixed QTVSADDINBUG-875: Can't open .ui files if path has space + - Fixed QTVSADDINBUG-887: Cannot add Qt version + - Fixed QTVSADDINBUG-888: lupdate fails to locate source files + - Fixed QTVSADDINBUG-890: Background build and VS2019 freeze on open solution + - Fixed QTVSADDINBUG-898: .ts files not processed + - Fixed QTVSADDINBUG-916: Build concurrency issues + - Fixed QTVSADDINBUG-917: Build concurrency issues + - Added module selection popup in Qt Settings property page + - Optimized IntelliSense updates ("background build") + - UI feedback of IntelliSense update, with the option to cancel background tasks + - Context menu option to update Qt project IntelliSense information (Qt > Refresh IntelliSense) + - Notification of available development release + + +Qt Visual Studio Tools version 2.7.1: + +Changes +------- + - Translation tools integrated with Qt/MSBuild + - Qt.props property sheet accessible through the VS Property Manager window + - Qt editors are opened in a document window inside Visual Studio + - IntelliSense is updated on changes to .ui files + - The Qt Options UI will be displayed as part of the standard VS 'Tools > Options' dialog + - Added the following new fields to the Qt Options page: + * QML debugger connection timeout + * Path to Qt/MSBuild files + * Enable QML debugger (overrides project settings) + * Enable refresh of IntelliSense after build + * Enable refresh of IntelliSense when a .ui file is changed + - Pressing F1 will query Qt documentation + - Fixed QTVSADDINBUG-698: Qt online help not working + - Fixed QTVSADDINBUG-755: QML debug not working without a QRC file + - Fixed QTVSADDINBUG-822: Incorrect generation of command line options + - Fixed QTVSADDINBUG-825: Calls to qmake fail silently + - Fixed QTVSADDINBUG-826: Crash in new-project wizard + - Fixed QTVSADDINBUG-827: Command line options of Qt tools generated incorrectly + - Fixed QTVSADDINBUG-837: Debug output cluttered with QML debugger status messages + - Fixed QTVSADDINBUG-841: Incremental build failure + - Fixed QTVSADDINBUG-857: Build fails due to concurrency issues + + +Qt Visual Studio Tools version 2.6.0: + +Changes +------- + - Support for cross-compilation with the Linux development VS workload + - Integration of Qt for Windows deployment tool (windeployqt) + - Fixed QTVSADDINBUG-797: Incorrect generation of command-line flags for Qt tools + + +Qt Visual Studio Tools version 2.5.2: + +Changes +------- + - Fixed QTVSADDINBUG-749: lupdate/lrelease options ignored + - Fixed QTVSADDINBUG-756, QTVSADDINBUG-783: Qt tools generate files outside of project directory + - Fixed QTVSADDINBUG-760: repc output directory not added to include path + - Fixed QTVSADDINBUG-772, QTVSADDINBUG-777: Qt tools fail to run if path contains spaces + - Fixed QTVSADDINBUG-780: Regression in Qt Quick compiler integration + - Fixed QTVSADDINBUG-781: Error accessing Qt version when using non-ASCII characters in paths + - Fixed QTVSADDINBUG-782: Incorrect build settings in the debug configuration of Qt Console apps + - Fixed QTVSADDINBUG-785: OBJECTS_DIR ignored when importing .pro + - Fixed QTVSADDINBUG-786: repc command line generated incorrectly + - Fixed QTVSADDINBUG-790: Build errors when temp directory ends with .lnk + - Fixed QTVSADDINBUG-792: Files with Q_OBJECT macro not handled correctly + - Fixed QTVSADDINBUG-793: Backslashes in Qt version name causes error + + +Qt Visual Studio Tools version 2.5.1: + +Changes +------- + - Minimum supported version of VS2017 (15.8.28010.0) enforced by installer + - Improved IntelliSense synchronization + - Fixed QTVSADDINBUG-545: Missing support for custom installations of Qt + - Fixed QTVSADDINBUG-771: .pro import does not replace Qt path with $(QTDIR) + - Fixed QTVSADDINBUG-771: .pro import removes explicit reference to opengl32.lib + + +Qt Visual Studio Tools version 2.5.0: + +Changes +------- + - New-project wizards will now allow choice of Qt version per configuration (QTVSADDINBUG-737) + - Added the Qt Quick Application project wizard (QTVSADDINBUG-589) + - Renamed Qt GUI Application wizard to Qt Widgets Application (QTVSADDINBUG-589) + - Added support for rcc two-pass mode (QTVSADDINBUG-564) + - Fixed QTVSADDINBUG-642: Ignoring changes to VS document folder path when deploying .natvis + - Fixed QTVSADDINBUG-643: Debug visualizer for QHash does not work with const key type + - Fixed QTVSADDINBUG-644: Error message "Qt not configured" does not identify the project + - Fixed QTVSADDINBUG-646: QML debugging will not work if command-line arguments are added + - Fixed QTVSADDINBUG-647: If no Qt version is configured in VS, a generic error message is shown + - Fixed QTVSADDINBUG-648: Importing .pro shows qmake status and error messages out-of-sync + - Fixed QTVSADDINBUG-704: Importing .pro ignores private Qt modules + - Fixed QTVSADDINBUG-711: Incompatibility with VS unity builds + - Fixed QTVSADDINBUG-721: Target path for Qt properties file cannot be changed + - Fixed QTVSADDINBUG-732: Importing .pro blindly substitutes the string 'release' in file names + - Fixed QTVSADDINBUG-733: NDEBUG defined in Release configurations + + +The Qt Visual Studio Tools version 2.4.3 includes several bug fixes. + +Changes +------- + - Fixed QTVSADDINBUG-560: New Qt projects with duplicate GUID + - Fixed QTVSADDINBUG-685: Property sheets not working with Qt projects + - Fixed QTVSADDINBUG-690: All configs not named 'Debug' use release build settings + - Fixed QTVSADDINBUG-702: Conversion errors when project has empty property values + - Fixed QTVSADDINBUG-703: Pre-compiled header disabled for generated sources + - Fixed QTVSADDINBUG-708: Link errors with projects including Windows resources (.rc file) + - Fixed QTVSADDINBUG-709: Qt-related menu items show in non-Qt project context menu + - Fixed QTVSADDINBUG-712: Qt INCLUDEPATH property has incorrect values + - Fixed QTVSADDINBUG-717: Qt project build fails when multiple VS instances are launched + - Fixed QTVSADDINBUG-722: Error getting Qt property values from generated Makefile + - Fixed QTVSADDINBUG-723: Projects built multiple times when dependencies are defined + + +The Qt Visual Studio Tools version 2.4.2 includes several bug fixes. + +Changes +------- + - Fixed QTVSADDINBUG-674: Error importing .pro: "invalid character in path" + - Fixed QTVSADDINBUG-676: Link error in project with references to other projects + - Fixed QTVSADDINBUG-681: Error importing .pro using static build of Qt + - Fixed QTVSADDINBUG-683: Error building with long PATH value + - Fixed QTVSADDINBUG-684: Build error: " was unexpected + - Fixed QTVSADDINBUG-693: Compiling sources generated by Qt tools produces unexpected .asm files + - Fixed QTVSADDINBUG-694: Project is never up-to-date + - Fixed QTVSADDINBUG-699: Error compiling qrc-generated sources with pre-compiled headers + + +The Qt Visual Studio Tools version 2.4.1 includes several bug fixes. + +Rev. 12 +------- + - Fixed QTVSADDINBUG-663: Project format conversion fails due to unexpected elements in .vcxproj + - Fixed QTVSADDINBUG-665: $(DefaultVersion) no longer works + - Fixed QTVSADDINBUG-670: Unicode WinAPI functions called despite using multi-byte char set + - Fixed QTVSADDINBUG-671: Error "command too long" when using static build of Qt + - Fixed QTVSADDINBUG-672: Extension does not load in VS2019 v16.2 or earlier + - Fixed QTVSADDINBUG-673: Intellisense is unable to locate Qt headers + - Fixed QTVSADDINBUG-680: Build errors when using an earlier version of Qt (<= 5.7) + - Fixed QTVSADDINBUG-682: Menu open "Qt Project Settings" opens empty dialog + +Changes +------- + - Fixed QTVSADDINBUG-391: Qt VS Tools no longer works with static builds + - Fixed QTVSADDINBUG-496: VS crashes if no Qt version is installed + - Fixed QTVSADDINBUG-545: No support for third-party library managers (e.g. vcpkg) + - Fixed QTVSADDINBUG-597: Converted projects lose excluded-from-build settings + - Fixed QTVSADDINBUG-640: Qt tools not invoked if project includes a "custom build step" + - Fixed QTVSADDINBUG-650: Setting the QtInstall property to a path does not work + - Fixed QTVSADDINBUG-651: The QtInstall property does not expand macros + - Fixed QTVSADDINBUG-653: Build errors when using configuration names containing spaces + - Fixed QTVSADDINBUG-657: Projects imported from .pro files lose reference to QtHelp module + - Fixed QTVSADDINBUG-658: Build errors in Qt project with pre-compiled headers + - Fixed QTVSADDINBUG-661: Selected files build: missing path to uic-generated headers + + +The Qt Visual Studio Tools version 2.4.0 implements Qt project settings as MSBuild properties, and +also adds support for Qt Remote Objects. + +Changes +------- + - Qt settings are now stored as fully fledged MSBuild properties (QTVSADDINBUG-575) + - Added support for Qt Remote Objects (QTVSADDINBUG-585) + - Fixed QTVSADDINBUG-598: Cannot export library projects to .pro files + - Fixed QTVSADDINBUG-632: Include path out-of-sync between moc and C++ compiler + + +The Qt Visual Studio Tools version 2.3.2 contains several bug fixes. + +Changes +------- +- Fixed QTVSADDINBUG-601: Pressing Alt-F1 crashes VS or fails to find selected documentation +- Fixed QTVSADDINBUG-608: Error in QRC compilation when paths contain spaces +- Fixed QTVSADDINBUG-609: C++ compiler output directories are not created +- Fixed QTVSADDINBUG-610: QML debug blocked when path to executable contains spaces +- Fixed QTVSADDINBUG-611: Build errors when combining RelativeDir with absolute paths in Qt tools +- Fixed QTVSADDINBUG-613: Project dependencies ignored +- Fixed QTVSADDINBUG-615: Unable to set breakpoints in .js files +- Fixed QTVSADDINBUG-618: Error when building with FIPS group policy enabled +- Will now display the version of the Qt Visual Studio Tools in the main menu (QTVSADDINBUG-607). + + +The Qt Visual Studio Tools version 2.3.1 adds support for Visual Studio 2019. This version also +includes fixes to bugs reported in the previous version. + +Changes +------- +- Added support for Visual Studio 2019 +- Extension initialization will now run in the background +- Fixed QTVSADDINBUG-583: Incorrect conversion of custom build to Qt/MSBuild +- Fixed QTVSADDINBUG-592: Custom build steps running after Qt tools and C++ compilation +- Fixed QTVSADDINBUG-593: .natvis files not installed if visualizers directory does not yet exist +- Fixed QTVSADDINBUG-594: Failed to build project with QRC files and precompiled headers +- Fixed QTVSADDINBUG-599: Debug session frozen if QML debugging is enabled +- Fixed QTVSADDINBUG-601: Pressing Alt+F1 for help on a Qt class member crashes Visual Studio +- Fixed QTVSADDINBUG-604: Conversion to Qt/MSBuild seems to freeze when errors occur + + +The Qt Visual Studio Tools version 2.3.0 introduces QML debugging in Visual Studio, as well as the +ahead-of-time compilation of QML files using the Qt Quick Compiler. This release also includes a +number of bug fixes. + +Changes +------- +- Debug QML programs in the Visual Studio IDE +- Ahead-of-time compilation of QML files using the Qt Quick Compiler +- Fixed QTVSADDINBUG-120: Output directories (MOC_DIR, UI_DIR, RCC_DIR) in imported .pro ignored +- Fixed QTVSADDINBUG-378: Debug data visualization not working with Qt built with namespace +- Fixed QTVSADDINBUG-526: QRC not rebuilt if resources are updated +- Fixed QTVSADDINBUG-550: Standard Visual Studio macros not allowed in rcc and uic path setting +- Fixed QTVSADDINBUG-584: Compilation of selected files (i.e. 'right-click' > 'Compile') not working + + +The Qt Visual Studio Tools version 2.2.2 contains several bug fixes. + +Changes +------- +- Fixed QTVSADDINBUG-544: Build is never up-to-date, even if no files have been changed. +- Fixed QTVSADDINBUG-551: Qt VS Tools adds a delay to Visual Studio startup. +- Fixed QTVSADDINBUG-558: Missing Qt module: Data Visualization +- Fixed QTVSADDINBUG-559: Missing Qt module: Charts +- Fixed QTVSADDINBUG-563: Error "same key already added" when converting project to Qt/MSBuild +- Fixed QTVSADDINBUG-566: Error converting to Qt/MSBuild if custom build command has a newline +- Fixed QTVSADDINBUG-569: %QTMSBUILD% environment variable always overwritten at start-up + + +The Qt Visual Studio Tools version 2.2.1 contains several bug fixes. + +Changes +------- +- Fixed QTVSADDINBUG-509: Noticeable delays when saving any .cpp file in larger projects created + with previous versions of the Qt VS Tools +- Fixed QTVSADDINBUG-510: Errors converting .pro files with long INCLUDEPATH definitions +- Fixed QTVSADDINBUG-511: Incorrect usage of $(IntDir) in the definition of QtWorkFilePath +- Fixed QTVSADDINBUG-512: PrecompiledHeaders.h include missing in moc after adding new class +- Fixed QTVSADDINBUG-514: Quotes removed from preprocessor definition +- Fixed QTVSADDINBUG-515: Visual Studio unresponsive while Converting custom build to Qt/MSBuild +- Fixed QTVSADDINBUG-520: Delay when starting build in solutions with many projects/configurations +- Fixed QTVSADDINBUG-521: Unable to use Qt version if it was built with -qtlibinfix +- Fixed QTVSADDINBUG-522: Duplicating defines and include paths from moc in the C++ dynamic source +- Fixed QTVSADDINBUG-523: Qt VS Tools stopped working with static builds +- Fixed QTVSADDINBUG-530: When a file includes the generated moc output for a header file as well + as its own generated moc output then it will fail to build +- Fixed QTVSADDINBUG-531: Project not being built when a .uic or .qrc file is changed +- Fixed QTVSADDINBUG-532: Delay when continuing execution after hitting breakpoint +- Fixed QTVSADDINBUG-533: Errors in conversion to Qt/MSBuild when using MOC_DIR or the '@' option +- Fixed QTVSADDINBUG-534: Problems with inheritance of properties defined in property sheets. +- Fixed QTVSADDINBUG-535: Problems with incremental build +- Fixed QTVSADDINBUG-537: Delay after changing C++ properties in large project +- Fixed QTVSADDINBUG-538: Incorrect escaping of paths in command line strings + + +The Qt Visual Studio Tools version 2.2.0 is the first official release of the Visual Studio add-in +for Visual Studio 2017, 2015 and 2013. + +Changes +------- +- New approach to moc, rcc and uic integration using MSBuild rules and targets +- No longer adds generated files to projects +- Creates Universal Windows projects when using a WinRT version of Qt +- Allows import of .pro files using a WinRT version of Qt +- Optimized the performance of adding header files (QTVSADDINBUG-64) +- Optimized the performance of the .pro import procedure (QTVSADDINBUG-442) +- Parallel execution of moc, rcc and uic processes (QTVSADDINBUG-124) +- Uses multi-processor compilation of C++ by default (QTVSADDINBUG-443) +- Updated list of Qt modules (QTVSADDINBUG-411) +- Fixed QTVSADDINBUG-105: moced source files are output to the same directory, prone to filename + clashes +- Fixed QTVSADDINBUG-140: VS2012 Doesn't start-up anymore correctly after VSAddin usage. +- Fixed QTVSADDINBUG-363: When the QTDIR variable is set after the LocalDebuggerEnvironment + variable in the users file for a project then it will not find the debugger +- Fixed QTVSADDINBUG-439: Visual Studio Addin creates bad project file if "flat" flag is removed + from config before converting to Visual Studio project +- Fixed QTVSADDINBUG-479: Error building Qt projects in VS 2017: SDK not found +- Fixed QTVSADDINBUG-485: Microsoft.Cpp.$(Platform).user.props absent +- Fixed QTVSADDINBUG-489: When a qrc file is added to the project it is defaulting to being + processed with -no-compress which is not the default on the command line +- Fixed QTVSADDINBUG-491: lupdate no longer works via Visual Studio Add-in +- Fixed QTVSADDINBUG-493: The application exited with an error (ExitCode 1) +- Fixed QTVSADDINBUG-504: Changing the project's Qt from Desktop to UWP causes linker errors +- Fixed QTVSADDINBUG-505: Opening a .pro file that does not contain Qt3D causes "3D module" to be + checked in project's Qt settings + + +The Qt Visual Studio Tools version 2.1.2 contains support for VS 2017 and several bug fixes. + +QTVSADDINBUG-459: Fix reported issues against the VS2017 beta release +QTVSADDINBUG-396: Fix new item with VS not generating the moc +QTVSADDINBUG-472: Fix opening pro file with Qt 5.9 +QTVSADDINBUG-460: Fix package containing corrupted data + + +The Qt Visual Studio Tools version 2.1.1 contains a bug fix for QTVSADDINBUG-453 + +The Qt Visual Studio Tools version 2.1.0 contains improvements for QML syntax highlighting and +several bug fixes. + +- Some speed improvements while importing large qmake based projects +- Better handling of $(QTDIR) environment and possible related errors +- Use TextMate language files for QML and qmake syntax highlighting (VS 2015) + +- QTVSADDINBUG-445: Fix wrong usage of QT_STATIC and QT_DLL +- QTVSADDINBUG-407: Fix Environment variables are not saved/restored after VS restart + + +The Qt Visual Studio Tools version 2.0.0 is an update of the existing Add-In implementation +to the newer Visual Studio Package (VSIX extension) system. + +Changes +------- +- Rewrite the Add-In as VSIX extension +- Known issues: Missing QML, help and localization support + +The Qt Visual Studio Add-In version 1.2.5 contains an update of the Qt documentation +and some bug fixes. + +Changes +------- +- Qt 5.6 help documents included. +- Fixed wrong VCProjectEngine dependency for 2012 installer, which prevented the Add-In from being + loaded (QTVSADDINBUG-392) +- Use correct solution platform when creating projects using x64 Qt build (QTVSADDINBUG-377) +- Fixed several issues with projects' Qt modules settings (QTVSADDINBUG-344, QTVSADDINBUG-359, + QTVSADDINBUG-388, QTVSADDINBUG-390) +- Do not overwrite debugging environment on project loading (QTVSADDINBUG-375) +- Fixed issues that prevented Qt 5.6 version from being registered to the Add-In (QTVSADDINBUG-418) + +The Qt Visual Studio Add-In version 1.2.4 contains updated help +documents and a bug fixes. + +Changes +------- +- Qt 5.4 help documents included. +- Fixed crash when creating new Qt5 app in VS2008 (QTVSADDINBUG-385) + +The Qt Visual Studio Add-In version 1.2.3 contains support for VS2013, +updated help documents and some bug fixes. + +Changes +------- +- Visual Studio 2013 support. +- Qt 5.3 help documents included. +- Handle wchar_t as builtin type, fixed. (QTVSADDINBUG-171) +- Fix for detecting wince build. (QTVSADDINBUG-176) +- Release project with debug info enabled links wrong qt dll's, fixed.(QTVSADDINBUG-350) +- Generated pri/pro files have mixed line endings (LF/CRLF), fixed.(QTVSADDINBUG-351) +- Intermediate Directory needs a trailing slash, fixed.(QTVSADDINBUG-356) + +The Qt Visual Studio Add-In version 1.2.2 contains updated help documents and +two bug fixes. + +Changes +------- +- Qt 5.1 help documents included. +- Incorrect overwriting of the environment setting value on every build/run + removed. (QTVSADDINBUG-159) +- Links in Qt help not working, fixed. (QTVSADDINBUG-160) + +The Qt Visual Studio Add-In version 1.2.1 contains multiple bugfixes and +improvements. + +Changes +------- +- Qt 5.0.1 help documents included +- Qmake wrapper COM component removed and replaced with command line tool + qmakefilereader to avoid need to load Qt5 library binaries to Visual Studio + process. And to get installation of add-in easier, especially with VS2008. +- Qt4 add-in is not allowed to run same time, if found it will be closed. + Also if Qt4 add-in is present in system, default editor values for .ts, .ui + and .qrc file types are not permanently overwritten by Qt5 add-in. Values are + written when Qt5 add-in loads and Qt4 values are set when Qt5 add-in unloads. + This should make it possible to install and use both Qt4 and Qt5 versions of + add-in. Not to use same time but in turns. Though user must be careful what he + does. +- Recognizing Qt5 CE build. (QTVSADDINBUG-153) +- Incorrect additional directories paths set when Qt modules added/removed from + Qt Project Settings page. (QTVSADDINBUG-151) +- Include directories search path for some Qt modules fixed (QTVSADDINBUG-150) +- Invalid Qt module names fixed (QTVSADDINBUG-142) +- Crash when importing .pri file fixed (QTVSADDINBUG-139) +- Qt default settings saving problem fixed (QTVSADDINBUG-137) +- Naming of precompiled header fixed (QTVSADDINBUG-132) +- Support for debugger visualizers in VS2012 (QTVSADDINBUG-129) +- Setting Qt libraries path for debugging session fixed (QTVSADDINBUG-125) + +The Qt Visual Studio Add-In version 1.2.0 supports Qt 5.0. + +Changes +------- +- Parameter passing to lupdate fixed (QTVSADDINBUG-131) +- Project creation wizards updated to follow Qt5 module structure +- Project settings form updated to follow Qt5 module structure +- Help documents updated to Qt5 +- No more Qt4 project creation wizards and project settings +- Support for Visual Studio 2012 +- All project files (*.sln, *.csproj etc) are renamed to Qt5 + +The Qt Visual Studio Add-In version 1.1.11 contains multiple bugfixes and +improvements. + +Changes +------- +- Documentation updated +- Documentation support for Visual Studio 2010. (QTVSADDINBUG-15) +- Fixed always moc'ing problem. (QTVSADDINBUG-92) +- Fixed .pro file opening having include to "." path. (QTVSADDINBUG-117) +- Removed extra back slashes from qmake arg paths. +- Fixed some additional include dir handling causing same path to be added multiple times to custom + build step. + +The Qt Visual Studio Add-In version 1.1.10 contains multiple bugfixes and +improvements. + +Changes +------- +- Be aware of comments when parsing qmake.conf. (QTVSADDINBUG-76) +- Fixed exception when using using unsupported lupdate parameters. (QTVSADDINGBUG-70) +- Detecting the MSVC version from qmake.conf was fixed. +- Added QtScriptTools and QtUiTools to project settings. (QTVSADDINBUG-71) +- Removed the MSVC version check when adding Qt versions. As this "feature" seems to cause more + trouble than it solves, we decided to remove it. Checking for the makefile generator should at + least prevent people from trying to add MinGW builds. +- Fixed creation of addin projects from 64 bit Qt qmake projects. +- Do not add the include path "$QTDIR/include/qtmain". + This path is a relict and confuses IntelliSense. (QTVSADDINBUG-81) +- Be aware of strings when looking for Q_OBJECT macros. (QTVSADDINBUG-75) +- Fix problems with the debugging environment. (QTVSADDINBUG-93) +- Pass correct QtDesigner4.lib library name to linker. (QTVSADDINBUG-85) +- Fix cleaning of autoexp.dat on uninstallation for VS 2005. (QTVSADDINBUG-67) +- Use slashes as path separators in includes in moc files. (QTVSADDINBUG-84) +- Choose a valid Qt version if the default Qt version becomes invalid. (QTVSADDINBUG-60) +- Prevent infinite loop if QtAppWrapper cannot connect. (QTVSADDINBUG-73) +- Add and remove the correct OpenGL libraries when changing project's Qt version. (QTVSADDINBUG-80) +- Use project macro for filenames in moc step. (QTVSADDINBUG-30) +- Fixed batch builds of Qt projects for multiple platforms. (QTVSADDINBUG-51) + +The Qt Visual Studio Add-In version 1.1.9 contains multiple bugfixes and +improvements. + +Changes +------- +- Fix a regression when importing .pro files that was introduced in 1.1.8. +- Make the add-in work with Intel VTune projects. (QTVSADDINBUG-65) +- Check for compatibility with VS version when adding new Qt version. This will hopefully stop + people from trying to use a MinGW Qt build with Visual Studio. (QTVSADDINBUG-58) +- Added possiblity to specifiy lupdate/lrelease options. (QTVSADDINBUG-48) + +The Qt Visual Studio Add-In version 1.1.8 contains multiple bugfixes and +improvements. + +Changes +------- +- Do not save the QTDIR in a property sheet for VS 2010. (QTVSADDINBUG-12) +- QtDeclarative module added to the modules page and wizards. (QTVSADDINBUG-44) +- Fix import of .pri files. (QTVSADDINBUG-38) +- Save the modified generated .vc(x)proj after .pro file import. (QTVSADDINBUG-40) +- Proper handling of moc file exclusion when saving cpp files. (QTVSADDINBUG-33) +- Remove user macro from property sheed when $(QTDIR) is used in VS2010. (QTVSADDINBUG-12) +- Documented possibility to add $(QTDIR) version. (QTVSADDINGBUG-19) +- Fix broken include paths in moc calls for VS 2010. (QTVSADDINBUG-34) +- Fix import of previously unloaded projects. (QTVSADDINBUG-31) +- Preserve line breaks in custom build steps after import. (QTVSADDINBUG-32) +- Fix exception that could be raised when changing the Qt version of solutions that contained + non-C++ projects. (QTVSADDINBUG-50) +- Fix moc command line creation for include paths that contain whitespace + characters. (QTVSADDINBUG-53) +- Fixed the Windows CE wizards that stopped working under certain circumstances. (QTVSADDINBUG-42) +- Several fixes for Windows CE project generation. +- Qt 4.7.2 documentation included. + +The Qt Visual Studio Add-In version 1.1.7 contains multiple bugfixes and +improvements. + +Changes +------- +- Fix import of .pro files with "CONFIG -= flat". (QTVSADDINBUG-10) +- Fix naming of classes in generated .ui files. (QTVSADDINBUG-9) +- Visualizers for QPolygon and QPolygonF fixed. (QTVSADDINBUG-8) +- Fix expanding filters on build. +- Fix Qt 4.7 qmake warning wrt backslash escaping on import. +- VS2010: fix AddMocStep for new header files. (QTVSADDINBUG-2) +- GUI class wizard: fix .ui file overwriting check. (QTVSADDINBUG-3) +- When adding moc step, do not assume that cpp file is present. (QTVSADDINBUG-1) + +The Qt Visual Studio Add-In version 1.1.6 contains multiple bugfixes and +improvements. + +Changes +------- +- Fixed collapse of Generated files after import/build on Visual Studio +2005. (QTBUG-4750) +- Support for Visual Studio 2010. (QTBUG-5374) +- Added check for invalid variables in Rcc/Uic directories in Qt (project) settings. (QTBUG-6818) +- Updated documentation for changing the target platform. (QTBUG-7262) +- Translate filter names on .pro file import or when switching from a qmake generated + (qmake -tp vc) project to an Add-in project. (QTBUG-11325) +- Fixed regression in custom build step generation. (QTBUG-11527) +- Fixed "Change Solution's Qt version" for solutions that contain Intel Fortran + projects. (QTBUG-11567) +- Fix output paths for custom build steps in release configuration. (QTBUG-12145) +- Fix import if CONFIG contains "silent". (QTBUG-12344) +- Phonon is added automatically if WebKit is selected. (QTBUG-10154) +- Fixed display of uninitialized QSet and QHash object. (QTBUG-12890) + +The Qt Visual Studio Add-In version 1.1.5 contains multiple bugfixes and +improvements. + +Changes +------- +- Fixed setting of preprocessor definitions when changing to a Windows CE Qt version. (QTBUG-10564) +- Added contribution for QMap visualization. (QTBUG-3970) +- Recursively look for includes/defines in PropertySheets. (QTBUG-10406) +- Fixed bugs affecting Q_OBJECTs in .cpp files. +- On import, the content of .qrc files is added to the Resources filter of the Visual Studio + project. (QTBUG-10113) +- QRC Editor: The add-in will synchronize the list of resources in a .qrc file with the files in + the Resources filter of the project. (QTBUG-6103) +- QRC Editor: show full resource URL. (QTBUG-3801) +- Refresh moc steps when PCH options are changed. (QTBUG-7700) +- You don't have to be admin anymore to set Windows CE Qt versions. (QTBUG-9550) +- QtAppWrapper: Do not stop listening to new connections if error occurs. (QTBUG-8508) +- Handle C++ comments correctly. (QTBUG-7641) +- Support $(PlatformName) for moc, uic and rcc directories. (QTBUG-5814) +- Fixed exception when exporting non-C++ projects. (QTBUG-9234) +- Fixed module handling for static libraries. (QTBUG-8670) +- Resolve %BASECLASS% in Qt4Class template again, to keep old custom class wizard additions + working. (QTBUG-8615) +- Fixed wrong DLL names in Windows CE deployment of Qt modules. (QTBUG-8622) +- Fixed import of .pro files with library dependency paths that contain whitespace + characters. (QTBUG-8493) +- Added possibility to use mocDir without $(ConfigurationName). (QTBUG-7288) +- Fixed import for projects with relative moc dir settings. (QTBUG-8372) +- Fixed import for project without resources. +- Don't depend on .NET Framework 3.5 SP1. (QTBUG-8415) +- Fix moc steps for file names, that are contain substrings of variables, + like "Config.h" (QTBUG-8218) +- Detection of invalid class names in projects wizards. (QTBUG-8154) +- Fixed creation of translation files when custom cultures are present. (QTBUG-11124) + +The Qt Visual Studio Add-In version 1.1.4 contains multiple bugfixes and +improvements. + +Changes +------- +- Several bugs when opening .ui and .ts files have been fixed. The qtappwrapper has been + overhauled. (QTBUG-6857, QTBUG-7491) +- Importing .pro files with source files in subdirectories does not longer cause creation of + invalid moc steps. (QTBUG-7603) +- The Qt Multimedia module is now available in all project wizards and the Qt modules + dialog. (QTBUG-7487) +- The "Generated Files" filter is collapsed after .pro file import. (QTBUG-7687) +- Automatically checkout .ui, .qrc and .ts files if they are under source control. (QTBUG-7680) +- Remove .res files from the "Generated Files" filter after .pro file import as + they are not needed. (QTBUG-7494) +- Class wizards: Take care of forbidden characters when building include guards for + header files. (QTBUG-7811) +- Fix exception when accessing the Qt project settings for projects, that do not have + a compiler tool. (QTBUG-7930) +- Fix bug in moc step update, which occured when there was more than one file in + the "Outputs" property of the custom build step. (QTBUG-7937) +- Added an option to turn off the automatic moc steps update. (QTBUG-7938) + +The Qt Visual Studio Add-In version 1.1.3 contains multiple bugfixes and +improvements. + +Changes +------- +- Added checks to avoid unneeded access to .vcproj file when saving files + with Q_OBJECT. (QTBUG-5813) +- When using Visual Studio 2005 the project tree is no longer expanded + after each build. (QTBUG-6482) +- Switching between qmake and AddIn projects more robust. (QTBUG-6434) +- Set target machine explicitly when changing Qt version. (QTBUG-5206) +- Work around Visual Studio bug to support the simplified Chinese + version of Visual Studio. (QTBUG-6318) +- Fixed bug for Windows CE static builds. (QTBUG-3493) +- Create signature step for Windows CE projects. (QTBUG-3493) +- Fixed visualizers for QList and QMap. (very simple version for QMap as + an appropiate visualization seems impossible in autoexp.dat) (QTBUG-660 + and QTBUG-3970) +- Fixed visualizers for QVector. (QTBUG-7121) +- Support empty base classes in Qt4Class wizard. (QTBUG-6797) +- Support namespaces when creating Qt classes. (QTBUG-5227) + +The Qt Visual Studio Add-In version 1.1.2 contains multiple bugfixes and +improvements. + +Changes +------- +- Fixed various problems concerning precompiled headers (stdafx as default + (QTBUG-5226), change of settings when building...) +- Increased support for solution folders (QTBUG-4914) +- Detection of solution .pro files fixed (be aware of possible newline + after \) +- Fixed bug, which could appear when moc steps had to be updated. + The update caused an invalid moc commandline which made the build fail. +- Added support for Q_Gadget macro(QTBUG-5291) +- Project tree is not expanded on every build when using VS2005 (QTBUG-5291) +- Fixed conversion from AddIn project to Qmake project and vice + versa (QTBUG5380) +- Repair moc path when importing .pro file (QTBUG-4221) +- Support of commandline builds (QTBUG-5321) + (In VS2005 there are some issues when trying to load AddIns from the + commandline. See http://support.microsoft.com/kb/934517) + +The Qt Visual Studio Add-In version 1.1.1 contains multiple bugfixes and +improvements. + +Changes +------- +- Fixed startup of Qt Designer and Qt Linguist when the application was + not build for the current project's Qt Version. The Add-In tries to find + the needed tool inside the directory of the other registered Qt Versions. +- "Generate basic pro file" gets disabled in project's context menu and Qt + menu if the current project is a qmake generated project. +- When importing a solution file while a solution is opened, the user gets + the possibility to automatically close the current solution. +- Avoid infinite recursion in inclusion which happened when moccing .cpp + files, which used precompiled headers. +- Translation files are added when exporting project to a .pri file. +- Project is cleaned, when its Qt version is changed. +- As there is no way to avoid the expansion of generated files filter when + the project is built the first time, the generated files are collapsed + after the build has finished (QTBUG-4750). +- Changing the project's Qt Version after importing a .pro file does not + longer make the build fail (QTBUG-4756) +- Exclusion of .cpp and .h files cause the mocced files to be excluded from + build too (QTBUG-3404) +- Adding a Qt class no longer implies that precompiled headers are used. +- Fixed processing of lupdate in projects with a big amount of files +(QTBUG-4783). + +The Qt Visual Studio Add-In version 1.1.0 contains multiple bugfixes and +improvements. + +Changes +------- +- The custom build step generation has been changed to use a subfolder for + every configuration. This solves the problem of heavy changes in the vcproj + file whenever the user switches to another build configuration. (Task 251918) +- Qt Designer, Qt Linguist and the QRC Editor are started via a properly + registered editor wrapper. Now its possible to open ui files in other + editors, like the built-in XML editor. (Task 250601) +- Projects can be converted from Qt Add-in projects to qmake generated + projects and back. (Task 153484) +- Fixing the behaviour of adding a file to a filter. (Task 254968) +- Opening a vcproj file that has a non-defined Qt version works now like in + the old Qt VS integration. (Task 258704) +- The detection of Windows CE Qt builds has been fixed. +- OpenGL Desktop Windows libraries aren't added to linker options in + Windows CE projects. (Task 260550) +- If there's no Generated Files folder, then we don't try to delete its + content. (Task 260723) +- Fix bug for solutions that contain disabled (unloaded) projects. (Task 259807) +- Switching between static and dynamic Qt builds has been fixed. +- Add-in's documentation has been updated. + +The Qt Visual Studio Add-In version 1.0.2 contains multiple bugfixes. + +Changes +------- +- Platform was not set correctly when solution contains several projects. +- Repair broken QTDIR Variable when project is added to solution. +- Fix problem with dots in pro file names. +- Qt module Phonon added to wizards and dialogs. +- Don't mess around with whitespaces in every source file we add. (Task 251987) +- Prevent duplicates in additional dependencies, includes etc. +- Fix rare exception when importing solution pro file. +- Fix switching between static and dynamic Qt build. +- Enable the user to add a "$(QTDIR)" Qt version even if QTDIR is not set. + +The Qt Visual Studio Add-In version 1.0.1 contains multiple bugfixes. + +Changes +------- +- Qt .pro file import fixed for projects containing SUBDIRS. +- Fixed moc file build exclusion when saving a .cpp file. +- Keep user defined debugging environment when adding the Qt environment. +- Fix the display of full file paths in custom build tool descriptions. +- Documentation updated. + diff --git a/LICENSE.GPL3-EXCEPT b/LICENSE.GPL3-EXCEPT new file mode 100644 index 0000000..9fa4858 --- /dev/null +++ b/LICENSE.GPL3-EXCEPT @@ -0,0 +1,704 @@ +This is the GNU General Public License version 3, annotated with The +Qt Company GPL Exception 1.0: + +------------------------------------------------------------------------- + +The Qt Company GPL Exception 1.0 + +Exception 1: + +As a special exception you may create a larger work which contains the +output of this application and distribute that work under terms of your +choice, so long as the work is not otherwise derived from or based on +this application and so long as the work does not in itself generate +output that contains the output from this application in its original +or modified form. + +Exception 2: + +As a special exception, you have permission to combine this application +with Plugins licensed under the terms of your choice, to produce an +executable, and to copy and distribute the resulting executable under +the terms of your choice. However, the executable must be accompanied +by a prominent notice offering all users of the executable the entire +source code to this application, excluding the source code of the +independent modules, but including any changes you have made to this +application, under the terms of this license. + + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/QMakeFileReader/evalhandler.cpp b/QMakeFileReader/evalhandler.cpp new file mode 100644 index 0000000..49795db --- /dev/null +++ b/QMakeFileReader/evalhandler.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "evalhandler.h" + +void EvalHandler::message(int type, const QString &msg, const QString &fileName, int lineNo) +{ + Q_UNUSED(type); + Q_UNUSED(msg); + Q_UNUSED(fileName); + Q_UNUSED(lineNo); +} + +void EvalHandler::fileMessage(const QString &msg) +{ + Q_UNUSED(msg); +} + +void EvalHandler::aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type) +{ + Q_UNUSED(parent); + Q_UNUSED(proFile); + Q_UNUSED(type); +} + +void EvalHandler::doneWithEval(ProFile *parent) +{ + Q_UNUSED(parent); +} diff --git a/QMakeFileReader/evalhandler.h b/QMakeFileReader/evalhandler.h new file mode 100644 index 0000000..54bd82c --- /dev/null +++ b/QMakeFileReader/evalhandler.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EVALHANDLER_H +#define EVALHANDLER_H + +#include <qmakeevaluator.h> + +/** + * Dummy handler to please qmake's parser. + */ +class EvalHandler : public QMakeHandler +{ +public: + void message(int type, const QString &msg, const QString &fileName, int lineNo); + void fileMessage(const QString &msg); + void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type); + void doneWithEval(ProFile *parent); +}; + +#endif // EVALHANDLER_H diff --git a/QMakeFileReader/evaluator/README b/QMakeFileReader/evaluator/README new file mode 100644 index 0000000..5770e86 --- /dev/null +++ b/QMakeFileReader/evaluator/README @@ -0,0 +1,6 @@ +This is a copy of qmake's parser and evaluator from qmake/library +directory in the qtbase repository at git://gitorious.org/qt/qtbase.git. +It has been taken at 5e525d283d308fe462e83955996fc53d80465c1b. + +If you do changes to these files, please make sure they are upstreamed! + diff --git a/QMakeFileReader/evaluator/evaluator.pri b/QMakeFileReader/evaluator/evaluator.pri new file mode 100644 index 0000000..dcd5a7e --- /dev/null +++ b/QMakeFileReader/evaluator/evaluator.pri @@ -0,0 +1,22 @@ +INCLUDEPATH *= $$PWD +DEPENDPATH *= $$PWD + +INCLUDEPATH += \ + $$QMAKE_PARSER_DIR + +HEADERS += \ + $$PWD/ioutils.h \ + $$PWD/proitems.h \ + $$PWD/qmakeevaluator.h \ + $$PWD/qmakeevaluator_p.h \ + $$PWD/qmakeglobals.h \ + $$PWD/qmakeparser.h \ + $$PWD/qmake_global.h + +SOURCES += \ + $$PWD/ioutils.cpp \ + $$PWD/proitems.cpp \ + $$PWD/qmakebuiltins.cpp \ + $$PWD/qmakeevaluator.cpp \ + $$PWD/qmakeglobals.cpp \ + $$PWD/qmakeparser.cpp diff --git a/QMakeFileReader/evaluator/ioutils.cpp b/QMakeFileReader/evaluator/ioutils.cpp new file mode 100644 index 0000000..b35c163 --- /dev/null +++ b/QMakeFileReader/evaluator/ioutils.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ioutils.h" + +#include <qdir.h> +#include <qfile.h> + +#ifdef Q_OS_WIN +# include <windows.h> +#else +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#endif + +QT_BEGIN_NAMESPACE + +using namespace QMakeInternal; + +IoUtils::FileType IoUtils::fileType(const QString &fileName) +{ + Q_ASSERT(fileName.isEmpty() || isAbsolutePath(fileName)); +#ifdef Q_OS_WIN + DWORD attr = GetFileAttributesW((WCHAR*)fileName.utf16()); + if (attr == INVALID_FILE_ATTRIBUTES) + return FileNotFound; + return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FileIsDir : FileIsRegular; +#else + struct ::stat st; + if (::stat(fileName.toLocal8Bit().constData(), &st)) + return FileNotFound; + return S_ISDIR(st.st_mode) ? FileIsDir : FileIsRegular; +#endif +} + +bool IoUtils::isRelativePath(const QString &path) +{ + if (path.startsWith(QLatin1Char('/'))) + return false; +#ifdef Q_OS_WIN + if (path.startsWith(QLatin1Char('\\'))) + return false; + // Unlike QFileInfo, this won't accept a relative path with a drive letter. + // Such paths result in a royal mess anyway ... + if (path.length() >= 3 && path.at(1) == QLatin1Char(':') && path.at(0).isLetter() + && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) + return false; +#endif + return true; +} + +QStringRef IoUtils::fileName(const QString &fileName) +{ + return fileName.midRef(fileName.lastIndexOf(QLatin1Char('/')) + 1); +} + +QString IoUtils::resolvePath(const QString &baseDir, const QString &fileName) +{ + if (fileName.isEmpty()) + return QString(); + if (isAbsolutePath(fileName)) + return QDir::cleanPath(fileName); + return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName); +} + +inline static +bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16]) +{ + for (int x = arg.length() - 1; x >= 0; --x) { + ushort c = arg.unicode()[x].unicode(); + if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)))) + return true; + } + return false; +} + +QString IoUtils::shellQuoteUnix(const QString &arg) +{ + // Chars that should be quoted (TM). This includes: + static const uchar iqm[] = { + 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8, + 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78 + }; // 0-32 \'"$`<>|;&(){}*?#!~[] + + if (!arg.length()) + return QString::fromLatin1("\"\""); + + QString ret(arg); + if (hasSpecialChars(ret, iqm)) { + ret.replace(QLatin1Char('\''), QLatin1String("'\\''")); + ret.prepend(QLatin1Char('\'')); + ret.append(QLatin1Char('\'')); + } + return ret; +} + +QString IoUtils::shellQuoteWin(const QString &arg) +{ + // Chars that should be quoted (TM). This includes: + // - control chars & space + // - the shell meta chars "&()<>^| + // - the potential separators ,;= + static const uchar iqm[] = { + 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10 + }; + + if (!arg.length()) + return QString::fromLatin1("\"\""); + + QString ret(arg); + if (hasSpecialChars(ret, iqm)) { + // Quotes are escaped and their preceding backslashes are doubled. + // It's impossible to escape anything inside a quoted string on cmd + // level, so the outer quoting must be "suspended". + ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\"\\1\\1\\^\"\"")); + // The argument must not end with a \ since this would be interpreted + // as escaping the quote -- rather put the \ behind the quote: e.g. + // rather use "foo"\ than "foo\" + int i = ret.length(); + while (i > 0 && ret.at(i - 1) == QLatin1Char('\\')) + --i; + ret.insert(i, QLatin1Char('"')); + ret.prepend(QLatin1Char('"')); + } + return ret; +} + +QT_END_NAMESPACE diff --git a/QMakeFileReader/evaluator/ioutils.h b/QMakeFileReader/evaluator/ioutils.h new file mode 100644 index 0000000..3b04587 --- /dev/null +++ b/QMakeFileReader/evaluator/ioutils.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef IOUTILS_H +#define IOUTILS_H + +#include <qstring.h> + +QT_BEGIN_NAMESPACE + +namespace QMakeInternal { + +/*! + This class provides replacement functionality for QFileInfo, QFile & QDir, + as these are abysmally slow. +*/ +class IoUtils { +public: + enum FileType { + FileNotFound = 0, + FileIsRegular = 1, + FileIsDir = 2 + }; + + static FileType fileType(const QString &fileName); + static bool exists(const QString &fileName) { return fileType(fileName) != FileNotFound; } + static bool isRelativePath(const QString &fileName); + static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); } + static QStringRef fileName(const QString &fileName); // Requires normalized path + static QString resolvePath(const QString &baseDir, const QString &fileName); + static QString shellQuoteUnix(const QString &arg); + static QString shellQuoteWin(const QString &arg); + static QString shellQuote(const QString &arg) +#ifdef Q_OS_UNIX + { return shellQuoteUnix(arg); } +#else + { return shellQuoteWin(arg); } +#endif +}; + +} // namespace ProFileEvaluatorInternal + +QT_END_NAMESPACE + +#endif // IOUTILS_H diff --git a/QMakeFileReader/evaluator/proitems.cpp b/QMakeFileReader/evaluator/proitems.cpp new file mode 100644 index 0000000..4212155 --- /dev/null +++ b/QMakeFileReader/evaluator/proitems.cpp @@ -0,0 +1,450 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "proitems.h" + +#include <qfileinfo.h> +#include <qset.h> +#include <qstringlist.h> +#include <qtextstream.h> + +QT_BEGIN_NAMESPACE + +// from qhash.cpp +uint ProString::hash(const QChar *p, int n) +{ + uint h = 0; + + while (n--) { + h = (h << 4) + (*p++).unicode(); + h ^= (h & 0xf0000000) >> 23; + h &= 0x0fffffff; + } + return h; +} + +ProString::ProString() : + m_offset(0), m_length(0), m_file(0), m_hash(0x80000000) +{ +} + +ProString::ProString(const ProString &other) : + m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(other.m_hash) +{ +} + +ProString::ProString(const ProString &other, OmitPreHashing) : + m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(0x80000000) +{ +} + +ProString::ProString(const QString &str, DoPreHashing) : + m_string(str), m_offset(0), m_length(str.length()), m_file(0) +{ + updatedHash(); +} + +ProString::ProString(const QString &str) : + m_string(str), m_offset(0), m_length(str.length()), m_file(0), m_hash(0x80000000) +{ +} + +ProString::ProString(const char *str, DoPreHashing) : + m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0) +{ + updatedHash(); +} + +ProString::ProString(const char *str) : + m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0), m_hash(0x80000000) +{ +} + +ProString::ProString(const QString &str, int offset, int length, DoPreHashing) : + m_string(str), m_offset(offset), m_length(length), m_file(0) +{ + updatedHash(); +} + +ProString::ProString(const QString &str, int offset, int length, uint hash) : + m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(hash) +{ +} + +ProString::ProString(const QString &str, int offset, int length) : + m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(0x80000000) +{ +} + +void ProString::setValue(const QString &str) +{ + m_string = str, m_offset = 0, m_length = str.length(), m_hash = 0x80000000; +} + +uint ProString::updatedHash() const +{ + return (m_hash = hash(m_string.constData() + m_offset, m_length)); +} + +uint qHash(const ProString &str) +{ + if (!(str.m_hash & 0x80000000)) + return str.m_hash; + return str.updatedHash(); +} + +ProKey::ProKey(const QString &str) : + ProString(str, DoHash) +{ +} + +ProKey::ProKey(const char *str) : + ProString(str, DoHash) +{ +} + +ProKey::ProKey(const QString &str, int off, int len) : + ProString(str, off, len, DoHash) +{ +} + +ProKey::ProKey(const QString &str, int off, int len, uint hash) : + ProString(str, off, len, hash) +{ +} + +void ProKey::setValue(const QString &str) +{ + m_string = str, m_offset = 0, m_length = str.length(); + updatedHash(); +} + +QString ProString::toQString() const +{ + return m_string.mid(m_offset, m_length); +} + +QString &ProString::toQString(QString &tmp) const +{ + return tmp.setRawData(m_string.constData() + m_offset, m_length); +} + +QChar *ProString::prepareExtend(int extraLen, int thisTarget, int extraTarget) +{ + if (m_string.isDetached() && m_length + extraLen <= m_string.capacity()) { + m_string.reserve(0); // Prevent the resize() below from reallocating + QChar *ptr = (QChar *)m_string.constData(); + if (m_offset != thisTarget) + memmove(ptr + thisTarget, ptr + m_offset, m_length * 2); + ptr += extraTarget; + m_offset = 0; + m_length += extraLen; + m_string.resize(m_length); + m_hash = 0x80000000; + return ptr; + } else { + QString neu(m_length + extraLen, Qt::Uninitialized); + QChar *ptr = (QChar *)neu.constData(); + memcpy(ptr + thisTarget, m_string.constData() + m_offset, m_length * 2); + ptr += extraTarget; + *this = ProString(neu); + return ptr; + } +} + +ProString &ProString::prepend(const ProString &other) +{ + if (other.m_length) { + if (!m_length) { + *this = other; + } else { + QChar *ptr = prepareExtend(other.m_length, other.m_length, 0); + memcpy(ptr, other.constData(), other.m_length * 2); + if (!m_file) + m_file = other.m_file; + } + } + return *this; +} + +ProString &ProString::append(const QLatin1String other) +{ + const char *latin1 = other.latin1(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + int size = other.size(); +#else + int size = strlen(latin1); +#endif + if (size) { + QChar *ptr = prepareExtend(size, 0, m_length); + for (int i = 0; i < size; i++) + *ptr++ = QLatin1Char(latin1[i]); + } + return *this; +} + +ProString &ProString::append(QChar other) +{ + QChar *ptr = prepareExtend(1, 0, m_length); + *ptr = other; + return *this; +} + +// If pending != 0, prefix with space if appending to non-empty non-pending +ProString &ProString::append(const ProString &other, bool *pending) +{ + if (other.m_length) { + if (!m_length) { + *this = other; + } else { + QChar *ptr; + if (pending && !*pending) { + ptr = prepareExtend(1 + other.m_length, 0, m_length); + *ptr++ = 32; + } else { + ptr = prepareExtend(other.m_length, 0, m_length); + } + memcpy(ptr, other.m_string.constData() + other.m_offset, other.m_length * 2); + if (other.m_file) + m_file = other.m_file; + } + if (pending) + *pending = true; + } + return *this; +} + +ProString &ProString::append(const ProStringList &other, bool *pending, bool skipEmpty1st) +{ + if (const int sz = other.size()) { + int startIdx = 0; + if (pending && !*pending && skipEmpty1st && other.at(0).isEmpty()) { + if (sz == 1) + return *this; + startIdx = 1; + } + if (!m_length && sz == startIdx + 1) { + *this = other.at(startIdx); + } else { + int totalLength = sz - startIdx; + for (int i = startIdx; i < sz; ++i) + totalLength += other.at(i).size(); + bool putSpace = false; + if (pending && !*pending && m_length) + putSpace = true; + else + totalLength--; + + QChar *ptr = prepareExtend(totalLength, 0, m_length); + for (int i = startIdx; i < sz; ++i) { + if (putSpace) + *ptr++ = 32; + else + putSpace = true; + const ProString &str = other.at(i); + memcpy(ptr, str.m_string.constData() + str.m_offset, str.m_length * 2); + ptr += str.m_length; + } + if (other.last().m_file) + m_file = other.last().m_file; + } + if (pending) + *pending = true; + } + return *this; +} + +QString operator+(const ProString &one, const ProString &two) +{ + if (two.m_length) { + if (!one.m_length) { + return two.toQString(); + } else { + QString neu(one.m_length + two.m_length, Qt::Uninitialized); + ushort *ptr = (ushort *)neu.constData(); + memcpy(ptr, one.m_string.constData() + one.m_offset, one.m_length * 2); + memcpy(ptr + one.m_length, two.m_string.constData() + two.m_offset, two.m_length * 2); + return neu; + } + } + return one.toQString(); +} + + +ProString ProString::mid(int off, int len) const +{ + ProString ret(*this, NoHash); + if (off > m_length) + off = m_length; + ret.m_offset += off; + ret.m_length -= off; + if ((uint)ret.m_length > (uint)len) // Unsigned comparison to interpret < 0 as infinite + ret.m_length = len; + return ret; +} + +ProString ProString::trimmed() const +{ + ProString ret(*this, NoHash); + int cur = m_offset; + int end = cur + m_length; + const QChar *data = m_string.constData(); + for (; cur < end; cur++) + if (!data[cur].isSpace()) { + // No underrun check - we know there is at least one non-whitespace + while (data[end - 1].isSpace()) + end--; + break; + } + ret.m_offset = cur; + ret.m_length = end - cur; + return ret; +} + +QTextStream &operator<<(QTextStream &t, const ProString &str) +{ + t << str.toQString(); // XXX optimize ... somehow + return t; +} + +static QString ProStringList_join(const ProStringList &this_, const QChar *sep, const size_t sepSize) +{ + int totalLength = 0; + const int sz = this_.size(); + + for (int i = 0; i < sz; ++i) + totalLength += this_.at(i).size(); + + if (sz) + totalLength += sepSize * (sz - 1); + + QString res(totalLength, Qt::Uninitialized); + QChar *ptr = (QChar *)res.constData(); + for (int i = 0; i < sz; ++i) { + if (i) { + memcpy(ptr, sep, sepSize * sizeof(QChar)); + ptr += sepSize; + } + const ProString &str = this_.at(i); + memcpy(ptr, str.constData(), str.size() * sizeof(QChar)); + ptr += str.size(); + } + return res; +} + +QString ProStringList::join(const QString &sep) const +{ + return ProStringList_join(*this, sep.constData(), sep.size()); +} + +QString ProStringList::join(QChar sep) const +{ + return ProStringList_join(*this, &sep, 1); +} + +void ProStringList::removeAll(const ProString &str) +{ + for (int i = size(); --i >= 0; ) + if (at(i) == str) + remove(i); +} + +void ProStringList::removeAll(const char *str) +{ + for (int i = size(); --i >= 0; ) + if (at(i) == str) + remove(i); +} + +void ProStringList::removeDuplicates() +{ + int n = size(); + int j = 0; + QSet<ProString> seen; + seen.reserve(n); + for (int i = 0; i < n; ++i) { + const ProString &s = at(i); + if (seen.contains(s)) + continue; + seen.insert(s); + if (j != i) + (*this)[j] = s; + ++j; + } + if (n != j) + erase(begin() + j, end()); +} + +ProStringList::ProStringList(const QStringList &list) +{ + reserve(list.size()); + foreach (const QString &str, list) + *this << ProString(str); +} + +QStringList ProStringList::toQStringList() const +{ + QStringList ret; + ret.reserve(size()); + foreach (const ProString &str, *this) + ret << str.toQString(); + return ret; +} + +bool ProStringList::contains(const ProString &str, Qt::CaseSensitivity cs) const +{ + for (int i = 0; i < size(); i++) + if (!at(i).compare(str, cs)) + return true; + return false; +} + +bool ProStringList::contains(const char *str, Qt::CaseSensitivity cs) const +{ + for (int i = 0; i < size(); i++) + if (!at(i).compare(str, cs)) + return true; + return false; +} + +ProFile::ProFile(const QString &fileName) + : m_refCount(1), + m_fileName(fileName), + m_ok(true), + m_hostBuild(false) +{ + if (!fileName.startsWith(QLatin1Char('('))) + m_directoryName = QFileInfo( // qmake sickness: canonicalize only the directory! + fileName.left(fileName.lastIndexOf(QLatin1Char('/')))).canonicalFilePath(); +} + +ProFile::~ProFile() +{ +} + +QT_END_NAMESPACE diff --git a/QMakeFileReader/evaluator/proitems.h b/QMakeFileReader/evaluator/proitems.h new file mode 100644 index 0000000..9ef7d6d --- /dev/null +++ b/QMakeFileReader/evaluator/proitems.h @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROITEMS_H +#define PROITEMS_H + +#include "qmake_global.h" + +#include <qstring.h> +#include <qvector.h> +#include <qhash.h> + +QT_BEGIN_NAMESPACE + +class QTextStream; + +#ifdef PROPARSER_THREAD_SAFE +typedef QAtomicInt ProItemRefCount; +#else +class ProItemRefCount { +public: + ProItemRefCount(int cnt = 0) : m_cnt(cnt) {} + bool ref() { return ++m_cnt != 0; } + bool deref() { return --m_cnt != 0; } + ProItemRefCount &operator=(int value) { m_cnt = value; return *this; } +private: + int m_cnt; +}; +#endif + +#ifndef QT_BUILD_QMAKE +# define PROITEM_EXPLICIT explicit +#else +# define PROITEM_EXPLICIT +#endif + +class ProKey; +class ProStringList; +class ProFile; + +class ProString { +public: + ProString(); + ProString(const ProString &other); + PROITEM_EXPLICIT ProString(const QString &str); + PROITEM_EXPLICIT ProString(const char *str); + ProString(const QString &str, int offset, int length); + void setValue(const QString &str); + void clear() { m_string.clear(); m_length = 0; } + ProString &setSource(const ProString &other) { m_file = other.m_file; return *this; } + ProString &setSource(const ProFile *pro) { m_file = pro; return *this; } + const ProFile *sourceFile() const { return m_file; } + + ProString &prepend(const ProString &other); + ProString &append(const ProString &other, bool *pending = 0); + ProString &append(const QString &other) { return append(ProString(other)); } + ProString &append(const QLatin1String other); + ProString &append(const char *other) { return append(QLatin1String(other)); } + ProString &append(QChar other); + ProString &append(const ProStringList &other, bool *pending = 0, bool skipEmpty1st = false); + ProString &operator+=(const ProString &other) { return append(other); } + ProString &operator+=(const QString &other) { return append(other); } + ProString &operator+=(const QLatin1String other) { return append(other); } + ProString &operator+=(const char *other) { return append(other); } + ProString &operator+=(QChar other) { return append(other); } + + void chop(int n) { Q_ASSERT(n <= m_length); m_length -= n; } + void chopFront(int n) { Q_ASSERT(n <= m_length); m_offset += n; m_length -= n; } + + bool operator==(const ProString &other) const { return toQStringRef() == other.toQStringRef(); } + bool operator==(const QString &other) const { return toQStringRef() == other; } + bool operator==(QLatin1String other) const { return toQStringRef() == other; } + bool operator==(const char *other) const { return toQStringRef() == QLatin1String(other); } + bool operator!=(const ProString &other) const { return !(*this == other); } + bool operator!=(const QString &other) const { return !(*this == other); } + bool operator!=(QLatin1String other) const { return !(*this == other); } + bool operator!=(const char *other) const { return !(*this == other); } + bool isNull() const { return m_string.isNull(); } + bool isEmpty() const { return !m_length; } + int length() const { return m_length; } + int size() const { return m_length; } + QChar at(int i) const { Q_ASSERT((uint)i < (uint)m_length); return constData()[i]; } + const QChar *constData() const { return m_string.constData() + m_offset; } + ProString mid(int off, int len = -1) const; + ProString left(int len) const { return mid(0, len); } + ProString right(int len) const { return mid(qMax(0, size() - len)); } + ProString trimmed() const; + int compare(const ProString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(sub.toQStringRef(), cs); } + int compare(const QString &sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(sub, cs); } + int compare(const char *sub, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().compare(QLatin1String(sub), cs); } + bool startsWith(const ProString &sub) const { return toQStringRef().startsWith(sub.toQStringRef()); } + bool startsWith(const QString &sub) const { return toQStringRef().startsWith(sub); } + bool startsWith(const char *sub) const { return toQStringRef().startsWith(QLatin1String(sub)); } + bool startsWith(QChar c) const { return toQStringRef().startsWith(c); } + bool endsWith(const ProString &sub) const { return toQStringRef().endsWith(sub.toQStringRef()); } + bool endsWith(const QString &sub) const { return toQStringRef().endsWith(sub); } + bool endsWith(const char *sub) const { return toQStringRef().endsWith(QLatin1String(sub)); } + bool endsWith(QChar c) const { return toQStringRef().endsWith(c); } + int indexOf(const QString &s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(s, from, cs); } + int indexOf(const char *s, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(QLatin1String(s), from, cs); } + int indexOf(QChar c, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().indexOf(c, from, cs); } + int lastIndexOf(const QString &s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(s, from, cs); } + int lastIndexOf(const char *s, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(QLatin1String(s), from, cs); } + int lastIndexOf(QChar c, int from = -1, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return toQStringRef().lastIndexOf(c, from, cs); } + bool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(s, 0, cs) >= 0; } + bool contains(const char *s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(QLatin1String(s), 0, cs) >= 0; } + bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(c, 0, cs) >= 0; } + int toInt(bool *ok = 0) const { return toQString().toInt(ok); } // XXX optimize + short toShort(bool *ok = 0) const { return toQString().toShort(ok); } // XXX optimize + + static uint hash(const QChar *p, int n); + + ALWAYS_INLINE QStringRef toQStringRef() const { return QStringRef(&m_string, m_offset, m_length); } + + ALWAYS_INLINE ProKey &toKey() { return *(ProKey *)this; } + ALWAYS_INLINE const ProKey &toKey() const { return *(const ProKey *)this; } + + QString toQString() const; + QString &toQString(QString &tmp) const; + + QByteArray toLatin1() const { return toQStringRef().toLatin1(); } + +private: + ProString(const ProKey &other); + ProString &operator=(const ProKey &other); + + enum OmitPreHashing { NoHash }; + ProString(const ProString &other, OmitPreHashing); + + enum DoPreHashing { DoHash }; + ALWAYS_INLINE ProString(const QString &str, DoPreHashing); + ALWAYS_INLINE ProString(const char *str, DoPreHashing); + ALWAYS_INLINE ProString(const QString &str, int offset, int length, DoPreHashing); + ALWAYS_INLINE ProString(const QString &str, int offset, int length, uint hash); + + QString m_string; + int m_offset, m_length; + const ProFile *m_file; + mutable uint m_hash; + QChar *prepareExtend(int extraLen, int thisTarget, int extraTarget); + uint updatedHash() const; + friend uint qHash(const ProString &str); + friend QString operator+(const ProString &one, const ProString &two); + friend class ProKey; +}; +Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE); + +class ProKey : public ProString { +public: + ALWAYS_INLINE ProKey() : ProString() {} + explicit ProKey(const QString &str); + PROITEM_EXPLICIT ProKey(const char *str); + ProKey(const QString &str, int off, int len); + ProKey(const QString &str, int off, int len, uint hash); + void setValue(const QString &str); + +#ifdef Q_CC_MSVC + // Workaround strange MSVC behaviour when exporting classes with ProKey members. + ALWAYS_INLINE ProKey(const ProKey &other) : ProString(other.toString()) {} + ALWAYS_INLINE ProKey &operator=(const ProKey &other) + { + toString() = other.toString(); + return *this; + } +#endif + + ALWAYS_INLINE ProString &toString() { return *(ProString *)this; } + ALWAYS_INLINE const ProString &toString() const { return *(const ProString *)this; } + +private: + ProKey(const ProString &other); +}; +Q_DECLARE_TYPEINFO(ProKey, Q_MOVABLE_TYPE); + +uint qHash(const ProString &str); +QString operator+(const ProString &one, const ProString &two); +inline QString operator+(const ProString &one, const QString &two) + { return one + ProString(two); } +inline QString operator+(const QString &one, const ProString &two) + { return ProString(one) + two; } + +inline QString operator+(const ProString &one, const char *two) + { return one + ProString(two); } // XXX optimize +inline QString operator+(const char *one, const ProString &two) + { return ProString(one) + two; } // XXX optimize + +inline QString &operator+=(QString &that, const ProString &other) + { return that += other.toQStringRef(); } + +inline bool operator==(const QString &that, const ProString &other) + { return other == that; } +inline bool operator!=(const QString &that, const ProString &other) + { return !(other == that); } + +QTextStream &operator<<(QTextStream &t, const ProString &str); + +class ProStringList : public QVector<ProString> { +public: + ProStringList() {} + ProStringList(const ProString &str) { *this << str; } + explicit ProStringList(const QStringList &list); + QStringList toQStringList() const; + + ProStringList &operator<<(const ProString &str) + { QVector<ProString>::operator<<(str); return *this; } + + int length() const { return size(); } + + QString join(const QString &sep) const; + QString join(QChar sep) const; + + void removeAll(const ProString &str); + void removeAll(const char *str); + void removeAt(int idx) { remove(idx); } + void removeDuplicates(); + + bool contains(const ProString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { return contains(ProString(str), cs); } + bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; +}; +Q_DECLARE_TYPEINFO(ProStringList, Q_MOVABLE_TYPE); + +inline ProStringList operator+(const ProStringList &one, const ProStringList &two) + { ProStringList ret = one; ret += two; return ret; } + +typedef QHash<ProKey, ProStringList> ProValueMap; + +// These token definitions affect both ProFileEvaluator and ProWriter +enum ProToken { + TokTerminator = 0, // end of stream (possibly not included in length; must be zero) + TokLine, // line marker: + // - line (1) + TokAssign, // variable = + TokAppend, // variable += + TokAppendUnique, // variable *= + TokRemove, // variable -= + TokReplace, // variable ~= + // previous literal/expansion is a variable manipulation + // - value expression + TokValueTerminator + TokValueTerminator, // assignment value terminator + TokLiteral, // literal string (fully dequoted) + // - length (1) + // - string data (length; unterminated) + TokHashLiteral, // literal string with hash (fully dequoted) + // - hash (2) + // - length (1) + // - string data (length; unterminated) + TokVariable, // qmake variable expansion + // - hash (2) + // - name length (1) + // - name (name length; unterminated) + TokProperty, // qmake property expansion + // - hash (2) + // - name length (1) + // - name (name length; unterminated) + TokEnvVar, // environment variable expansion + // - name length (1) + // - name (name length; unterminated) + TokFuncName, // replace function expansion + // - hash (2) + // - name length (1) + // - name (name length; unterminated) + // - ((nested expansion + TokArgSeparator)* + nested expansion)? + // - TokFuncTerminator + TokArgSeparator, // function argument separator + TokFuncTerminator, // function argument list terminator + TokCondition, // previous literal/expansion is a conditional + TokTestCall, // previous literal/expansion is a test function call + // - ((nested expansion + TokArgSeparator)* + nested expansion)? + // - TokFuncTerminator + TokReturn, // previous literal/expansion is a return value + TokBreak, // break loop + TokNext, // shortcut to next loop iteration + TokNot, // '!' operator + TokAnd, // ':' operator + TokOr, // '|' operator + TokBranch, // branch point: + // - then block length (2) + // - then block + TokTerminator (then block length) + // - else block length (2) + // - else block + TokTerminator (else block length) + TokForLoop, // for loop: + // - variable name: hash (2), length (1), chars (length) + // - expression: length (2), bytes + TokValueTerminator (length) + // - body length (2) + // - body + TokTerminator (body length) + TokTestDef, // test function definition: + TokReplaceDef, // replace function definition: + // - function name: hash (2), length (1), chars (length) + // - body length (2) + // - body + TokTerminator (body length) + TokMask = 0xff, + TokQuoted = 0x100, // The expression is quoted => join expanded stringlist + TokNewStr = 0x200 // Next stringlist element +}; + +class QMAKE_EXPORT ProFile +{ +public: + explicit ProFile(const QString &fileName); + ~ProFile(); + + QString fileName() const { return m_fileName; } + QString directoryName() const { return m_directoryName; } + const QString &items() const { return m_proitems; } + QString *itemsRef() { return &m_proitems; } + const ushort *tokPtr() const { return (const ushort *)m_proitems.constData(); } + + void ref() { m_refCount.ref(); } + void deref() { if (!m_refCount.deref()) delete this; } + + bool isOk() const { return m_ok; } + void setOk(bool ok) { m_ok = ok; } + + bool isHostBuild() const { return m_hostBuild; } + void setHostBuild(bool host_build) { m_hostBuild = host_build; } + +private: + ProItemRefCount m_refCount; + QString m_proitems; + QString m_fileName; + QString m_directoryName; + bool m_ok; + bool m_hostBuild; +}; + +class ProFunctionDef { +public: + ProFunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); } + ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); } + ~ProFunctionDef() { m_pro->deref(); } + ProFunctionDef &operator=(const ProFunctionDef &o) + { + if (this != &o) { + m_pro->deref(); + m_pro = o.m_pro; + m_pro->ref(); + m_offset = o.m_offset; + } + return *this; + } + ProFile *pro() const { return m_pro; } + const ushort *tokPtr() const { return m_pro->tokPtr() + m_offset; } +private: + ProFile *m_pro; + int m_offset; +}; + +Q_DECLARE_TYPEINFO(ProFunctionDef, Q_MOVABLE_TYPE); + +struct ProFunctionDefs { + QHash<ProKey, ProFunctionDef> testFunctions; + QHash<ProKey, ProFunctionDef> replaceFunctions; +}; + +QT_END_NAMESPACE + +#endif // PROITEMS_H diff --git a/QMakeFileReader/evaluator/qmake_global.h b/QMakeFileReader/evaluator/qmake_global.h new file mode 100644 index 0000000..1f3a5f6 --- /dev/null +++ b/QMakeFileReader/evaluator/qmake_global.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMAKE_GLOBAL_H +#define QMAKE_GLOBAL_H + +#include <qglobal.h> + +#if defined(QMAKE_AS_LIBRARY) +# if defined(QMAKE_LIBRARY) +# define QMAKE_EXPORT Q_DECL_EXPORT +# else +# define QMAKE_EXPORT Q_DECL_IMPORT +# endif +#else +# define QMAKE_EXPORT +#endif + +// Be fast even for debug builds +// MinGW GCC 4.5+ has a problem with always_inline putTok and putBlockLen +#if defined(__GNUC__) && !(defined(__MINGW32__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 5) +# define ALWAYS_INLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +# define ALWAYS_INLINE __forceinline +#else +# define ALWAYS_INLINE inline +#endif + +#endif diff --git a/QMakeFileReader/evaluator/qmakebuiltins.cpp b/QMakeFileReader/evaluator/qmakebuiltins.cpp new file mode 100644 index 0000000..124db2a --- /dev/null +++ b/QMakeFileReader/evaluator/qmakebuiltins.cpp @@ -0,0 +1,1635 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmakeevaluator.h" + +#include "qmakeevaluator_p.h" +#include "qmakeglobals.h" +#include "qmakeparser.h" +#include "ioutils.h" + +#include <qbytearray.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qlist.h> +#include <qregexp.h> +#include <qset.h> +#include <qstringlist.h> +#include <qtextstream.h> + +#ifdef Q_OS_UNIX +#include <time.h> +#include <utime.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#else +#include <windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> + +#ifdef Q_OS_WIN32 +#define QT_POPEN _popen +#define QT_PCLOSE _pclose +#else +#define QT_POPEN popen +#define QT_PCLOSE pclose +#endif + +using namespace QMakeInternal; + +QT_BEGIN_NAMESPACE + +#define fL1S(s) QString::fromLatin1(s) + +enum ExpandFunc { + E_INVALID = 0, E_MEMBER, E_FIRST, E_LAST, E_SIZE, E_CAT, E_FROMFILE, E_EVAL, E_LIST, + E_SPRINTF, E_FORMAT_NUMBER, E_JOIN, E_SPLIT, E_BASENAME, E_DIRNAME, E_SECTION, + E_FIND, E_SYSTEM, E_UNIQUE, E_REVERSE, E_QUOTE, E_ESCAPE_EXPAND, + E_UPPER, E_LOWER, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE, + E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS, + E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH, + E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE +}; + +enum TestFunc { + T_INVALID = 0, T_REQUIRES, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, + T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, + T_DEFINED, T_CONTAINS, T_INFILE, + T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_LOG, T_MESSAGE, T_WARNING, T_ERROR, T_IF, + T_MKPATH, T_WRITE_FILE, T_TOUCH, T_CACHE +}; + +void QMakeEvaluator::initFunctionStatics() +{ + static const struct { + const char * const name; + const ExpandFunc func; + } expandInits[] = { + { "member", E_MEMBER }, + { "first", E_FIRST }, + { "last", E_LAST }, + { "size", E_SIZE }, + { "cat", E_CAT }, + { "fromfile", E_FROMFILE }, + { "eval", E_EVAL }, + { "list", E_LIST }, + { "sprintf", E_SPRINTF }, + { "format_number", E_FORMAT_NUMBER }, + { "join", E_JOIN }, + { "split", E_SPLIT }, + { "basename", E_BASENAME }, + { "dirname", E_DIRNAME }, + { "section", E_SECTION }, + { "find", E_FIND }, + { "system", E_SYSTEM }, + { "unique", E_UNIQUE }, + { "reverse", E_REVERSE }, + { "quote", E_QUOTE }, + { "escape_expand", E_ESCAPE_EXPAND }, + { "upper", E_UPPER }, + { "lower", E_LOWER }, + { "re_escape", E_RE_ESCAPE }, + { "val_escape", E_VAL_ESCAPE }, + { "files", E_FILES }, + { "prompt", E_PROMPT }, + { "replace", E_REPLACE }, + { "sort_depends", E_SORT_DEPENDS }, + { "resolve_depends", E_RESOLVE_DEPENDS }, + { "enumerate_vars", E_ENUMERATE_VARS }, + { "shadowed", E_SHADOWED }, + { "absolute_path", E_ABSOLUTE_PATH }, + { "relative_path", E_RELATIVE_PATH }, + { "clean_path", E_CLEAN_PATH }, + { "system_path", E_SYSTEM_PATH }, + { "shell_path", E_SHELL_PATH }, + { "system_quote", E_SYSTEM_QUOTE }, + { "shell_quote", E_SHELL_QUOTE }, + }; + for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i) + statics.expands.insert(ProKey(expandInits[i].name), expandInits[i].func); + + static const struct { + const char * const name; + const TestFunc func; + } testInits[] = { + { "requires", T_REQUIRES }, + { "greaterThan", T_GREATERTHAN }, + { "lessThan", T_LESSTHAN }, + { "equals", T_EQUALS }, + { "isEqual", T_EQUALS }, + { "exists", T_EXISTS }, + { "export", T_EXPORT }, + { "clear", T_CLEAR }, + { "unset", T_UNSET }, + { "eval", T_EVAL }, + { "CONFIG", T_CONFIG }, + { "if", T_IF }, + { "isActiveConfig", T_CONFIG }, + { "system", T_SYSTEM }, + { "defined", T_DEFINED }, + { "contains", T_CONTAINS }, + { "infile", T_INFILE }, + { "count", T_COUNT }, + { "isEmpty", T_ISEMPTY }, + { "load", T_LOAD }, + { "include", T_INCLUDE }, + { "debug", T_DEBUG }, + { "log", T_LOG }, + { "message", T_MESSAGE }, + { "warning", T_WARNING }, + { "error", T_ERROR }, + { "mkpath", T_MKPATH }, + { "write_file", T_WRITE_FILE }, + { "touch", T_TOUCH }, + { "cache", T_CACHE }, + }; + for (unsigned i = 0; i < sizeof(testInits)/sizeof(testInits[0]); ++i) + statics.functions.insert(ProKey(testInits[i].name), testInits[i].func); +} + +static bool isTrue(const ProString &_str, QString &tmp) +{ + const QString &str = _str.toQString(tmp); + return !str.compare(statics.strtrue, Qt::CaseInsensitive) || str.toInt(); +} + +#ifdef Q_OS_WIN +static QString windowsErrorCode() +{ + wchar_t *string = 0; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&string, + 0, + NULL); + QString ret = QString::fromWCharArray(string); + LocalFree((HLOCAL)string); + return ret; +} +#endif + +static QString +quoteValue(const ProString &val) +{ + QString ret; + ret.reserve(val.size()); + const QChar *chars = val.constData(); + bool quote = val.isEmpty(); + bool escaping = false; + for (int i = 0, l = val.size(); i < l; i++) { + QChar c = chars[i]; + ushort uc = c.unicode(); + if (uc < 32) { + if (!escaping) { + escaping = true; + ret += QLatin1String("$$escape_expand("); + } + switch (uc) { + case '\r': + ret += QLatin1String("\\\\r"); + break; + case '\n': + ret += QLatin1String("\\\\n"); + break; + case '\t': + ret += QLatin1String("\\\\t"); + break; + default: + ret += QString::fromLatin1("\\\\x%1").arg(uc, 2, 16, QLatin1Char('0')); + break; + } + } else { + if (escaping) { + escaping = false; + ret += QLatin1Char(')'); + } + switch (uc) { + case '\\': + ret += QLatin1String("\\\\"); + break; + case '"': + ret += QLatin1String("\\\""); + break; + case '\'': + ret += QLatin1String("\\'"); + break; + case '$': + ret += QLatin1String("\\$"); + break; + case '#': + ret += QLatin1String("$${LITERAL_HASH}"); + break; + case 32: + quote = true; + // fallthrough + default: + ret += c; + break; + } + } + } + if (escaping) + ret += QLatin1Char(')'); + if (quote) { + ret.prepend(QLatin1Char('"')); + ret.append(QLatin1Char('"')); + } + return ret; +} + +static bool +doWriteFile(const QString &name, QIODevice::OpenMode mode, const QString &contents, QString *errStr) +{ + QByteArray bytes = contents.toLocal8Bit(); + QFile cfile(name); + if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (cfile.readAll() == bytes) + return true; + cfile.close(); + } + if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) { + *errStr = cfile.errorString(); + return false; + } + cfile.write(bytes); + cfile.close(); + if (cfile.error() != QFile::NoError) { + *errStr = cfile.errorString(); + return false; + } + return true; +} + +QMakeEvaluator::VisitReturn +QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode, + const QString &contents) +{ + QFileInfo qfi(fn); + if (!QDir::current().mkpath(qfi.path())) { + evalError(fL1S("Cannot create %1directory %2.") + .arg(ctx, QDir::toNativeSeparators(qfi.path()))); + return ReturnFalse; + } + QString errStr; + if (!doWriteFile(qfi.filePath(), mode, contents, &errStr)) { + evalError(fL1S("Cannot write %1file %2: %3.") + .arg(ctx, QDir::toNativeSeparators(qfi.filePath()), errStr)); + return ReturnFalse; + } + m_parser->discardFileFromCache(qfi.filePath()); + return ReturnTrue; +} + +#ifndef QT_BOOTSTRAPPED +void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const +{ + proc->setWorkingDirectory(currentDirectory()); +# ifdef PROEVALUATOR_SETENV + if (!m_option->environment.isEmpty()) + proc->setProcessEnvironment(m_option->environment); +# endif +# ifdef Q_OS_WIN + proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"')); + proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList()); +# else + proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command); +# endif + proc->waitForFinished(-1); +} +#endif + +QByteArray QMakeEvaluator::getCommandOutput(const QString &args) const +{ + QByteArray out; +#ifndef QT_BOOTSTRAPPED + QProcess proc; + runProcess(&proc, args); + QByteArray errout = proc.readAllStandardError(); +# ifdef PROEVALUATOR_FULL + // FIXME: Qt really should have the option to set forwarding per channel + fputs(errout.constData(), stderr); +# else + if (!errout.isEmpty()) { + if (errout.endsWith('\n')) + errout.chop(1); + m_handler->message(QMakeHandler::EvalError, QString::fromLocal8Bit(errout)); + } +# endif + out = proc.readAllStandardOutput(); +# ifdef Q_OS_WIN + // FIXME: Qt's line end conversion on sequential files should really be fixed + out.replace("\r\n", "\n"); +# endif +#else + if (FILE *proc = QT_POPEN(QString(QLatin1String("cd ") + + IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory())) + + QLatin1String(" && ") + args).toLocal8Bit().constData(), "r")) { + while (!feof(proc)) { + char buff[10 * 1024]; + int read_in = int(fread(buff, 1, sizeof(buff), proc)); + if (!read_in) + break; + out += QByteArray(buff, read_in); + } + QT_PCLOSE(proc); + } +#endif + return out; +} + +void QMakeEvaluator::populateDeps( + const ProStringList &deps, const ProString &prefix, + QHash<ProKey, QSet<ProKey> > &dependencies, ProValueMap &dependees, + ProStringList &rootSet) const +{ + foreach (const ProString &item, deps) + if (!dependencies.contains(item.toKey())) { + QSet<ProKey> &dset = dependencies[item.toKey()]; // Always create entry + ProStringList depends = values(ProKey(prefix + item + QString::fromLatin1(".depends"))); + if (depends.isEmpty()) { + rootSet << item; + } else { + foreach (const ProString &dep, depends) { + dset.insert(dep.toKey()); + dependees[dep.toKey()] << item; + } + populateDeps(depends, prefix, dependencies, dependees, rootSet); + } + } +} + +ProStringList QMakeEvaluator::evaluateBuiltinExpand( + int func_t, const ProKey &func, const ProStringList &args) +{ + ProStringList ret; + + traceMsg("calling built-in $$%s(%s)", dbgKey(func), dbgSepStrList(args)); + + switch (func_t) { + case E_BASENAME: + case E_DIRNAME: + case E_SECTION: { + bool regexp = false; + QString sep; + ProString var; + int beg = 0; + int end = -1; + if (func_t == E_SECTION) { + if (args.count() != 3 && args.count() != 4) { + evalError(fL1S("%1(var) section(var, sep, begin, end) requires" + " three or four arguments.").arg(func.toQString(m_tmp1))); + } else { + var = args[0]; + sep = args.at(1).toQString(); + beg = args.at(2).toQString(m_tmp2).toInt(); + if (args.count() == 4) + end = args.at(3).toQString(m_tmp2).toInt(); + } + } else { + if (args.count() != 1) { + evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1))); + } else { + var = args[0]; + regexp = true; + sep = QLatin1String("[\\\\/]"); + if (func_t == E_DIRNAME) + end = -2; + else + beg = -1; + } + } + if (!var.isEmpty()) { + if (regexp) { + QRegExp sepRx(sep); + foreach (const ProString &str, values(map(var))) { + const QString &rstr = str.toQString(m_tmp1).section(sepRx, beg, end); + ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr).setSource(str)); + } + } else { + foreach (const ProString &str, values(map(var))) { + const QString &rstr = str.toQString(m_tmp1).section(sep, beg, end); + ret << (rstr.isSharedWith(m_tmp1) ? str : ProString(rstr).setSource(str)); + } + } + } + break; + } + case E_SPRINTF: + if (args.count() < 1) { + evalError(fL1S("sprintf(format, ...) requires at least one argument.")); + } else { + QString tmp = args.at(0).toQString(m_tmp1); + for (int i = 1; i < args.count(); ++i) + tmp = tmp.arg(args.at(i).toQString(m_tmp2)); + // Note: this depends on split_value_list() making a deep copy + ret = split_value_list(tmp); + } + break; + case E_FORMAT_NUMBER: + if (args.count() > 2) { + evalError(fL1S("format_number(number[, options...]) requires one or two arguments.")); + } else { + int ibase = 10; + int obase = 10; + int width = 0; + bool zeropad = false; + bool leftalign = false; + enum { DefaultSign, PadSign, AlwaysSign } sign = DefaultSign; + if (args.count() >= 2) { + foreach (const ProString &opt, split_value_list(args.at(1).toQString(m_tmp2))) { + opt.toQString(m_tmp3); + if (m_tmp3.startsWith(QLatin1String("ibase="))) { + ibase = m_tmp3.mid(6).toInt(); + } else if (m_tmp3.startsWith(QLatin1String("obase="))) { + obase = m_tmp3.mid(6).toInt(); + } else if (m_tmp3.startsWith(QLatin1String("width="))) { + width = m_tmp3.mid(6).toInt(); + } else if (m_tmp3 == QLatin1String("zeropad")) { + zeropad = true; + } else if (m_tmp3 == QLatin1String("padsign")) { + sign = PadSign; + } else if (m_tmp3 == QLatin1String("alwayssign")) { + sign = AlwaysSign; + } else if (m_tmp3 == QLatin1String("leftalign")) { + leftalign = true; + } else { + evalError(fL1S("format_number(): invalid format option %1.").arg(m_tmp3)); + goto formfail; + } + } + } + args.at(0).toQString(m_tmp3); + if (m_tmp3.contains(QLatin1Char('.'))) { + evalError(fL1S("format_number(): floats are currently not supported.")); + break; + } + bool ok; + qlonglong num = m_tmp3.toLongLong(&ok, ibase); + if (!ok) { + evalError(fL1S("format_number(): malformed number %2 for base %1.") + .arg(ibase).arg(m_tmp3)); + break; + } + QString outstr; + if (num < 0) { + num = -num; + outstr = QLatin1Char('-'); + } else if (sign == AlwaysSign) { + outstr = QLatin1Char('+'); + } else if (sign == PadSign) { + outstr = QLatin1Char(' '); + } + QString numstr = QString::number(num, obase); + int space = width - outstr.length() - numstr.length(); + if (space <= 0) { + outstr += numstr; + } else if (leftalign) { + outstr += numstr + QString(space, QLatin1Char(' ')); + } else if (zeropad) { + outstr += QString(space, QLatin1Char('0')) + numstr; + } else { + outstr.prepend(QString(space, QLatin1Char(' '))); + outstr += numstr; + } + ret += ProString(outstr); + } + formfail: + break; + case E_JOIN: { + if (args.count() < 1 || args.count() > 4) { + evalError(fL1S("join(var, glue, before, after) requires one to four arguments.")); + } else { + QString glue; + ProString before, after; + if (args.count() >= 2) + glue = args.at(1).toQString(m_tmp1); + if (args.count() >= 3) + before = args[2]; + if (args.count() == 4) + after = args[3]; + const ProStringList &var = values(map(args.at(0))); + if (!var.isEmpty()) { + const ProFile *src = currentProFile(); + foreach (const ProString &v, var) + if (const ProFile *s = v.sourceFile()) { + src = s; + break; + } + ret = split_value_list(before + var.join(glue) + after, src); + } + } + break; + } + case E_SPLIT: + if (args.count() < 1 || args.count() > 2) { + evalError(fL1S("split(var, sep) requires one or two arguments.")); + } else { + const QString &sep = (args.count() == 2) ? args.at(1).toQString(m_tmp1) : statics.field_sep; + foreach (const ProString &var, values(map(args.at(0)))) + foreach (const QString &splt, var.toQString(m_tmp2).split(sep)) + ret << (splt.isSharedWith(m_tmp2) ? var : ProString(splt).setSource(var)); + } + break; + case E_MEMBER: + if (args.count() < 1 || args.count() > 3) { + evalError(fL1S("member(var, start, end) requires one to three arguments.")); + } else { + bool ok = true; + const ProStringList &var = values(map(args.at(0))); + int start = 0, end = 0; + if (args.count() >= 2) { + const QString &start_str = args.at(1).toQString(m_tmp1); + start = start_str.toInt(&ok); + if (!ok) { + if (args.count() == 2) { + int dotdot = start_str.indexOf(statics.strDotDot); + if (dotdot != -1) { + start = start_str.left(dotdot).toInt(&ok); + if (ok) + end = start_str.mid(dotdot+2).toInt(&ok); + } + } + if (!ok) + evalError(fL1S("member() argument 2 (start) '%2' invalid.") + .arg(start_str)); + } else { + end = start; + if (args.count() == 3) + end = args.at(2).toQString(m_tmp1).toInt(&ok); + if (!ok) + evalError(fL1S("member() argument 3 (end) '%2' invalid.") + .arg(args.at(2).toQString(m_tmp1))); + } + } + if (ok) { + if (start < 0) + start += var.count(); + if (end < 0) + end += var.count(); + if (start < 0 || start >= var.count() || end < 0 || end >= var.count()) { + //nothing + } else if (start < end) { + for (int i = start; i <= end && var.count() >= i; i++) + ret.append(var[i]); + } else { + for (int i = start; i >= end && var.count() >= i && i >= 0; i--) + ret += var[i]; + } + } + } + break; + case E_FIRST: + case E_LAST: + if (args.count() != 1) { + evalError(fL1S("%1(var) requires one argument.").arg(func.toQString(m_tmp1))); + } else { + const ProStringList &var = values(map(args.at(0))); + if (!var.isEmpty()) { + if (func_t == E_FIRST) + ret.append(var[0]); + else + ret.append(var.last()); + } + } + break; + case E_SIZE: + if (args.count() != 1) + evalError(fL1S("size(var) requires one argument.")); + else + ret.append(ProString(QString::number(values(map(args.at(0))).size()))); + break; + case E_CAT: + if (args.count() < 1 || args.count() > 2) { + evalError(fL1S("cat(file, singleline=true) requires one or two arguments.")); + } else { + const QString &file = args.at(0).toQString(m_tmp1); + + bool blob = false; + bool lines = false; + bool singleLine = true; + if (args.count() > 1) { + args.at(1).toQString(m_tmp2); + if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive)) + singleLine = false; + else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive)) + blob = true; + else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive)) + lines = true; + } + + QFile qfile(resolvePath(m_option->expandEnvVars(file))); + if (qfile.open(QIODevice::ReadOnly)) { + QTextStream stream(&qfile); + if (blob) { + ret += ProString(stream.readAll()); + } else { + while (!stream.atEnd()) { + if (lines) { + ret += ProString(stream.readLine()); + } else { + ret += split_value_list(stream.readLine().trimmed()); + if (!singleLine) + ret += ProString("\n"); + } + } + } + } + } + break; + case E_FROMFILE: + if (args.count() != 2) { + evalError(fL1S("fromfile(file, variable) requires two arguments.")); + } else { + ProValueMap vars; + QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); + fn.detach(); + if (evaluateFileInto(fn, &vars, LoadProOnly) == ReturnTrue) + ret = vars.value(map(args.at(1))); + } + break; + case E_EVAL: + if (args.count() != 1) { + evalError(fL1S("eval(variable) requires one argument.")); + } else { + ret += values(map(args.at(0))); + } + break; + case E_LIST: { + QString tmp; + tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", m_listCount++); + ret = ProStringList(ProString(tmp)); + ProStringList lst; + foreach (const ProString &arg, args) + lst += split_value_list(arg.toQString(m_tmp1), arg.sourceFile()); // Relies on deep copy + m_valuemapStack.top()[ret.at(0).toKey()] = lst; + break; } + case E_FIND: + if (args.count() != 2) { + evalError(fL1S("find(var, str) requires two arguments.")); + } else { + QRegExp regx(args.at(1).toQString()); + int t = 0; + foreach (const ProString &val, values(map(args.at(0)))) { + if (regx.indexIn(val.toQString(m_tmp[t])) != -1) + ret += val; + t ^= 1; + } + } + break; + case E_SYSTEM: + if (!m_skipLevel) { + if (args.count() < 1 || args.count() > 2) { + evalError(fL1S("system(execute) requires one or two arguments.")); + } else { + bool blob = false; + bool lines = false; + bool singleLine = true; + if (args.count() > 1) { + args.at(1).toQString(m_tmp2); + if (!m_tmp2.compare(QLatin1String("false"), Qt::CaseInsensitive)) + singleLine = false; + else if (!m_tmp2.compare(QLatin1String("blob"), Qt::CaseInsensitive)) + blob = true; + else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive)) + lines = true; + } + QByteArray bytes = getCommandOutput(args.at(0).toQString(m_tmp2)); + if (lines) { + QTextStream stream(bytes); + while (!stream.atEnd()) + ret += ProString(stream.readLine()); + } else { + QString output = QString::fromLocal8Bit(bytes); + if (blob) { + ret += ProString(output); + } else { + output.replace(QLatin1Char('\t'), QLatin1Char(' ')); + if (singleLine) + output.replace(QLatin1Char('\n'), QLatin1Char(' ')); + ret += split_value_list(output); + } + } + } + } + break; + case E_UNIQUE: + if (args.count() != 1) { + evalError(fL1S("unique(var) requires one argument.")); + } else { + ret = values(map(args.at(0))); + ret.removeDuplicates(); + } + break; + case E_REVERSE: + if (args.count() != 1) { + evalError(fL1S("reverse(var) requires one argument.")); + } else { + ProStringList var = values(args.at(0).toKey()); + for (int i = 0; i < var.size() / 2; i++) + qSwap(var[i], var[var.size() - i - 1]); + ret += var; + } + break; + case E_QUOTE: + ret += args; + break; + case E_ESCAPE_EXPAND: + for (int i = 0; i < args.size(); ++i) { + QString str = args.at(i).toQString(); + QChar *i_data = str.data(); + int i_len = str.length(); + for (int x = 0; x < i_len; ++x) { + if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) { + if (*(i_data+x+1) == QLatin1Char('\\')) { + ++x; + } else { + struct { + char in, out; + } mapped_quotes[] = { + { 'n', '\n' }, + { 't', '\t' }, + { 'r', '\r' }, + { 0, 0 } + }; + for (int i = 0; mapped_quotes[i].in; ++i) { + if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) { + *(i_data+x) = QLatin1Char(mapped_quotes[i].out); + if (x < i_len-2) + memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar)); + --i_len; + break; + } + } + } + } + } + ret.append(ProString(QString(i_data, i_len)).setSource(args.at(i))); + } + break; + case E_RE_ESCAPE: + for (int i = 0; i < args.size(); ++i) { + const QString &rstr = QRegExp::escape(args.at(i).toQString(m_tmp1)); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i))); + } + break; + case E_VAL_ESCAPE: + if (args.count() != 1) { + evalError(fL1S("val_escape(var) requires one argument.")); + } else { + const ProStringList &vals = values(args.at(0).toKey()); + ret.reserve(vals.size()); + foreach (const ProString &str, vals) + ret += ProString(quoteValue(str)); + } + break; + case E_UPPER: + case E_LOWER: + for (int i = 0; i < args.count(); ++i) { + QString rstr = args.at(i).toQString(m_tmp1); + rstr = (func_t == E_UPPER) ? rstr.toUpper() : rstr.toLower(); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(i) : ProString(rstr).setSource(args.at(i))); + } + break; + case E_FILES: + if (args.count() != 1 && args.count() != 2) { + evalError(fL1S("files(pattern, recursive=false) requires one or two arguments.")); + } else { + bool recursive = false; + if (args.count() == 2) + recursive = isTrue(args.at(1), m_tmp2); + QStringList dirs; + QString r = m_option->expandEnvVars(args.at(0).toQString(m_tmp1)) + .replace(QLatin1Char('\\'), QLatin1Char('/')); + QString pfx; + if (IoUtils::isRelativePath(r)) { + pfx = currentDirectory(); + if (!pfx.endsWith(QLatin1Char('/'))) + pfx += QLatin1Char('/'); + } + int slash = r.lastIndexOf(QLatin1Char('/')); + if (slash != -1) { + dirs.append(r.left(slash+1)); + r = r.mid(slash+1); + } else { + dirs.append(QString()); + } + + r.detach(); // Keep m_tmp out of QRegExp's cache + QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard); + for (int d = 0; d < dirs.count(); d++) { + QString dir = dirs[d]; + QDir qdir(pfx + dir); + for (int i = 0; i < (int)qdir.count(); ++i) { + if (qdir[i] == statics.strDot || qdir[i] == statics.strDotDot) + continue; + QString fname = dir + qdir[i]; + if (IoUtils::fileType(pfx + fname) == IoUtils::FileIsDir) { + if (recursive) + dirs.append(fname + QLatin1Char('/')); + } + if (regex.exactMatch(qdir[i])) + ret += ProString(fname).setSource(currentProFile()); + } + } + } + break; +#ifdef PROEVALUATOR_FULL + case E_PROMPT: { + if (args.count() != 1) { + evalError(fL1S("prompt(question) requires one argument.")); +// } else if (currentFileName() == QLatin1String("-")) { +// evalError(fL1S("prompt(question) cannot be used when '-o -' is used")); + } else { + QString msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp1)); + if (!msg.endsWith(QLatin1Char('?'))) + msg += QLatin1Char('?'); + fprintf(stderr, "Project PROMPT: %s ", qPrintable(msg)); + + QFile qfile; + if (qfile.open(stdin, QIODevice::ReadOnly)) { + QTextStream t(&qfile); + ret = split_value_list(t.readLine()); + } + } + break; } +#endif + case E_REPLACE: + if (args.count() != 3 ) { + evalError(fL1S("replace(var, before, after) requires three arguments.")); + } else { + const QRegExp before(args.at(1).toQString()); + const QString &after(args.at(2).toQString(m_tmp2)); + foreach (const ProString &val, values(map(args.at(0)))) { + QString rstr = val.toQString(m_tmp1); + QString copy = rstr; // Force a detach on modify + rstr.replace(before, after); + ret << (rstr.isSharedWith(m_tmp1) ? val : ProString(rstr).setSource(val)); + } + } + break; + case E_SORT_DEPENDS: + case E_RESOLVE_DEPENDS: + if (args.count() < 1 || args.count() > 2) { + evalError(fL1S("%1(var, prefix) requires one or two arguments.").arg(func.toQString(m_tmp1))); + } else { + QHash<ProKey, QSet<ProKey> > dependencies; + ProValueMap dependees; + ProStringList rootSet; + ProStringList orgList = values(args.at(0).toKey()); + populateDeps(orgList, (args.count() < 2 ? ProString() : args.at(1)), + dependencies, dependees, rootSet); + for (int i = 0; i < rootSet.size(); ++i) { + const ProString &item = rootSet.at(i); + if ((func_t == E_RESOLVE_DEPENDS) || orgList.contains(item)) + ret.prepend(item); + foreach (const ProString &dep, dependees[item.toKey()]) { + QSet<ProKey> &dset = dependencies[dep.toKey()]; + dset.remove(rootSet.at(i).toKey()); // *Don't* use 'item' - rootSet may have changed! + if (dset.isEmpty()) + rootSet << dep; + } + } + } + break; + case E_ENUMERATE_VARS: { + QSet<ProString> keys; + foreach (const ProValueMap &vmap, m_valuemapStack) + for (ProValueMap::ConstIterator it = vmap.constBegin(); it != vmap.constEnd(); ++it) + keys.insert(it.key()); + ret.reserve(keys.size()); + foreach (const ProString &key, keys) + ret << key; + break; } + case E_SHADOWED: + if (args.count() != 1) { + evalError(fL1S("shadowed(path) requires one argument.")); + } else { + QString rstr = m_option->shadowedPath(resolvePath(args.at(0).toQString(m_tmp1))); + if (rstr.isEmpty()) + break; + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + case E_ABSOLUTE_PATH: + if (args.count() > 2) { + evalError(fL1S("absolute_path(path[, base]) requires one or two arguments.")); + } else { + QString rstr = QDir::cleanPath( + QDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory()) + .absoluteFilePath(args.at(0).toQString(m_tmp1))); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + case E_RELATIVE_PATH: + if (args.count() > 2) { + evalError(fL1S("relative_path(path[, base]) requires one or two arguments.")); + } else { + QDir baseDir(args.count() > 1 ? args.at(1).toQString(m_tmp2) : currentDirectory()); + QString rstr = baseDir.relativeFilePath(baseDir.absoluteFilePath( + args.at(0).toQString(m_tmp1))); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + case E_CLEAN_PATH: + if (args.count() != 1) { + evalError(fL1S("clean_path(path) requires one argument.")); + } else { + QString rstr = QDir::cleanPath(args.at(0).toQString(m_tmp1)); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + case E_SYSTEM_PATH: + if (args.count() != 1) { + evalError(fL1S("system_path(path) requires one argument.")); + } else { + QString rstr = args.at(0).toQString(m_tmp1); +#ifdef Q_OS_WIN + rstr.replace(QLatin1Char('/'), QLatin1Char('\\')); +#else + rstr.replace(QLatin1Char('\\'), QLatin1Char('/')); +#endif + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + case E_SHELL_PATH: + if (args.count() != 1) { + evalError(fL1S("shell_path(path) requires one argument.")); + } else { + QString rstr = args.at(0).toQString(m_tmp1); + if (m_dirSep.startsWith(QLatin1Char('\\'))) + rstr.replace(QLatin1Char('/'), QLatin1Char('\\')); + else + rstr.replace(QLatin1Char('\\'), QLatin1Char('/')); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + case E_SYSTEM_QUOTE: + if (args.count() != 1) { + evalError(fL1S("system_quote(arg) requires one argument.")); + } else { + QString rstr = IoUtils::shellQuote(args.at(0).toQString(m_tmp1)); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + case E_SHELL_QUOTE: + if (args.count() != 1) { + evalError(fL1S("shell_quote(arg) requires one argument.")); + } else { + QString rstr = args.at(0).toQString(m_tmp1); + if (m_dirSep.startsWith(QLatin1Char('\\'))) + rstr = IoUtils::shellQuoteWin(rstr); + else + rstr = IoUtils::shellQuoteUnix(rstr); + ret << (rstr.isSharedWith(m_tmp1) ? args.at(0) : ProString(rstr).setSource(args.at(0))); + } + break; + default: + evalError(fL1S("Function '%1' is not implemented.").arg(func.toQString(m_tmp1))); + break; + } + + return ret; +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( + int func_t, const ProKey &function, const ProStringList &args) +{ + traceMsg("calling built-in %s(%s)", dbgKey(function), dbgSepStrList(args)); + + switch (func_t) { + case T_DEFINED: { + if (args.count() < 1 || args.count() > 2) { + evalError(fL1S("defined(function, [\"test\"|\"replace\"])" + " requires one or two arguments.")); + return ReturnFalse; + } + const ProKey &var = args.at(0).toKey(); + if (args.count() > 1) { + if (args[1] == QLatin1String("test")) { + return returnBool(m_functionDefs.testFunctions.contains(var)); + } else if (args[1] == QLatin1String("replace")) { + return returnBool(m_functionDefs.replaceFunctions.contains(var)); + } else if (args[1] == QLatin1String("var")) { + ProValueMap::Iterator it; + return returnBool(findValues(var, &it)); + } + evalError(fL1S("defined(function, type): unexpected type [%1].") + .arg(args.at(1).toQString(m_tmp1))); + return ReturnFalse; + } + return returnBool(m_functionDefs.replaceFunctions.contains(var) + || m_functionDefs.testFunctions.contains(var)); + } + case T_EXPORT: { + if (args.count() != 1) { + evalError(fL1S("export(variable) requires one argument.")); + return ReturnFalse; + } + const ProKey &var = map(args.at(0)); + for (ProValueMapStack::Iterator vmi = m_valuemapStack.end(); + --vmi != m_valuemapStack.begin(); ) { + ProValueMap::Iterator it = (*vmi).find(var); + if (it != (*vmi).end()) { + if (it->constBegin() == statics.fakeValue.constBegin()) { + // This is stupid, but qmake doesn't propagate deletions + m_valuemapStack.first()[var] = ProStringList(); + } else { + m_valuemapStack.first()[var] = *it; + } + (*vmi).erase(it); + while (--vmi != m_valuemapStack.begin()) + (*vmi).remove(var); + break; + } + } + return ReturnTrue; + } + case T_INFILE: + if (args.count() < 2 || args.count() > 3) { + evalError(fL1S("infile(file, var, [values]) requires two or three arguments.")); + } else { + ProValueMap vars; + QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); + fn.detach(); + VisitReturn ok = evaluateFileInto(fn, &vars, LoadProOnly); + if (ok != ReturnTrue) + return ok; + if (args.count() == 2) + return returnBool(vars.contains(map(args.at(1)))); + QRegExp regx; + const QString &qry = args.at(2).toQString(m_tmp1); + if (qry != QRegExp::escape(qry)) { + QString copy = qry; + copy.detach(); + regx.setPattern(copy); + } + int t = 0; + foreach (const ProString &s, vars.value(map(args.at(1)))) { + if ((!regx.isEmpty() && regx.exactMatch(s.toQString(m_tmp[t]))) || s == qry) + return ReturnTrue; + t ^= 1; + } + } + return ReturnFalse; +#ifdef PROEVALUATOR_FULL + case T_REQUIRES: + checkRequirements(args); + return ReturnFalse; // Another qmake breakage +#endif + case T_EVAL: { + VisitReturn ret = ReturnFalse; + ProFile *pro = m_parser->parsedProBlock(args.join(statics.field_sep), + m_current.pro->fileName(), m_current.line); + if (pro) { + if (m_cumulative || pro->isOk()) { + m_locationStack.push(m_current); + visitProBlock(pro, pro->tokPtr()); + ret = ReturnTrue; // This return value is not too useful, but that's qmake + m_current = m_locationStack.pop(); + } + pro->deref(); + } + return ret; + } + case T_IF: { + if (args.count() != 1) { + evalError(fL1S("if(condition) requires one argument.")); + return ReturnFalse; + } + return returnBool(evaluateConditional(args.at(0).toQString(), + m_current.pro->fileName(), m_current.line)); + } + case T_CONFIG: { + if (args.count() < 1 || args.count() > 2) { + evalError(fL1S("CONFIG(config) requires one or two arguments.")); + return ReturnFalse; + } + if (args.count() == 1) + return returnBool(isActiveConfig(args.at(0).toQString(m_tmp2))); + const QStringList &mutuals = args.at(1).toQString(m_tmp2).split(QLatin1Char('|')); + const ProStringList &configs = values(statics.strCONFIG); + + for (int i = configs.size() - 1; i >= 0; i--) { + for (int mut = 0; mut < mutuals.count(); mut++) { + if (configs[i] == mutuals[mut].trimmed()) { + return returnBool(configs[i] == args[0]); + } + } + } + return ReturnFalse; + } + case T_CONTAINS: { + if (args.count() < 2 || args.count() > 3) { + evalError(fL1S("contains(var, val) requires two or three arguments.")); + return ReturnFalse; + } + + const QString &qry = args.at(1).toQString(m_tmp1); + QRegExp regx; + if (qry != QRegExp::escape(qry)) { + QString copy = qry; + copy.detach(); + regx.setPattern(copy); + } + const ProStringList &l = values(map(args.at(0))); + if (args.count() == 2) { + int t = 0; + for (int i = 0; i < l.size(); ++i) { + const ProString &val = l[i]; + if ((!regx.isEmpty() && regx.exactMatch(val.toQString(m_tmp[t]))) || val == qry) + return ReturnTrue; + t ^= 1; + } + } else { + const QStringList &mutuals = args.at(2).toQString(m_tmp3).split(QLatin1Char('|')); + for (int i = l.size() - 1; i >= 0; i--) { + const ProString val = l[i]; + for (int mut = 0; mut < mutuals.count(); mut++) { + if (val == mutuals[mut].trimmed()) { + return returnBool((!regx.isEmpty() + && regx.exactMatch(val.toQString(m_tmp2))) + || val == qry); + } + } + } + } + return ReturnFalse; + } + case T_COUNT: { + if (args.count() != 2 && args.count() != 3) { + evalError(fL1S("count(var, count, op=\"equals\") requires two or three arguments.")); + return ReturnFalse; + } + int cnt = values(map(args.at(0))).count(); + if (args.count() == 3) { + const ProString &comp = args.at(2); + const int val = args.at(1).toQString(m_tmp1).toInt(); + if (comp == QLatin1String(">") || comp == QLatin1String("greaterThan")) { + return returnBool(cnt > val); + } else if (comp == QLatin1String(">=")) { + return returnBool(cnt >= val); + } else if (comp == QLatin1String("<") || comp == QLatin1String("lessThan")) { + return returnBool(cnt < val); + } else if (comp == QLatin1String("<=")) { + return returnBool(cnt <= val); + } else if (comp == QLatin1String("equals") || comp == QLatin1String("isEqual") + || comp == QLatin1String("=") || comp == QLatin1String("==")) { + return returnBool(cnt == val); + } else { + evalError(fL1S("Unexpected modifier to count(%2).").arg(comp.toQString(m_tmp1))); + return ReturnFalse; + } + } + return returnBool(cnt == args.at(1).toQString(m_tmp1).toInt()); + } + case T_GREATERTHAN: + case T_LESSTHAN: { + if (args.count() != 2) { + evalError(fL1S("%1(variable, value) requires two arguments.") + .arg(function.toQString(m_tmp1))); + return ReturnFalse; + } + const QString &rhs(args.at(1).toQString(m_tmp1)), + &lhs(values(map(args.at(0))).join(statics.field_sep)); + bool ok; + int rhs_int = rhs.toInt(&ok); + if (ok) { // do integer compare + int lhs_int = lhs.toInt(&ok); + if (ok) { + if (func_t == T_GREATERTHAN) + return returnBool(lhs_int > rhs_int); + return returnBool(lhs_int < rhs_int); + } + } + if (func_t == T_GREATERTHAN) + return returnBool(lhs > rhs); + return returnBool(lhs < rhs); + } + case T_EQUALS: + if (args.count() != 2) { + evalError(fL1S("%1(variable, value) requires two arguments.") + .arg(function.toQString(m_tmp1))); + return ReturnFalse; + } + return returnBool(values(map(args.at(0))).join(statics.field_sep) + == args.at(1).toQString(m_tmp1)); + case T_CLEAR: { + if (args.count() != 1) { + evalError(fL1S("%1(variable) requires one argument.") + .arg(function.toQString(m_tmp1))); + return ReturnFalse; + } + ProValueMap *hsh; + ProValueMap::Iterator it; + const ProKey &var = map(args.at(0)); + if (!(hsh = findValues(var, &it))) + return ReturnFalse; + if (hsh == &m_valuemapStack.top()) + it->clear(); + else + m_valuemapStack.top()[var].clear(); + return ReturnTrue; + } + case T_UNSET: { + if (args.count() != 1) { + evalError(fL1S("%1(variable) requires one argument.") + .arg(function.toQString(m_tmp1))); + return ReturnFalse; + } + ProValueMap *hsh; + ProValueMap::Iterator it; + const ProKey &var = map(args.at(0)); + if (!(hsh = findValues(var, &it))) + return ReturnFalse; + if (m_valuemapStack.size() == 1) + hsh->erase(it); + else if (hsh == &m_valuemapStack.top()) + *it = statics.fakeValue; + else + m_valuemapStack.top()[var] = statics.fakeValue; + return ReturnTrue; + } + case T_INCLUDE: { + if (args.count() < 1 || args.count() > 3) { + evalError(fL1S("include(file, [into, [silent]]) requires one, two or three arguments.")); + return ReturnFalse; + } + QString parseInto; + LoadFlags flags = 0; + if (args.count() >= 2) { + parseInto = args.at(1).toQString(m_tmp2); + if (args.count() >= 3 && isTrue(args.at(2), m_tmp3)) + flags = LoadSilent; + } + QString fn = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); + fn.detach(); + VisitReturn ok; + if (parseInto.isEmpty()) { + ok = evaluateFileChecked(fn, QMakeHandler::EvalIncludeFile, LoadProOnly | flags); + } else { + ProValueMap symbols; + if ((ok = evaluateFileInto(fn, &symbols, LoadAll | flags)) == ReturnTrue) { + ProValueMap newMap; + for (ProValueMap::ConstIterator + it = m_valuemapStack.top().constBegin(), + end = m_valuemapStack.top().constEnd(); + it != end; ++it) { + const QString &ky = it.key().toQString(m_tmp1); + if (!(ky.startsWith(parseInto) && + (ky.length() == parseInto.length() + || ky.at(parseInto.length()) == QLatin1Char('.')))) + newMap[it.key()] = it.value(); + } + for (ProValueMap::ConstIterator it = symbols.constBegin(); + it != symbols.constEnd(); ++it) { + const QString &ky = it.key().toQString(m_tmp1); + if (!ky.startsWith(QLatin1Char('.'))) + newMap.insert(ProKey(parseInto + QLatin1Char('.') + ky), it.value()); + } + m_valuemapStack.top() = newMap; + } + } + if (ok == ReturnFalse && (flags & LoadSilent)) + ok = ReturnTrue; + return ok; + } + case T_LOAD: { + bool ignore_error = false; + if (args.count() == 2) { + ignore_error = isTrue(args.at(1), m_tmp2); + } else if (args.count() != 1) { + evalError(fL1S("load(feature) requires one or two arguments.")); + return ReturnFalse; + } + VisitReturn ok = evaluateFeatureFile(m_option->expandEnvVars(args.at(0).toQString()), + ignore_error); + if (ok == ReturnFalse && ignore_error) + ok = ReturnTrue; + return ok; + } + case T_DEBUG: { +#ifdef PROEVALUATOR_DEBUG + if (args.count() != 2) { + evalError(fL1S("debug(level, message) requires two arguments.")); + return ReturnFalse; + } + int level = args.at(0).toInt(); + if (level <= m_debugLevel) { + const QString &msg = m_option->expandEnvVars(args.at(1).toQString(m_tmp2)); + debugMsg(level, "Project DEBUG: %s", qPrintable(msg)); + } +#endif + return ReturnTrue; + } + case T_LOG: + case T_ERROR: + case T_WARNING: + case T_MESSAGE: { + if (args.count() != 1) { + evalError(fL1S("%1(message) requires one argument.") + .arg(function.toQString(m_tmp1))); + return ReturnFalse; + } + const QString &msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp2)); + if (!m_skipLevel) { + if (func_t == T_LOG) { +#ifdef PROEVALUATOR_FULL + fputs(msg.toLatin1().constData(), stderr); +#endif + } else { + m_handler->fileMessage(fL1S("Project %1: %2") + .arg(function.toQString(m_tmp1).toUpper(), msg)); + } + } + return (func_t == T_ERROR && !m_cumulative) ? ReturnError : ReturnTrue; + } +#ifdef PROEVALUATOR_FULL + case T_SYSTEM: { + if (m_cumulative) // Anything else would be insanity + return ReturnFalse; + if (args.count() != 1) { + evalError(fL1S("system(exec) requires one argument.")); + return ReturnFalse; + } +#ifndef QT_BOOTSTRAPPED + QProcess proc; + proc.setProcessChannelMode(QProcess::ForwardedChannels); + runProcess(&proc, args.at(0).toQString(m_tmp2)); + return returnBool(proc.exitStatus() == QProcess::NormalExit && proc.exitCode() == 0); +#else + return returnBool(system((QLatin1String("cd ") + + IoUtils::shellQuote(QDir::toNativeSeparators(currentDirectory())) + + QLatin1String(" && ") + args.at(0)).toLocal8Bit().constData()) == 0); +#endif + } +#endif + case T_ISEMPTY: { + if (args.count() != 1) { + evalError(fL1S("isEmpty(var) requires one argument.")); + return ReturnFalse; + } + return returnBool(values(map(args.at(0))).isEmpty()); + } + case T_EXISTS: { + if (args.count() != 1) { + evalError(fL1S("exists(file) requires one argument.")); + return ReturnFalse; + } + const QString &file = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); + + if (IoUtils::exists(file)) { + return ReturnTrue; + } + int slsh = file.lastIndexOf(QLatin1Char('/')); + QString fn = file.mid(slsh+1); + if (fn.contains(QLatin1Char('*')) || fn.contains(QLatin1Char('?'))) { + QString dirstr = file.left(slsh+1); + if (!QDir(dirstr).entryList(QStringList(fn)).isEmpty()) + return ReturnTrue; + } + + return ReturnFalse; + } +#ifdef PROEVALUATOR_FULL + case T_MKPATH: { + if (args.count() != 1) { + evalError(fL1S("mkpath(file) requires one argument.")); + return ReturnFalse; + } + const QString &fn = resolvePath(args.at(0).toQString(m_tmp1)); + if (!QDir::current().mkpath(fn)) { + evalError(fL1S("Cannot create directory %1.").arg(QDir::toNativeSeparators(fn))); + return ReturnFalse; + } + return ReturnTrue; + } + case T_WRITE_FILE: { + if (args.count() > 3) { + evalError(fL1S("write_file(name, [content var, [append]]) requires one to three arguments.")); + return ReturnFalse; + } + QIODevice::OpenMode mode = QIODevice::Truncate; + QString contents; + if (args.count() >= 2) { + const ProStringList &vals = values(args.at(1).toKey()); + if (!vals.isEmpty()) + contents = vals.join(fL1S("\n")) + QLatin1Char('\n'); + if (args.count() >= 3) + if (!args.at(2).toQString(m_tmp1).compare(fL1S("append"), Qt::CaseInsensitive)) + mode = QIODevice::Append; + } + return writeFile(QString(), resolvePath(args.at(0).toQString(m_tmp1)), mode, contents); + } + case T_TOUCH: { + if (args.count() != 2) { + evalError(fL1S("touch(file, reffile) requires two arguments.")); + return ReturnFalse; + } + const QString &tfn = resolvePath(args.at(0).toQString(m_tmp1)); + const QString &rfn = resolvePath(args.at(1).toQString(m_tmp2)); +#ifdef Q_OS_UNIX + struct stat st; + if (stat(rfn.toLocal8Bit().constData(), &st)) { + evalError(fL1S("Cannot stat() reference file %1: %2.").arg(rfn, fL1S(strerror(errno)))); + return ReturnFalse; + } + struct utimbuf utb; + utb.actime = time(0); + utb.modtime = st.st_mtime; + if (utime(tfn.toLocal8Bit().constData(), &utb)) { + evalError(fL1S("Cannot touch %1: %2.").arg(tfn, fL1S(strerror(errno)))); + return ReturnFalse; + } +#else + HANDLE rHand = CreateFile((wchar_t*)rfn.utf16(), + GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (rHand == INVALID_HANDLE_VALUE) { + evalError(fL1S("Cannot open() reference file %1: %2.").arg(rfn, windowsErrorCode())); + return ReturnFalse; + } + FILETIME ft; + GetFileTime(rHand, 0, 0, &ft); + CloseHandle(rHand); + HANDLE wHand = CreateFile((wchar_t*)tfn.utf16(), + GENERIC_WRITE, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (wHand == INVALID_HANDLE_VALUE) { + evalError(fL1S("Cannot open() %1: %2.").arg(tfn, windowsErrorCode())); + return ReturnFalse; + } + SetFileTime(wHand, 0, 0, &ft); + CloseHandle(wHand); +#endif + return ReturnTrue; + } + case T_CACHE: { + if (args.count() > 3) { + evalError(fL1S("cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments.")); + return ReturnFalse; + } + bool persist = true; + bool super = false; + enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet; + ProKey srcvar; + if (args.count() >= 2) { + foreach (const ProString &opt, split_value_list(args.at(1).toQString(m_tmp2))) { + opt.toQString(m_tmp3); + if (m_tmp3 == QLatin1String("transient")) { + persist = false; + } else if (m_tmp3 == QLatin1String("super")) { + super = true; + } else if (m_tmp3 == QLatin1String("set")) { + mode = CacheSet; + } else if (m_tmp3 == QLatin1String("add")) { + mode = CacheAdd; + } else if (m_tmp3 == QLatin1String("sub")) { + mode = CacheSub; + } else { + evalError(fL1S("cache(): invalid flag %1.").arg(m_tmp3)); + return ReturnFalse; + } + } + if (args.count() >= 3) { + srcvar = args.at(2).toKey(); + } else if (mode != CacheSet) { + evalError(fL1S("cache(): modes other than 'set' require a source variable.")); + return ReturnFalse; + } + } + QString varstr; + ProKey dstvar = args.at(0).toKey(); + if (!dstvar.isEmpty()) { + if (srcvar.isEmpty()) + srcvar = dstvar; + ProValueMap::Iterator srcvarIt; + if (!findValues(srcvar, &srcvarIt)) { + evalError(fL1S("Variable %1 is not defined.").arg(srcvar.toQString(m_tmp1))); + return ReturnFalse; + } + // The caches for the host and target may differ (e.g., when we are manipulating + // CONFIG), so we cannot compute a common new value for both. + const ProStringList &diffval = *srcvarIt; + ProStringList newval; + bool changed = false; + for (bool hostBuild = false; ; hostBuild = true) { + if (QMakeBaseEnv *baseEnv = m_option->baseEnvs.value( + QMakeBaseKey(m_buildRoot, hostBuild))) { + QMakeEvaluator *baseEval = baseEnv->evaluator; + const ProStringList &oldval = baseEval->values(dstvar); + if (mode == CacheSet) { + newval = diffval; + } else { + newval = oldval; + if (mode == CacheAdd) + newval += diffval; + else + removeEach(&newval, diffval); + } + if (oldval != newval) { + baseEval->valuesRef(dstvar) = newval; + if (super) { + do { + if (dstvar == QLatin1String("QMAKEPATH")) { + baseEval->m_qmakepath = newval.toQStringList(); + baseEval->updateMkspecPaths(); + } else if (dstvar == QLatin1String("QMAKEFEATURES")) { + baseEval->m_qmakefeatures = newval.toQStringList(); + } else { + break; + } + baseEval->updateFeaturePaths(); + if (hostBuild == m_hostBuild) + m_featureRoots = baseEval->m_featureRoots; + } while (false); + } + changed = true; + } + } + if (hostBuild) + break; + } + // We assume that whatever got the cached value to be what it is now will do so + // the next time as well, so we just skip the persisting if nothing changed. + if (!persist || !changed) + return ReturnTrue; + varstr = dstvar.toQString(); + if (mode == CacheAdd) + varstr += QLatin1String(" +="); + else if (mode == CacheSub) + varstr += QLatin1String(" -="); + else + varstr += QLatin1String(" ="); + if (diffval.count() == 1) { + varstr += QLatin1Char(' '); + varstr += quoteValue(diffval.at(0)); + } else if (!diffval.isEmpty()) { + foreach (const ProString &vval, diffval) { + varstr += QLatin1String(" \\\n "); + varstr += quoteValue(vval); + } + } + varstr += QLatin1Char('\n'); + } + QString fn; + if (super) { + if (m_superfile.isEmpty()) { + m_superfile = m_outputDir + QLatin1String("/.qmake.super"); + printf("Info: creating super cache file %s\n", qPrintable(m_superfile)); + valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile); + } + fn = m_superfile; + } else { + if (m_cachefile.isEmpty()) { + m_cachefile = m_outputDir + QLatin1String("/.qmake.cache"); + printf("Info: creating cache file %s\n", qPrintable(m_cachefile)); + valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile); + // We could update m_{source,build}Root and m_featureRoots here, or even + // "re-home" our rootEnv, but this doesn't sound too useful - if somebody + // wanted qmake to find something in the build directory, he could have + // done so "from the outside". + // The sub-projects will find the new cache all by themselves. + } + fn = m_cachefile; + } + return writeFile(fL1S("cache "), fn, QIODevice::Append, varstr); + } +#endif + default: + evalError(fL1S("Function '%1' is not implemented.").arg(function.toQString(m_tmp1))); + return ReturnFalse; + } +} + +QT_END_NAMESPACE diff --git a/QMakeFileReader/evaluator/qmakeevaluator.cpp b/QMakeFileReader/evaluator/qmakeevaluator.cpp new file mode 100644 index 0000000..ebf82bc --- /dev/null +++ b/QMakeFileReader/evaluator/qmakeevaluator.cpp @@ -0,0 +1,2027 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmakeevaluator.h" +#include "qmakeevaluator_p.h" + +#include "qmakeglobals.h" +#include "qmakeparser.h" +#include "ioutils.h" + +#include <qbytearray.h> +#include <qdatetime.h> +#include <qdebug.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qlist.h> +#include <qregexp.h> +#include <qset.h> +#include <qstack.h> +#include <qstring.h> +#include <qstringlist.h> +#ifdef PROEVALUATOR_THREAD_SAFE +# include <qthreadpool.h> +#endif + +#ifdef Q_OS_UNIX +#include <unistd.h> +#include <sys/utsname.h> +#else +#include <windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> + +using namespace QMakeInternal; + +QT_BEGIN_NAMESPACE + +#define fL1S(s) QString::fromLatin1(s) + + +QMakeBaseKey::QMakeBaseKey(const QString &_root, bool _hostBuild) + : root(_root), hostBuild(_hostBuild) +{ +} + +uint qHash(const QMakeBaseKey &key) +{ + return qHash(key.root) ^ (uint)key.hostBuild; +} + +bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two) +{ + return one.root == two.root && one.hostBuild == two.hostBuild; +} + +QMakeBaseEnv::QMakeBaseEnv() + : evaluator(0) +{ +#ifdef PROEVALUATOR_THREAD_SAFE + inProgress = false; + isOk = false; +#endif +} + +QMakeBaseEnv::~QMakeBaseEnv() +{ + delete evaluator; +} + +namespace QMakeInternal { +QMakeStatics statics; +} + +void QMakeEvaluator::initStatics() +{ + if (!statics.field_sep.isNull()) + return; + + statics.field_sep = QLatin1String(" "); + statics.strtrue = QLatin1String("true"); + statics.strfalse = QLatin1String("false"); + statics.strCONFIG = ProKey("CONFIG"); + statics.strARGS = ProKey("ARGS"); + statics.strDot = QLatin1String("."); + statics.strDotDot = QLatin1String(".."); + statics.strever = QLatin1String("ever"); + statics.strforever = QLatin1String("forever"); + statics.strhost_build = QLatin1String("host_build"); + statics.strTEMPLATE = ProKey("TEMPLATE"); +#ifdef PROEVALUATOR_FULL + statics.strREQUIRES = ProKey("REQUIRES"); +#endif + + statics.fakeValue = ProStringList(ProString("_FAKE_")); // It has to have a unique begin() value + + initFunctionStatics(); + + static const struct { + const char * const oldname, * const newname; + } mapInits[] = { + { "INTERFACES", "FORMS" }, + { "QMAKE_POST_BUILD", "QMAKE_POST_LINK" }, + { "TARGETDEPS", "POST_TARGETDEPS" }, + { "LIBPATH", "QMAKE_LIBDIR" }, + { "QMAKE_EXT_MOC", "QMAKE_EXT_CPP_MOC" }, + { "QMAKE_MOD_MOC", "QMAKE_H_MOD_MOC" }, + { "QMAKE_LFLAGS_SHAPP", "QMAKE_LFLAGS_APP" }, + { "PRECOMPH", "PRECOMPILED_HEADER" }, + { "PRECOMPCPP", "PRECOMPILED_SOURCE" }, + { "INCPATH", "INCLUDEPATH" }, + { "QMAKE_EXTRA_WIN_COMPILERS", "QMAKE_EXTRA_COMPILERS" }, + { "QMAKE_EXTRA_UNIX_COMPILERS", "QMAKE_EXTRA_COMPILERS" }, + { "QMAKE_EXTRA_WIN_TARGETS", "QMAKE_EXTRA_TARGETS" }, + { "QMAKE_EXTRA_UNIX_TARGETS", "QMAKE_EXTRA_TARGETS" }, + { "QMAKE_EXTRA_UNIX_INCLUDES", "QMAKE_EXTRA_INCLUDES" }, + { "QMAKE_EXTRA_UNIX_VARIABLES", "QMAKE_EXTRA_VARIABLES" }, + { "QMAKE_RPATH", "QMAKE_LFLAGS_RPATH" }, + { "QMAKE_FRAMEWORKDIR", "QMAKE_FRAMEWORKPATH" }, + { "QMAKE_FRAMEWORKDIR_FLAGS", "QMAKE_FRAMEWORKPATH_FLAGS" }, + { "IN_PWD", "PWD" } + }; + for (unsigned i = 0; i < sizeof(mapInits)/sizeof(mapInits[0]); ++i) + statics.varMap.insert(ProKey(mapInits[i].oldname), ProKey(mapInits[i].newname)); +} + +const ProKey &QMakeEvaluator::map(const ProKey &var) +{ + QHash<ProKey, ProKey>::ConstIterator it = statics.varMap.constFind(var); + if (it == statics.varMap.constEnd()) + return var; + deprecationWarning(fL1S("Variable %1 is deprecated; use %2 instead.") + .arg(var.toQString(), it.value().toQString())); + return it.value(); +} + + +QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, + QMakeParser *parser, QMakeHandler *handler) + : +#ifdef PROEVALUATOR_DEBUG + m_debugLevel(option->debugLevel), +#endif + m_option(option), m_parser(parser), m_handler(handler) +{ + // So that single-threaded apps don't have to call initialize() for now. + initStatics(); + + // Configuration, more or less + m_caller = 0; +#ifdef PROEVALUATOR_CUMULATIVE + m_cumulative = false; +#endif + m_hostBuild = false; + + // Evaluator state +#ifdef PROEVALUATOR_CUMULATIVE + m_skipLevel = 0; +#endif + m_listCount = 0; + m_valuemapStack.push(ProValueMap()); + m_valuemapInited = false; +} + +QMakeEvaluator::~QMakeEvaluator() +{ +} + +void QMakeEvaluator::initFrom(const QMakeEvaluator &other) +{ + Q_ASSERT_X(&other, "QMakeEvaluator::visitProFile", "Project not prepared"); + m_functionDefs = other.m_functionDefs; + m_valuemapStack = other.m_valuemapStack; + m_valuemapInited = true; + m_qmakespec = other.m_qmakespec; + m_qmakespecName = other.m_qmakespecName; + m_mkspecPaths = other.m_mkspecPaths; + m_featureRoots = other.m_featureRoots; + m_dirSep = other.m_dirSep; +} + +//////// Evaluator tools ///////// + +uint QMakeEvaluator::getBlockLen(const ushort *&tokPtr) +{ + uint len = *tokPtr++; + len |= (uint)*tokPtr++ << 16; + return len; +} + +ProString QMakeEvaluator::getStr(const ushort *&tokPtr) +{ + uint len = *tokPtr++; + ProString ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len); + ret.setSource(m_current.pro); + tokPtr += len; + return ret; +} + +ProKey QMakeEvaluator::getHashStr(const ushort *&tokPtr) +{ + uint hash = getBlockLen(tokPtr); + uint len = *tokPtr++; + ProKey ret(m_current.pro->items(), tokPtr - m_current.pro->tokPtr(), len, hash); + tokPtr += len; + return ret; +} + +void QMakeEvaluator::skipStr(const ushort *&tokPtr) +{ + uint len = *tokPtr++; + tokPtr += len; +} + +void QMakeEvaluator::skipHashStr(const ushort *&tokPtr) +{ + tokPtr += 2; + uint len = *tokPtr++; + tokPtr += len; +} + +// FIXME: this should not build new strings for direct sections. +// Note that the E_SPRINTF and E_LIST implementations rely on the deep copy. +ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFile *source) +{ + QString build; + ProStringList ret; + QStack<char> quote; + + const ushort SPACE = ' '; + const ushort LPAREN = '('; + const ushort RPAREN = ')'; + const ushort SINGLEQUOTE = '\''; + const ushort DOUBLEQUOTE = '"'; + const ushort BACKSLASH = '\\'; + + if (!source) + source = currentProFile(); + + ushort unicode; + const QChar *vals_data = vals.data(); + const int vals_len = vals.length(); + int parens = 0; + for (int x = 0; x < vals_len; x++) { + unicode = vals_data[x].unicode(); + if (x != (int)vals_len-1 && unicode == BACKSLASH && + (vals_data[x+1].unicode() == SINGLEQUOTE || vals_data[x+1].unicode() == DOUBLEQUOTE)) { + build += vals_data[x++]; //get that 'escape' + } else if (!quote.isEmpty() && unicode == quote.top()) { + quote.pop(); + } else if (unicode == SINGLEQUOTE || unicode == DOUBLEQUOTE) { + quote.push(unicode); + } else if (unicode == RPAREN) { + --parens; + } else if (unicode == LPAREN) { + ++parens; + } + + if (!parens && quote.isEmpty() && vals_data[x] == SPACE) { + ret << ProString(build).setSource(source); + build.clear(); + } else { + build += vals_data[x]; + } + } + if (!build.isEmpty()) + ret << ProString(build).setSource(source); + if (parens) + deprecationWarning(fL1S("Unmatched parentheses are deprecated.")); + return ret; +} + +static void zipEmpty(ProStringList *value) +{ + for (int i = value->size(); --i >= 0;) + if (value->at(i).isEmpty()) + value->remove(i); +} + +static void insertUnique(ProStringList *varlist, const ProStringList &value) +{ + foreach (const ProString &str, value) + if (!str.isEmpty() && !varlist->contains(str)) + varlist->append(str); +} + +static void removeAll(ProStringList *varlist, const ProString &value) +{ + for (int i = varlist->size(); --i >= 0; ) + if (varlist->at(i) == value) + varlist->remove(i); +} + +void QMakeEvaluator::removeEach(ProStringList *varlist, const ProStringList &value) +{ + foreach (const ProString &str, value) + if (!str.isEmpty()) + removeAll(varlist, str); +} + +static void replaceInList(ProStringList *varlist, + const QRegExp ®exp, const QString &replace, bool global, QString &tmp) +{ + for (ProStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) { + QString val = varit->toQString(tmp); + QString copy = val; // Force detach and have a reference value + val.replace(regexp, replace); + if (!val.isSharedWith(copy) && val != copy) { + if (val.isEmpty()) { + varit = varlist->erase(varit); + } else { + (*varit).setValue(val); + ++varit; + } + if (!global) + break; + } else { + ++varit; + } + } +} + +//////// Evaluator ///////// + +static ALWAYS_INLINE void addStr( + const ProString &str, ProStringList *ret, bool &pending, bool joined) +{ + if (joined) { + ret->last().append(str, &pending); + } else { + if (!pending) { + pending = true; + *ret << str; + } else { + ret->last().append(str); + } + } +} + +static ALWAYS_INLINE void addStrList( + const ProStringList &list, ushort tok, ProStringList *ret, bool &pending, bool joined) +{ + if (!list.isEmpty()) { + if (joined) { + ret->last().append(list, &pending, !(tok & TokQuoted)); + } else { + if (tok & TokQuoted) { + if (!pending) { + pending = true; + *ret << ProString(); + } + ret->last().append(list); + } else { + if (!pending) { + // Another qmake bizzarity: if nothing is pending and the + // first element is empty, it will be eaten + if (!list.at(0).isEmpty()) { + // The common case + pending = true; + *ret += list; + return; + } + } else { + ret->last().append(list.at(0)); + } + // This is somewhat slow, but a corner case + for (int j = 1; j < list.size(); ++j) { + pending = true; + *ret << list.at(j); + } + } + } + } +} + +void QMakeEvaluator::evaluateExpression( + const ushort *&tokPtr, ProStringList *ret, bool joined) +{ + debugMsg(2, joined ? "evaluating joined expression" : "evaluating expression"); + if (joined) + *ret << ProString(); + bool pending = false; + forever { + ushort tok = *tokPtr++; + if (tok & TokNewStr) { + debugMsg(2, "new string"); + pending = false; + } + ushort maskedTok = tok & TokMask; + switch (maskedTok) { + case TokLine: + m_current.line = *tokPtr++; + break; + case TokLiteral: { + const ProString &val = getStr(tokPtr); + debugMsg(2, "literal %s", dbgStr(val)); + addStr(val, ret, pending, joined); + break; } + case TokHashLiteral: { + const ProKey &val = getHashStr(tokPtr); + debugMsg(2, "hashed literal %s", dbgStr(val.toString())); + addStr(val, ret, pending, joined); + break; } + case TokVariable: { + const ProKey &var = getHashStr(tokPtr); + const ProStringList &val = values(map(var)); + debugMsg(2, "variable %s => %s", dbgKey(var), dbgStrList(val)); + addStrList(val, tok, ret, pending, joined); + break; } + case TokProperty: { + const ProKey &var = getHashStr(tokPtr); + const ProString &val = propertyValue(var); + debugMsg(2, "property %s => %s", dbgKey(var), dbgStr(val)); + addStr(val, ret, pending, joined); + break; } + case TokEnvVar: { + const ProString &var = getStr(tokPtr); + const ProStringList &val = split_value_list(m_option->getEnv(var.toQString(m_tmp1))); + debugMsg(2, "env var %s => %s", dbgStr(var), dbgStrList(val)); + addStrList(val, tok, ret, pending, joined); + break; } + case TokFuncName: { + const ProKey &func = getHashStr(tokPtr); + debugMsg(2, "function %s", dbgKey(func)); + addStrList(evaluateExpandFunction(func, tokPtr), tok, ret, pending, joined); + break; } + default: + debugMsg(2, "evaluated expression => %s", dbgStrList(*ret)); + tokPtr--; + return; + } + } +} + +void QMakeEvaluator::skipExpression(const ushort *&pTokPtr) +{ + const ushort *tokPtr = pTokPtr; + forever { + ushort tok = *tokPtr++; + switch (tok) { + case TokLine: + m_current.line = *tokPtr++; + break; + case TokValueTerminator: + case TokFuncTerminator: + pTokPtr = tokPtr; + return; + case TokArgSeparator: + break; + default: + switch (tok & TokMask) { + case TokLiteral: + case TokEnvVar: + skipStr(tokPtr); + break; + case TokHashLiteral: + case TokVariable: + case TokProperty: + skipHashStr(tokPtr); + break; + case TokFuncName: + skipHashStr(tokPtr); + pTokPtr = tokPtr; + skipExpression(pTokPtr); + tokPtr = pTokPtr; + break; + default: + Q_ASSERT_X(false, "skipExpression", "Unrecognized token"); + break; + } + } + } +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( + ProFile *pro, const ushort *tokPtr) +{ + m_current.pro = pro; + m_current.line = 0; + return visitProBlock(tokPtr); +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock( + const ushort *tokPtr) +{ + traceMsg("entering block"); + ProStringList curr; + bool okey = true, or_op = false, invert = false; + uint blockLen; + while (ushort tok = *tokPtr++) { + VisitReturn ret; + switch (tok) { + case TokLine: + m_current.line = *tokPtr++; + continue; + case TokAssign: + case TokAppend: + case TokAppendUnique: + case TokRemove: + case TokReplace: + visitProVariable(tok, curr, tokPtr); + curr.clear(); + continue; + case TokBranch: + blockLen = getBlockLen(tokPtr); + if (m_cumulative) { +#ifdef PROEVALUATOR_CUMULATIVE + if (!okey) + m_skipLevel++; + ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue; + tokPtr += blockLen; + blockLen = getBlockLen(tokPtr); + if (!okey) + m_skipLevel--; + else + m_skipLevel++; + if ((ret == ReturnTrue || ret == ReturnFalse) && blockLen) + ret = visitProBlock(tokPtr); + if (okey) + m_skipLevel--; +#endif + } else { + if (okey) { + traceMsg("taking 'then' branch"); + ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue; + traceMsg("finished 'then' branch"); + } + tokPtr += blockLen; + blockLen = getBlockLen(tokPtr); + if (!okey) { + traceMsg("taking 'else' branch"); + ret = blockLen ? visitProBlock(tokPtr) : ReturnTrue; + traceMsg("finished 'else' branch"); + } + } + tokPtr += blockLen; + okey = true, or_op = false; // force next evaluation + break; + case TokForLoop: + if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop + skipHashStr(tokPtr); + uint exprLen = getBlockLen(tokPtr); + tokPtr += exprLen; + blockLen = getBlockLen(tokPtr); + ret = visitProBlock(tokPtr); + } else if (okey != or_op) { + const ProKey &variable = getHashStr(tokPtr); + uint exprLen = getBlockLen(tokPtr); + const ushort *exprPtr = tokPtr; + tokPtr += exprLen; + blockLen = getBlockLen(tokPtr); + ret = visitProLoop(variable, exprPtr, tokPtr); + } else { + skipHashStr(tokPtr); + uint exprLen = getBlockLen(tokPtr); + tokPtr += exprLen; + blockLen = getBlockLen(tokPtr); + traceMsg("skipped loop"); + ret = ReturnTrue; + } + tokPtr += blockLen; + okey = true, or_op = false; // force next evaluation + break; + case TokTestDef: + case TokReplaceDef: + if (m_cumulative || okey != or_op) { + const ProKey &name = getHashStr(tokPtr); + blockLen = getBlockLen(tokPtr); + visitProFunctionDef(tok, name, tokPtr); + traceMsg("defined %s function %s", + tok == TokTestDef ? "test" : "replace", dbgKey(name)); + } else { + traceMsg("skipped function definition"); + skipHashStr(tokPtr); + blockLen = getBlockLen(tokPtr); + } + tokPtr += blockLen; + okey = true, or_op = false; // force next evaluation + continue; + case TokNot: + traceMsg("NOT"); + invert ^= true; + continue; + case TokAnd: + traceMsg("AND"); + or_op = false; + continue; + case TokOr: + traceMsg("OR"); + or_op = true; + continue; + case TokCondition: + if (!m_skipLevel && okey != or_op) { + if (curr.size() != 1) { + if (!m_cumulative || !curr.isEmpty()) + evalError(fL1S("Conditional must expand to exactly one word.")); + okey = false; + } else { + okey = isActiveConfig(curr.at(0).toQString(m_tmp2), true); + traceMsg("condition %s is %s", dbgStr(curr.at(0)), dbgBool(okey)); + okey ^= invert; + } + } else { + traceMsg("skipped condition %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>"); + } + or_op = !okey; // tentatively force next evaluation + invert = false; + curr.clear(); + continue; + case TokTestCall: + if (!m_skipLevel && okey != or_op) { + if (curr.size() != 1) { + if (!m_cumulative || !curr.isEmpty()) + evalError(fL1S("Test name must expand to exactly one word.")); + skipExpression(tokPtr); + okey = false; + } else { + traceMsg("evaluating test function %s", dbgStr(curr.at(0))); + ret = evaluateConditionalFunction(curr.at(0).toKey(), tokPtr); + switch (ret) { + case ReturnTrue: okey = true; break; + case ReturnFalse: okey = false; break; + default: + traceMsg("aborting block, function status: %s", dbgReturn(ret)); + return ret; + } + traceMsg("test function returned %s", dbgBool(okey)); + okey ^= invert; + } + } else if (m_cumulative) { +#ifdef PROEVALUATOR_CUMULATIVE + m_skipLevel++; + if (curr.size() != 1) + skipExpression(tokPtr); + else + evaluateConditionalFunction(curr.at(0).toKey(), tokPtr); + m_skipLevel--; +#endif + } else { + skipExpression(tokPtr); + traceMsg("skipped test function %s", curr.size() == 1 ? dbgStr(curr.at(0)) : "<invalid>"); + } + or_op = !okey; // tentatively force next evaluation + invert = false; + curr.clear(); + continue; + case TokReturn: + m_returnValue = curr; + curr.clear(); + ret = ReturnReturn; + goto ctrlstm; + case TokBreak: + ret = ReturnBreak; + goto ctrlstm; + case TokNext: + ret = ReturnNext; + ctrlstm: + if (!m_skipLevel && okey != or_op) { + traceMsg("flow control statement '%s', aborting block", dbgReturn(ret)); + return ret; + } + traceMsg("skipped flow control statement '%s'", dbgReturn(ret)); + okey = false, or_op = true; // force next evaluation + continue; + default: { + const ushort *oTokPtr = --tokPtr; + evaluateExpression(tokPtr, &curr, false); + if (tokPtr != oTokPtr) + continue; + } + Q_ASSERT_X(false, "visitProBlock", "unexpected item type"); + continue; + } + if (ret != ReturnTrue && ret != ReturnFalse) { + traceMsg("aborting block, status: %s", dbgReturn(ret)); + return ret; + } + } + traceMsg("leaving block, okey=%s", dbgBool(okey)); + return returnBool(okey); +} + + +void QMakeEvaluator::visitProFunctionDef( + ushort tok, const ProKey &name, const ushort *tokPtr) +{ + QHash<ProKey, ProFunctionDef> *hash = + (tok == TokTestDef + ? &m_functionDefs.testFunctions + : &m_functionDefs.replaceFunctions); + hash->insert(name, ProFunctionDef(m_current.pro, tokPtr - m_current.pro->tokPtr())); +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop( + const ProKey &_variable, const ushort *exprPtr, const ushort *tokPtr) +{ + VisitReturn ret = ReturnTrue; + bool infinite = false; + int index = 0; + ProKey variable; + ProStringList oldVarVal; + ProString it_list = expandVariableReferences(exprPtr, 0, true).at(0); + if (_variable.isEmpty()) { + if (it_list != statics.strever) { + evalError(fL1S("Invalid loop expression.")); + return ReturnFalse; + } + it_list = ProString(statics.strforever); + } else { + variable = map(_variable); + oldVarVal = values(variable); + } + ProStringList list = values(it_list.toKey()); + if (list.isEmpty()) { + if (it_list == statics.strforever) { + infinite = true; + } else { + const QString &itl = it_list.toQString(m_tmp1); + int dotdot = itl.indexOf(statics.strDotDot); + if (dotdot != -1) { + bool ok; + int start = itl.left(dotdot).toInt(&ok); + if (ok) { + int end = itl.mid(dotdot+2).toInt(&ok); + if (ok) { + if (start < end) { + for (int i = start; i <= end; i++) + list << ProString(QString::number(i)); + } else { + for (int i = start; i >= end; i--) + list << ProString(QString::number(i)); + } + } + } + } + } + } + + if (infinite) + traceMsg("entering infinite loop for %s", dbgKey(variable)); + else + traceMsg("entering loop for %s over %s", dbgKey(variable), dbgStrList(list)); + + forever { + if (infinite) { + if (!variable.isEmpty()) + m_valuemapStack.top()[variable] = ProStringList(ProString(QString::number(index++))); + if (index > 1000) { + evalError(fL1S("Ran into infinite loop (> 1000 iterations).")); + break; + } + traceMsg("loop iteration %d", index); + } else { + ProString val; + do { + if (index >= list.count()) + goto do_break; + val = list.at(index++); + } while (val.isEmpty()); // stupid, but qmake is like that + traceMsg("loop iteration %s", dbgStr(val)); + m_valuemapStack.top()[variable] = ProStringList(val); + } + + ret = visitProBlock(tokPtr); + switch (ret) { + case ReturnTrue: + case ReturnFalse: + break; + case ReturnNext: + ret = ReturnTrue; + break; + case ReturnBreak: + ret = ReturnTrue; + goto do_break; + default: + goto do_break; + } + } + do_break: + + traceMsg("done looping"); + + if (!variable.isEmpty()) + m_valuemapStack.top()[variable] = oldVarVal; + return ret; +} + +void QMakeEvaluator::visitProVariable( + ushort tok, const ProStringList &curr, const ushort *&tokPtr) +{ + int sizeHint = *tokPtr++; + + if (curr.size() != 1) { + skipExpression(tokPtr); + if (!m_cumulative || !curr.isEmpty()) + evalError(fL1S("Left hand side of assignment must expand to exactly one word.")); + return; + } + const ProKey &varName = map(curr.first()); + + if (tok == TokReplace) { // ~= + // DEFINES ~= s/a/b/?[gqi] + + const ProStringList &varVal = expandVariableReferences(tokPtr, sizeHint, true); + const QString &val = varVal.at(0).toQString(m_tmp1); + if (val.length() < 4 || val.at(0) != QLatin1Char('s')) { + evalError(fL1S("The ~= operator can handle only the s/// function.")); + return; + } + QChar sep = val.at(1); + QStringList func = val.split(sep); + if (func.count() < 3 || func.count() > 4) { + evalError(fL1S("The s/// function expects 3 or 4 arguments.")); + return; + } + + bool global = false, quote = false, case_sense = false; + if (func.count() == 4) { + global = func[3].indexOf(QLatin1Char('g')) != -1; + case_sense = func[3].indexOf(QLatin1Char('i')) == -1; + quote = func[3].indexOf(QLatin1Char('q')) != -1; + } + QString pattern = func[1]; + QString replace = func[2]; + if (quote) + pattern = QRegExp::escape(pattern); + + QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive); + + // We could make a union of modified and unmodified values, + // but this will break just as much as it fixes, so leave it as is. + replaceInList(&valuesRef(varName), regexp, replace, global, m_tmp2); + debugMsg(2, "replaced %s with %s", dbgQStr(pattern), dbgQStr(replace)); + } else { + ProStringList varVal = expandVariableReferences(tokPtr, sizeHint); + switch (tok) { + default: // whatever - cannot happen + case TokAssign: // = + zipEmpty(&varVal); + if (!m_cumulative) { + // FIXME: add check+warning about accidental value removal. + // This may be a bit too noisy, though. + m_valuemapStack.top()[varName] = varVal; + } else { + if (!varVal.isEmpty()) { + // We are greedy for values. But avoid exponential growth. + ProStringList &v = valuesRef(varName); + if (v.isEmpty()) { + v = varVal; + } else { + ProStringList old = v; + v = varVal; + QSet<ProString> has; + has.reserve(v.size()); + foreach (const ProString &s, v) + has.insert(s); + v.reserve(v.size() + old.size()); + foreach (const ProString &s, old) + if (!has.contains(s)) + v << s; + } + } + } + debugMsg(2, "assigning"); + break; + case TokAppendUnique: // *= + insertUnique(&valuesRef(varName), varVal); + debugMsg(2, "appending unique"); + break; + case TokAppend: // += + zipEmpty(&varVal); + valuesRef(varName) += varVal; + debugMsg(2, "appending"); + break; + case TokRemove: // -= + if (!m_cumulative) { + removeEach(&valuesRef(varName), varVal); + } else { + // We are stingy with our values, too. + } + debugMsg(2, "removing"); + break; + } + } + traceMsg("%s := %s", dbgKey(varName), dbgStrList(values(varName))); + + if (varName == statics.strTEMPLATE) + setTemplate(); +#ifdef PROEVALUATOR_FULL + else if (varName == statics.strREQUIRES) + checkRequirements(values(varName)); +#endif +} + +void QMakeEvaluator::setTemplate() +{ + ProStringList &values = valuesRef(statics.strTEMPLATE); + if (!m_option->user_template.isEmpty()) { + // Don't allow override + values = ProStringList(ProString(m_option->user_template)); + } else { + if (values.isEmpty()) + values.append(ProString("app")); + else + values.erase(values.begin() + 1, values.end()); + } + if (!m_option->user_template_prefix.isEmpty()) { + QString val = values.first().toQString(m_tmp1); + if (!val.startsWith(m_option->user_template_prefix)) { + val.prepend(m_option->user_template_prefix); + values = ProStringList(ProString(val)); + } + } +} + +void QMakeEvaluator::loadDefaults() +{ + ProValueMap &vars = m_valuemapStack.top(); + + vars[ProKey("DIR_SEPARATOR")] << ProString(m_option->dir_sep); + vars[ProKey("DIRLIST_SEPARATOR")] << ProString(m_option->dirlist_sep); + vars[ProKey("_DATE_")] << ProString(QDateTime::currentDateTime().toString()); + if (!m_option->qmake_abslocation.isEmpty()) + vars[ProKey("QMAKE_QMAKE")] << ProString(m_option->qmake_abslocation); +#if defined(Q_OS_WIN32) + vars[ProKey("QMAKE_HOST.os")] << ProString("Windows"); + + DWORD name_length = 1024; + wchar_t name[1024]; + if (GetComputerName(name, &name_length)) + vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromWCharArray(name)); + + QSysInfo::WinVersion ver = QSysInfo::WindowsVersion; + vars[ProKey("QMAKE_HOST.version")] << ProString(QString::number(ver)); + ProString verStr; + switch (ver) { + case QSysInfo::WV_Me: verStr = ProString("WinMe"); break; + case QSysInfo::WV_95: verStr = ProString("Win95"); break; + case QSysInfo::WV_98: verStr = ProString("Win98"); break; + case QSysInfo::WV_NT: verStr = ProString("WinNT"); break; + case QSysInfo::WV_2000: verStr = ProString("Win2000"); break; + case QSysInfo::WV_2003: verStr = ProString("Win2003"); break; + case QSysInfo::WV_XP: verStr = ProString("WinXP"); break; + case QSysInfo::WV_VISTA: verStr = ProString("WinVista"); break; + default: verStr = ProString("Unknown"); break; + } + vars[ProKey("QMAKE_HOST.version_string")] << verStr; + + SYSTEM_INFO info; + GetSystemInfo(&info); + ProString archStr; + switch (info.wProcessorArchitecture) { +# ifdef PROCESSOR_ARCHITECTURE_AMD64 + case PROCESSOR_ARCHITECTURE_AMD64: + archStr = ProString("x86_64"); + break; +# endif + case PROCESSOR_ARCHITECTURE_INTEL: + archStr = ProString("x86"); + break; + case PROCESSOR_ARCHITECTURE_IA64: +# ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: +# endif + archStr = ProString("IA64"); + break; + default: + archStr = ProString("Unknown"); + break; + } + vars[ProKey("QMAKE_HOST.arch")] << archStr; + +# if defined(Q_CC_MSVC) // ### bogus condition, but nobody x-builds for msvc with a different qmake + QLatin1Char backslash('\\'); + QString paths = m_option->getEnv(QLatin1String("PATH")); + QString vcBin64 = m_option->getEnv(QLatin1String("VCINSTALLDIR")); + if (!vcBin64.endsWith(backslash)) + vcBin64.append(backslash); + vcBin64.append(QLatin1String("bin\\amd64")); + QString vcBinX86_64 = m_option->getEnv(QLatin1String("VCINSTALLDIR")); + if (!vcBinX86_64.endsWith(backslash)) + vcBinX86_64.append(backslash); + vcBinX86_64.append(QLatin1String("bin\\x86_amd64")); + if (paths.contains(vcBin64, Qt::CaseInsensitive) + || paths.contains(vcBinX86_64, Qt::CaseInsensitive)) + vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86_64"); + else + vars[ProKey("QMAKE_TARGET.arch")] << ProString("x86"); +# endif +#elif defined(Q_OS_UNIX) + struct utsname name; + if (!uname(&name)) { + vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname); + vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(name.nodename)); + vars[ProKey("QMAKE_HOST.version")] << ProString(name.release); + vars[ProKey("QMAKE_HOST.version_string")] << ProString(name.version); + vars[ProKey("QMAKE_HOST.arch")] << ProString(name.machine); + } +#endif + + m_valuemapInited = true; +} + +bool QMakeEvaluator::prepareProject(const QString &inDir) +{ + QString superdir; + if (m_option->do_cache) { + QString conffile; + QString cachefile = m_option->cachefile; + if (cachefile.isEmpty()) { //find it as it has not been specified + if (m_outputDir.isEmpty()) + goto no_cache; + superdir = m_outputDir; + forever { + QString superfile = superdir + QLatin1String("/.qmake.super"); + if (IoUtils::exists(superfile)) { + m_superfile = superfile; + break; + } + QFileInfo qdfi(superdir); + if (qdfi.isRoot()) { + superdir.clear(); + break; + } + superdir = qdfi.path(); + } + QString sdir = inDir; + QString dir = m_outputDir; + forever { + conffile = sdir + QLatin1String("/.qmake.conf"); + if (!IoUtils::exists(conffile)) + conffile.clear(); + cachefile = dir + QLatin1String("/.qmake.cache"); + if (!IoUtils::exists(cachefile)) + cachefile.clear(); + if (!conffile.isEmpty() || !cachefile.isEmpty()) { + if (dir != sdir) + m_sourceRoot = sdir; + m_buildRoot = dir; + break; + } + if (dir == superdir) + goto no_cache; + QFileInfo qsdfi(sdir); + QFileInfo qdfi(dir); + if (qsdfi.isRoot() || qdfi.isRoot()) + goto no_cache; + sdir = qsdfi.path(); + dir = qdfi.path(); + } + } else { + m_buildRoot = QFileInfo(cachefile).path(); + } + m_conffile = conffile; + m_cachefile = cachefile; + } + no_cache: + + // Look for mkspecs/ in source and build. First to win determines the root. + QString sdir = inDir; + QString dir = m_outputDir; + while (dir != m_buildRoot) { + if ((dir != sdir && QFileInfo(sdir, QLatin1String("mkspecs")).isDir()) + || QFileInfo(dir, QLatin1String("mkspecs")).isDir()) { + if (dir != sdir) + m_sourceRoot = sdir; + m_buildRoot = dir; + break; + } + if (dir == superdir) + break; + QFileInfo qsdfi(sdir); + QFileInfo qdfi(dir); + if (qsdfi.isRoot() || qdfi.isRoot()) + break; + sdir = qsdfi.path(); + dir = qdfi.path(); + } + + return true; +} + +bool QMakeEvaluator::loadSpecInternal() +{ + if (evaluateFeatureFile(QLatin1String("spec_pre.prf")) != ReturnTrue) + return false; + QString spec = m_qmakespec + QLatin1String("/qmake.conf"); + if (evaluateFile(spec, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) { + evalError(fL1S("Could not read qmake configuration file %1.").arg(spec)); + return false; + } +#ifndef QT_BUILD_QMAKE + // Legacy support for Qt4 default specs +# ifdef Q_OS_UNIX + if (m_qmakespec.endsWith(QLatin1String("/default-host")) + || m_qmakespec.endsWith(QLatin1String("/default"))) { + QString rspec = QFileInfo(m_qmakespec).readLink(); + if (!rspec.isEmpty()) + m_qmakespec = QDir::cleanPath(QDir(m_qmakespec).absoluteFilePath(rspec)); + } +# else + // We can't resolve symlinks as they do on Unix, so configure.exe puts + // the source of the qmake.conf at the end of the default/qmake.conf in + // the QMAKESPEC_ORIGINAL variable. + const ProString &orig_spec = first(ProKey("QMAKESPEC_ORIGINAL")); + if (!orig_spec.isEmpty()) + m_qmakespec = orig_spec.toQString(); +# endif +#endif + valuesRef(ProKey("QMAKESPEC")) << ProString(m_qmakespec); + m_qmakespecName = IoUtils::fileName(m_qmakespec).toString(); + if (evaluateFeatureFile(QLatin1String("spec_post.prf")) != ReturnTrue) + return false; + // The MinGW and x-build specs may change the separator; $$shell_{path,quote}() need it + m_dirSep = first(ProKey("QMAKE_DIR_SEP")); + return true; +} + +bool QMakeEvaluator::loadSpec() +{ + QString qmakespec = m_option->expandEnvVars( + m_hostBuild ? m_option->qmakespec : m_option->xqmakespec); + + { + QMakeEvaluator evaluator(m_option, m_parser, m_handler); + if (!m_superfile.isEmpty()) { + valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile); + if (evaluator.evaluateFile( + m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) + return false; + } + if (!m_conffile.isEmpty()) { + valuesRef(ProKey("_QMAKE_CONF_")) << ProString(m_conffile); + if (evaluator.evaluateFile( + m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) + return false; + } + if (!m_cachefile.isEmpty()) { + valuesRef(ProKey("_QMAKE_CACHE_")) << ProString(m_cachefile); + if (evaluator.evaluateFile( + m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) + return false; + } + if (qmakespec.isEmpty()) { + if (!m_hostBuild) + qmakespec = evaluator.first(ProKey("XQMAKESPEC")).toQString(); + if (qmakespec.isEmpty()) + qmakespec = evaluator.first(ProKey("QMAKESPEC")).toQString(); + } + m_qmakepath = evaluator.values(ProKey("QMAKEPATH")).toQStringList(); + m_qmakefeatures = evaluator.values(ProKey("QMAKEFEATURES")).toQStringList(); + } + + updateMkspecPaths(); + if (qmakespec.isEmpty()) + qmakespec = propertyValue(ProKey(m_hostBuild ? "QMAKE_SPEC" : "QMAKE_XSPEC")).toQString(); +#ifndef QT_BUILD_QMAKE + // Legacy support for Qt4 qmake in Qt Creator, etc. + if (qmakespec.isEmpty()) + qmakespec = m_hostBuild ? QLatin1String("default-host") : QLatin1String("default"); +#endif + if (IoUtils::isRelativePath(qmakespec)) { + foreach (const QString &root, m_mkspecPaths) { + QString mkspec = root + QLatin1Char('/') + qmakespec; + if (IoUtils::exists(mkspec)) { + qmakespec = mkspec; + goto cool; + } + } + evalError(fL1S("Could not find qmake configuration file %1.").arg(qmakespec)); + return false; + } + cool: + m_qmakespec = QDir::cleanPath(qmakespec); + + if (!m_superfile.isEmpty() + && evaluateFile(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) { + return false; + } + if (!loadSpecInternal()) + return false; + updateFeaturePaths(); // The spec extends the feature search path, so rebuild the cache. + if (!m_conffile.isEmpty() + && evaluateFile(m_conffile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) { + return false; + } + if (!m_cachefile.isEmpty() + && evaluateFile(m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) { + return false; + } + return true; +} + +void QMakeEvaluator::setupProject() +{ + setTemplate(); + ProValueMap &vars = m_valuemapStack.top(); + vars[ProKey("TARGET")] << ProString(QFileInfo(currentFileName()).baseName()); + vars[ProKey("_PRO_FILE_")] << ProString(currentFileName()); + vars[ProKey("_PRO_FILE_PWD_")] << ProString(currentDirectory()); + vars[ProKey("OUT_PWD")] << ProString(m_outputDir); +} + +void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where) +{ + if (!cmds.isEmpty()) { + if (ProFile *pro = m_parser->parsedProBlock(cmds, where, -1)) { + if (pro->isOk()) { + m_locationStack.push(m_current); + visitProBlock(pro, pro->tokPtr()); + m_current = m_locationStack.pop(); + } + pro->deref(); + } + } +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConfigFeatures() +{ + QSet<QString> processed; + forever { + bool finished = true; + ProStringList configs = values(statics.strCONFIG); + for (int i = configs.size() - 1; i >= 0; --i) { + QString config = configs.at(i).toQString(m_tmp1).toLower(); + if (!processed.contains(config)) { + config.detach(); + processed.insert(config); + VisitReturn vr = evaluateFeatureFile(config, true); + if (vr == ReturnError) + return vr; + if (vr == ReturnTrue) { + finished = false; + break; + } + } + } + if (finished) + break; + } + return ReturnTrue; +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile( + ProFile *pro, QMakeHandler::EvalFileType type, LoadFlags flags) +{ + if (!m_cumulative && !pro->isOk()) + return ReturnFalse; + + if (flags & LoadPreFiles) { + if (!prepareProject(pro->directoryName())) + return ReturnFalse; + + m_hostBuild = pro->isHostBuild(); + +#ifdef PROEVALUATOR_THREAD_SAFE + m_option->mutex.lock(); +#endif + QMakeBaseEnv **baseEnvPtr = &m_option->baseEnvs[QMakeBaseKey(m_buildRoot, m_hostBuild)]; + if (!*baseEnvPtr) + *baseEnvPtr = new QMakeBaseEnv; + QMakeBaseEnv *baseEnv = *baseEnvPtr; + +#ifdef PROEVALUATOR_THREAD_SAFE + { + QMutexLocker locker(&baseEnv->mutex); + m_option->mutex.unlock(); + if (baseEnv->inProgress) { + QThreadPool::globalInstance()->releaseThread(); + baseEnv->cond.wait(&baseEnv->mutex); + QThreadPool::globalInstance()->reserveThread(); + if (!baseEnv->isOk) + return ReturnFalse; + } else +#endif + if (!baseEnv->evaluator) { +#ifdef PROEVALUATOR_THREAD_SAFE + baseEnv->inProgress = true; + locker.unlock(); +#endif + + QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_handler); + baseEnv->evaluator = baseEval; + baseEval->m_superfile = m_superfile; + baseEval->m_conffile = m_conffile; + baseEval->m_cachefile = m_cachefile; + baseEval->m_sourceRoot = m_sourceRoot; + baseEval->m_buildRoot = m_buildRoot; + baseEval->m_hostBuild = m_hostBuild; + bool ok = baseEval->loadSpec(); + +#ifdef PROEVALUATOR_THREAD_SAFE + locker.relock(); + baseEnv->isOk = ok; + baseEnv->inProgress = false; + baseEnv->cond.wakeAll(); +#endif + + if (!ok) + return ReturnFalse; + } +#ifdef PROEVALUATOR_THREAD_SAFE + } +#endif + + initFrom(*baseEnv->evaluator); + } else { + if (!m_valuemapInited) + loadDefaults(); + } + +#ifdef QT_BUILD_QMAKE + for (ProValueMap::ConstIterator it = m_extraVars.constBegin(); + it != m_extraVars.constEnd(); ++it) + m_valuemapStack.first().insert(it.key(), it.value()); +#endif + + VisitReturn vr; + + m_handler->aboutToEval(currentProFile(), pro, type); + m_profileStack.push(pro); + valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory())); + if (flags & LoadPreFiles) { + setupProject(); + + if ((vr = evaluateFeatureFile(QLatin1String("default_pre.prf"))) == ReturnError) + goto failed; + + evaluateCommand(m_option->precmds, fL1S("(command line)")); + +#ifdef QT_BUILD_QMAKE + // After user configs, to override them + if (!m_extraConfigs.isEmpty()) + evaluateCommand("CONFIG += " + m_extraConfigs.join(' '), fL1S("(extra configs)")); +#endif + } + + debugMsg(1, "visiting file %s", qPrintable(pro->fileName())); + if ((vr = visitProBlock(pro, pro->tokPtr())) == ReturnError) + goto failed; + debugMsg(1, "done visiting file %s", qPrintable(pro->fileName())); + + if (flags & LoadPostFiles) { + evaluateCommand(m_option->postcmds, fL1S("(command line -after)")); + +#ifdef QT_BUILD_QMAKE + // Again, to ensure the project does not mess with us. + // Specifically, do not allow a project to override debug/release within a + // debug_and_release build pass - it's too late for that at this point anyway. + if (!m_extraConfigs.isEmpty()) + evaluateCommand("CONFIG += " + m_extraConfigs.join(' '), fL1S("(extra configs)")); +#endif + + if ((vr = evaluateFeatureFile(QLatin1String("default_post.prf"))) == ReturnError) + goto failed; + + if ((vr = evaluateConfigFeatures()) == ReturnError) + goto failed; + } + vr = ReturnTrue; + failed: + m_profileStack.pop(); + valuesRef(ProKey("PWD")) = ProStringList(ProString(currentDirectory())); + m_handler->doneWithEval(currentProFile()); + + return vr; +} + + +void QMakeEvaluator::updateMkspecPaths() +{ + QStringList ret; + const QString concat = QLatin1String("/mkspecs"); + + foreach (const QString &it, m_option->getPathListEnv(QLatin1String("QMAKEPATH"))) + ret << it + concat; + + foreach (const QString &it, m_qmakepath) + ret << it + concat; + + if (!m_buildRoot.isEmpty()) + ret << m_buildRoot + concat; + if (!m_sourceRoot.isEmpty()) + ret << m_sourceRoot + concat; + + ret << m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + concat; + + ret.removeDuplicates(); + m_mkspecPaths = ret; +} + +void QMakeEvaluator::updateFeaturePaths() +{ + QString mkspecs_concat = QLatin1String("/mkspecs"); + QString features_concat = QLatin1String("/features/"); + + QStringList feature_roots; + + foreach (const QString &f, m_option->getPathListEnv(QLatin1String("QMAKEFEATURES"))) + feature_roots += f; + + feature_roots += m_qmakefeatures; + + feature_roots += m_option->propertyValue(ProKey("QMAKEFEATURES")).toQString(m_mtmp).split( + m_option->dirlist_sep, QString::SkipEmptyParts); + + QStringList feature_bases; + if (!m_buildRoot.isEmpty()) + feature_bases << m_buildRoot; + if (!m_sourceRoot.isEmpty()) + feature_bases << m_sourceRoot; + + foreach (const QString &item, m_option->getPathListEnv(QLatin1String("QMAKEPATH"))) + feature_bases << (item + mkspecs_concat); + + foreach (const QString &item, m_qmakepath) + feature_bases << (item + mkspecs_concat); + + if (!m_qmakespec.isEmpty()) { + // The spec is already platform-dependent, so no subdirs here. + feature_roots << (m_qmakespec + features_concat); + + // Also check directly under the root directory of the mkspecs collection + QDir specdir(m_qmakespec); + while (!specdir.isRoot() && specdir.cdUp()) { + const QString specpath = specdir.path(); + if (specpath.endsWith(mkspecs_concat)) { + if (IoUtils::exists(specpath + features_concat)) + feature_bases << specpath; + break; + } + } + } + + feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")).toQString(m_mtmp) + + mkspecs_concat); + + foreach (const QString &fb, feature_bases) { + foreach (const ProString &sfx, values(ProKey("QMAKE_PLATFORM"))) + feature_roots << (fb + features_concat + sfx + QLatin1Char('/')); + feature_roots << (fb + features_concat); + } + + for (int i = 0; i < feature_roots.count(); ++i) + if (!feature_roots.at(i).endsWith((ushort)'/')) + feature_roots[i].append((ushort)'/'); + + feature_roots.removeDuplicates(); + + QStringList ret; + foreach (const QString &root, feature_roots) + if (IoUtils::exists(root)) + ret << root; + m_featureRoots = ret; +} + +ProString QMakeEvaluator::propertyValue(const ProKey &name) const +{ + if (name == QLatin1String("QMAKE_MKSPECS")) + return ProString(m_mkspecPaths.join(m_option->dirlist_sep)); + ProString ret = m_option->propertyValue(name); +// if (ret.isNull()) +// evalError(fL1S("Querying unknown property %1").arg(name.toQString(m_mtmp))); + return ret; +} + +ProFile *QMakeEvaluator::currentProFile() const +{ + if (m_profileStack.count() > 0) + return m_profileStack.top(); + return 0; +} + +QString QMakeEvaluator::currentFileName() const +{ + ProFile *pro = currentProFile(); + if (pro) + return pro->fileName(); + return QString(); +} + +QString QMakeEvaluator::currentDirectory() const +{ + ProFile *pro = currentProFile(); + if (pro) + return pro->directoryName(); + return QString(); +} + +bool QMakeEvaluator::isActiveConfig(const QString &config, bool regex) +{ + // magic types for easy flipping + if (config == statics.strtrue) + return true; + if (config == statics.strfalse) + return false; + + if (config == statics.strhost_build) + return m_hostBuild; + + if (regex && (config.contains(QLatin1Char('*')) || config.contains(QLatin1Char('?')))) { + QString cfg = config; + cfg.detach(); // Keep m_tmp out of QRegExp's cache + QRegExp re(cfg, Qt::CaseSensitive, QRegExp::Wildcard); + + // mkspecs + if (re.exactMatch(m_qmakespecName)) + return true; + + // CONFIG variable + int t = 0; + foreach (const ProString &configValue, values(statics.strCONFIG)) { + if (re.exactMatch(configValue.toQString(m_tmp[t]))) + return true; + t ^= 1; + } + } else { + // mkspecs + if (m_qmakespecName == config) + return true; + + // CONFIG variable + if (values(statics.strCONFIG).contains(ProString(config))) + return true; + } + + return false; +} + +ProStringList QMakeEvaluator::expandVariableReferences( + const ushort *&tokPtr, int sizeHint, bool joined) +{ + ProStringList ret; + ret.reserve(sizeHint); + forever { + evaluateExpression(tokPtr, &ret, joined); + switch (*tokPtr) { + case TokValueTerminator: + case TokFuncTerminator: + tokPtr++; + return ret; + case TokArgSeparator: + if (joined) { + tokPtr++; + continue; + } + // fallthrough + default: + Q_ASSERT_X(false, "expandVariableReferences", "Unrecognized token"); + break; + } + } +} + +QList<ProStringList> QMakeEvaluator::prepareFunctionArgs(const ushort *&tokPtr) +{ + QList<ProStringList> args_list; + if (*tokPtr != TokFuncTerminator) { + for (;; tokPtr++) { + ProStringList arg; + evaluateExpression(tokPtr, &arg, false); + args_list << arg; + if (*tokPtr == TokFuncTerminator) + break; + Q_ASSERT(*tokPtr == TokArgSeparator); + } + } + tokPtr++; + return args_list; +} + +ProStringList QMakeEvaluator::evaluateFunction( + const ProFunctionDef &func, const QList<ProStringList> &argumentsList, VisitReturn *ok) +{ + VisitReturn vr; + ProStringList ret; + + if (m_valuemapStack.count() >= 100) { + evalError(fL1S("Ran into infinite recursion (depth > 100).")); + vr = ReturnFalse; + } else { + m_valuemapStack.push(ProValueMap()); + m_locationStack.push(m_current); + + ProStringList args; + for (int i = 0; i < argumentsList.count(); ++i) { + args += argumentsList[i]; + m_valuemapStack.top()[ProKey(QString::number(i+1))] = argumentsList[i]; + } + m_valuemapStack.top()[statics.strARGS] = args; + vr = visitProBlock(func.pro(), func.tokPtr()); + if (vr == ReturnReturn) + vr = ReturnTrue; + ret = m_returnValue; + m_returnValue.clear(); + + m_current = m_locationStack.pop(); + m_valuemapStack.pop(); + } + if (ok) + *ok = vr; + if (vr == ReturnTrue) + return ret; + return ProStringList(); +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBoolFunction( + const ProFunctionDef &func, const QList<ProStringList> &argumentsList, + const ProString &function) +{ + VisitReturn vr; + ProStringList ret = evaluateFunction(func, argumentsList, &vr); + if (vr == ReturnTrue) { + if (ret.isEmpty()) + return ReturnTrue; + if (ret.at(0) != statics.strfalse) { + if (ret.at(0) == statics.strtrue) + return ReturnTrue; + bool ok; + int val = ret.at(0).toQString(m_tmp1).toInt(&ok); + if (ok) { + if (val) + return ReturnTrue; + } else { + evalError(fL1S("Unexpected return value from test '%1': %2.") + .arg(function.toQString(m_tmp1)) + .arg(ret.join(QLatin1String(" :: ")))); + } + } + return ReturnFalse; + } + return vr; +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditionalFunction( + const ProKey &func, const ushort *&tokPtr) +{ + if (int func_t = statics.functions.value(func)) { + //why don't the builtin functions just use args_list? --Sam + return evaluateBuiltinConditional(func_t, func, expandVariableReferences(tokPtr, 5, true)); + } + + QHash<ProKey, ProFunctionDef>::ConstIterator it = + m_functionDefs.testFunctions.constFind(func); + if (it != m_functionDefs.testFunctions.constEnd()) { + const QList<ProStringList> args = prepareFunctionArgs(tokPtr); + traceMsg("calling %s(%s)", dbgKey(func), dbgStrListList(args)); + return evaluateBoolFunction(*it, args, func); + } + + skipExpression(tokPtr); + evalError(fL1S("'%1' is not a recognized test function.").arg(func.toQString(m_tmp1))); + return ReturnFalse; +} + +ProStringList QMakeEvaluator::evaluateExpandFunction( + const ProKey &func, const ushort *&tokPtr) +{ + if (int func_t = statics.expands.value(func)) { + //why don't the builtin functions just use args_list? --Sam + return evaluateBuiltinExpand(func_t, func, expandVariableReferences(tokPtr, 5, true)); + } + + QHash<ProKey, ProFunctionDef>::ConstIterator it = + m_functionDefs.replaceFunctions.constFind(func); + if (it != m_functionDefs.replaceFunctions.constEnd()) { + const QList<ProStringList> args = prepareFunctionArgs(tokPtr); + traceMsg("calling $$%s(%s)", dbgKey(func), dbgStrListList(args)); + return evaluateFunction(*it, args, 0); + } + + skipExpression(tokPtr); + evalError(fL1S("'%1' is not a recognized replace function.").arg(func.toQString(m_tmp1))); + return ProStringList(); +} + +bool QMakeEvaluator::evaluateConditional(const QString &cond, const QString &where, int line) +{ + bool ret = false; + ProFile *pro = m_parser->parsedProBlock(cond, where, line, QMakeParser::TestGrammar); + if (pro) { + if (pro->isOk()) { + m_locationStack.push(m_current); + ret = visitProBlock(pro, pro->tokPtr()) == ReturnTrue; + m_current = m_locationStack.pop(); + } + pro->deref(); + } + return ret; +} + +#ifdef PROEVALUATOR_FULL +void QMakeEvaluator::checkRequirements(const ProStringList &deps) +{ + ProStringList &failed = valuesRef(ProKey("QMAKE_FAILED_REQUIREMENTS")); + foreach (const ProString &dep, deps) + if (!evaluateConditional(dep.toQString(), m_current.pro->fileName(), m_current.line)) + failed << dep; +} +#endif + +ProValueMap *QMakeEvaluator::findValues(const ProKey &variableName, ProValueMap::Iterator *rit) +{ + ProValueMapStack::Iterator vmi = m_valuemapStack.end(); + do { + --vmi; + ProValueMap::Iterator it = (*vmi).find(variableName); + if (it != (*vmi).end()) { + if (it->constBegin() == statics.fakeValue.constBegin()) + return 0; + *rit = it; + return &(*vmi); + } + } while (vmi != m_valuemapStack.begin()); + return 0; +} + +ProStringList &QMakeEvaluator::valuesRef(const ProKey &variableName) +{ + ProValueMap::Iterator it = m_valuemapStack.top().find(variableName); + if (it != m_valuemapStack.top().end()) { + if (it->constBegin() == statics.fakeValue.constBegin()) + it->clear(); + return *it; + } + ProValueMapStack::Iterator vmi = m_valuemapStack.end(); + if (--vmi != m_valuemapStack.begin()) { + do { + --vmi; + ProValueMap::ConstIterator it = (*vmi).constFind(variableName); + if (it != (*vmi).constEnd()) { + ProStringList &ret = m_valuemapStack.top()[variableName]; + if (it->constBegin() != statics.fakeValue.constBegin()) + ret = *it; + return ret; + } + } while (vmi != m_valuemapStack.begin()); + } + return m_valuemapStack.top()[variableName]; +} + +ProStringList QMakeEvaluator::values(const ProKey &variableName) const +{ + ProValueMapStack::ConstIterator vmi = m_valuemapStack.constEnd(); + do { + --vmi; + ProValueMap::ConstIterator it = (*vmi).constFind(variableName); + if (it != (*vmi).constEnd()) { + if (it->constBegin() == statics.fakeValue.constBegin()) + break; + return *it; + } + } while (vmi != m_valuemapStack.constBegin()); + return ProStringList(); +} + +ProString QMakeEvaluator::first(const ProKey &variableName) const +{ + const ProStringList &vals = values(variableName); + if (!vals.isEmpty()) + return vals.first(); + return ProString(); +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile( + const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags) +{ + if (ProFile *pro = m_parser->parsedProFile(fileName, true)) { + m_locationStack.push(m_current); + VisitReturn ok = visitProFile(pro, type, flags); + m_current = m_locationStack.pop(); + pro->deref(); +#ifdef PROEVALUATOR_FULL + if (ok == ReturnTrue) { + ProStringList &iif = m_valuemapStack.first()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")]; + ProString ifn(fileName); + if (!iif.contains(ifn)) + iif << ifn; + } +#endif + return ok; + } else { + if (!(flags & LoadSilent) && !IoUtils::exists(fileName)) + evalError(fL1S("WARNING: Include file %1 not found").arg(fileName)); + return ReturnFalse; + } +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileChecked( + const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags) +{ + if (fileName.isEmpty()) + return ReturnFalse; + QMakeEvaluator *ref = this; + do { + foreach (const ProFile *pf, ref->m_profileStack) + if (pf->fileName() == fileName) { + evalError(fL1S("Circular inclusion of %1.").arg(fileName)); + return ReturnFalse; + } + } while ((ref = ref->m_caller)); + return evaluateFile(fileName, type, flags); +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile( + const QString &fileName, bool silent) +{ + QString fn = fileName; + if (!fn.endsWith(QLatin1String(".prf"))) + fn += QLatin1String(".prf"); + + if (m_featureRoots.isEmpty()) + updateFeaturePaths(); + int start_root = 0; + QString currFn = currentFileName(); + if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) { + for (int root = 0; root < m_featureRoots.size(); ++root) + if (currFn == m_featureRoots.at(root) + fn) { + start_root = root + 1; + break; + } + } + for (int root = start_root; root < m_featureRoots.size(); ++root) { + QString fname = m_featureRoots.at(root) + fn; + if (IoUtils::exists(fname)) { + fn = fname; + goto cool; + } + } +#ifdef QMAKE_BUILTIN_PRFS + fn.prepend(QLatin1String(":/qmake/features/")); + if (QFileInfo(fn).exists()) + goto cool; +#endif + if (!silent) + evalError(fL1S("Cannot find feature %1").arg(fileName)); + return ReturnFalse; + + cool: + ProStringList &already = valuesRef(ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES")); + ProString afn(fn); + if (already.contains(afn)) { + if (!silent) + languageWarning(fL1S("Feature %1 already included").arg(fileName)); + return ReturnTrue; + } + already.append(afn); + +#ifdef PROEVALUATOR_CUMULATIVE + bool cumulative = m_cumulative; + m_cumulative = false; +#endif + + // The path is fully normalized already. + VisitReturn ok = evaluateFile(fn, QMakeHandler::EvalFeatureFile, LoadProOnly); + +#ifdef PROEVALUATOR_CUMULATIVE + m_cumulative = cumulative; +#endif + return ok; +} + +QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileInto( + const QString &fileName, ProValueMap *values, LoadFlags flags) +{ + QMakeEvaluator visitor(m_option, m_parser, m_handler); + visitor.m_caller = this; + visitor.m_outputDir = m_outputDir; + visitor.m_featureRoots = m_featureRoots; + VisitReturn ret = visitor.evaluateFileChecked(fileName, QMakeHandler::EvalAuxFile, flags); + if (ret != ReturnTrue) + return ret; + *values = visitor.m_valuemapStack.top(); +#ifdef PROEVALUATOR_FULL + ProKey qiif("QMAKE_INTERNAL_INCLUDED_FILES"); + ProStringList &iif = m_valuemapStack.first()[qiif]; + foreach (const ProString &ifn, values->value(qiif)) + if (!iif.contains(ifn)) + iif << ifn; +#endif + return ReturnTrue; +} + +void QMakeEvaluator::message(int type, const QString &msg) const +{ + if (!m_skipLevel) + m_handler->message(type, msg, + m_current.line ? m_current.pro->fileName() : QString(), + m_current.line != 0xffff ? m_current.line : -1); +} + +#ifdef PROEVALUATOR_DEBUG +void QMakeEvaluator::debugMsgInternal(int level, const char *fmt, ...) const +{ + va_list ap; + + if (level <= m_debugLevel) { + fprintf(stderr, "DEBUG %d: ", level); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); + } +} + +void QMakeEvaluator::traceMsgInternal(const char *fmt, ...) const +{ + va_list ap; + + if (!m_current.pro) + fprintf(stderr, "DEBUG 1: "); + else if (m_current.line <= 0) + fprintf(stderr, "DEBUG 1: %s: ", qPrintable(m_current.pro->fileName())); + else + fprintf(stderr, "DEBUG 1: %s:%d: ", qPrintable(m_current.pro->fileName()), m_current.line); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputc('\n', stderr); +} + +QString QMakeEvaluator::formatValue(const ProString &val, bool forceQuote) +{ + QString ret; + ret.reserve(val.size() + 2); + const QChar *chars = val.constData(); + bool quote = forceQuote || val.isEmpty(); + for (int i = 0, l = val.size(); i < l; i++) { + QChar c = chars[i]; + ushort uc = c.unicode(); + if (uc < 32) { + switch (uc) { + case '\r': + ret += QLatin1String("\\r"); + break; + case '\n': + ret += QLatin1String("\\n"); + break; + case '\t': + ret += QLatin1String("\\t"); + break; + default: + ret += QString::fromLatin1("\\x%1").arg(uc, 2, 16, QLatin1Char('0')); + break; + } + } else { + switch (uc) { + case '\\': + ret += QLatin1String("\\\\"); + break; + case '"': + ret += QLatin1String("\\\""); + break; + case '\'': + ret += QLatin1String("\\'"); + break; + case 32: + quote = true; + // fallthrough + default: + ret += c; + break; + } + } + } + if (quote) { + ret.prepend(QLatin1Char('"')); + ret.append(QLatin1Char('"')); + } + return ret; +} + +QString QMakeEvaluator::formatValueList(const ProStringList &vals, bool commas) +{ + QString ret; + + foreach (const ProString &str, vals) { + if (!ret.isEmpty()) { + if (commas) + ret += QLatin1Char(','); + ret += QLatin1Char(' '); + } + ret += formatValue(str); + } + return ret; +} + +QString QMakeEvaluator::formatValueListList(const QList<ProStringList> &lists) +{ + QString ret; + + foreach (const ProStringList &list, lists) { + if (!ret.isEmpty()) + ret += QLatin1String(", "); + ret += formatValueList(list); + } + return ret; +} +#endif + +QT_END_NAMESPACE diff --git a/QMakeFileReader/evaluator/qmakeevaluator.h b/QMakeFileReader/evaluator/qmakeevaluator.h new file mode 100644 index 0000000..eeefc9b --- /dev/null +++ b/QMakeFileReader/evaluator/qmakeevaluator.h @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAKEEVALUATOR_H +#define QMAKEEVALUATOR_H + +#if defined(PROEVALUATOR_FULL) && defined(PROEVALUATOR_THREAD_SAFE) +# error PROEVALUATOR_FULL is incompatible with PROEVALUATOR_THREAD_SAFE due to cache() implementation +#endif + +#include "qmakeparser.h" +#include "ioutils.h" + +#include <qlist.h> +#include <qlinkedlist.h> +#include <qset.h> +#include <qstack.h> +#include <qstring.h> +#include <qstringlist.h> +#ifndef QT_BOOTSTRAPPED +# include <qprocess.h> +#endif + +QT_BEGIN_NAMESPACE + +class QMakeGlobals; + +class QMAKE_EXPORT QMakeHandler : public QMakeParserHandler +{ +public: + enum { + SourceEvaluator = 0x10, + + EvalWarnLanguage = SourceEvaluator | WarningMessage | WarnLanguage, + EvalWarnDeprecated = SourceEvaluator | WarningMessage | WarnDeprecated, + + EvalError = ErrorMessage | SourceEvaluator + }; + + // error(), warning() and message() from .pro file + virtual void fileMessage(const QString &msg) = 0; + + enum EvalFileType { EvalProjectFile, EvalIncludeFile, EvalConfigFile, EvalFeatureFile, EvalAuxFile }; + virtual void aboutToEval(ProFile *parent, ProFile *proFile, EvalFileType type) = 0; + virtual void doneWithEval(ProFile *parent) = 0; +}; + +// We use a QLinkedList based stack instead of a QVector based one (QStack), so that +// the addresses of value maps stay constant. The qmake generators rely on that. +class QMAKE_EXPORT ProValueMapStack : public QLinkedList<ProValueMap> +{ +public: + inline void push(const ProValueMap &t) { append(t); } + inline ProValueMap pop() { return takeLast(); } + ProValueMap &top() { return last(); } + const ProValueMap &top() const { return last(); } +}; + +class QMAKE_EXPORT QMakeEvaluator +{ +public: + enum LoadFlag { + LoadProOnly = 0, + LoadPreFiles = 1, + LoadPostFiles = 2, + LoadAll = LoadPreFiles|LoadPostFiles, + LoadSilent = 0x10 + }; + Q_DECLARE_FLAGS(LoadFlags, LoadFlag) + + static void initStatics(); + static void initFunctionStatics(); + QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, + QMakeHandler *handler); + ~QMakeEvaluator(); + +#ifdef QT_BUILD_QMAKE + void setExtraVars(const ProValueMap &extraVars) { m_extraVars = extraVars; } + void setExtraConfigs(const ProStringList &extraConfigs) { m_extraConfigs = extraConfigs; } +#endif + void setOutputDir(const QString &outputDir) { m_outputDir = outputDir; } + + ProStringList values(const ProKey &variableName) const; + ProStringList &valuesRef(const ProKey &variableName); + ProString first(const ProKey &variableName) const; + ProString propertyValue(const ProKey &val) const; + + ProString dirSep() const { return m_dirSep; } + bool isHostBuild() const { return m_hostBuild; } + + enum VisitReturn { + ReturnFalse, + ReturnTrue, + ReturnError, + ReturnBreak, + ReturnNext, + ReturnReturn + }; + + static ALWAYS_INLINE VisitReturn returnBool(bool b) + { return b ? ReturnTrue : ReturnFalse; } + + static ALWAYS_INLINE uint getBlockLen(const ushort *&tokPtr); + ProString getStr(const ushort *&tokPtr); + ProKey getHashStr(const ushort *&tokPtr); + void evaluateExpression(const ushort *&tokPtr, ProStringList *ret, bool joined); + static ALWAYS_INLINE void skipStr(const ushort *&tokPtr); + static ALWAYS_INLINE void skipHashStr(const ushort *&tokPtr); + void skipExpression(const ushort *&tokPtr); + + void loadDefaults(); + bool prepareProject(const QString &inDir); + bool loadSpecInternal(); + bool loadSpec(); + void initFrom(const QMakeEvaluator &other); + void setupProject(); + void evaluateCommand(const QString &cmds, const QString &where); + VisitReturn visitProFile(ProFile *pro, QMakeHandler::EvalFileType type, + LoadFlags flags); + VisitReturn visitProBlock(ProFile *pro, const ushort *tokPtr); + VisitReturn visitProBlock(const ushort *tokPtr); + VisitReturn visitProLoop(const ProKey &variable, const ushort *exprPtr, + const ushort *tokPtr); + void visitProFunctionDef(ushort tok, const ProKey &name, const ushort *tokPtr); + void visitProVariable(ushort tok, const ProStringList &curr, const ushort *&tokPtr); + + ALWAYS_INLINE const ProKey &map(const ProString &var) { return map(var.toKey()); } + const ProKey &map(const ProKey &var); + ProValueMap *findValues(const ProKey &variableName, ProValueMap::Iterator *it); + + void setTemplate(); + + ProStringList split_value_list(const QString &vals, const ProFile *source = 0); + ProStringList expandVariableReferences(const ProString &value, int *pos = 0, bool joined = false); + ProStringList expandVariableReferences(const ushort *&tokPtr, int sizeHint = 0, bool joined = false); + + QString currentFileName() const; + QString currentDirectory() const; + ProFile *currentProFile() const; + QString resolvePath(const QString &fileName) const + { return QMakeInternal::IoUtils::resolvePath(currentDirectory(), fileName); } + + VisitReturn evaluateFile(const QString &fileName, QMakeHandler::EvalFileType type, + LoadFlags flags); + VisitReturn evaluateFileChecked(const QString &fileName, QMakeHandler::EvalFileType type, + LoadFlags flags); + VisitReturn evaluateFeatureFile(const QString &fileName, bool silent = false); + VisitReturn evaluateFileInto(const QString &fileName, + ProValueMap *values, // output-only + LoadFlags flags); + VisitReturn evaluateConfigFeatures(); + void message(int type, const QString &msg) const; + void evalError(const QString &msg) const + { message(QMakeHandler::EvalError, msg); } + void languageWarning(const QString &msg) const + { message(QMakeHandler::EvalWarnLanguage, msg); } + void deprecationWarning(const QString &msg) const + { message(QMakeHandler::EvalWarnDeprecated, msg); } + + QList<ProStringList> prepareFunctionArgs(const ushort *&tokPtr); + ProStringList evaluateFunction(const ProFunctionDef &func, + const QList<ProStringList> &argumentsList, VisitReturn *ok); + VisitReturn evaluateBoolFunction(const ProFunctionDef &func, + const QList<ProStringList> &argumentsList, + const ProString &function); + + ProStringList evaluateExpandFunction(const ProKey &function, const ushort *&tokPtr); + VisitReturn evaluateConditionalFunction(const ProKey &function, const ushort *&tokPtr); + + ProStringList evaluateBuiltinExpand(int func_t, const ProKey &function, const ProStringList &args); + VisitReturn evaluateBuiltinConditional(int func_t, const ProKey &function, const ProStringList &args); + + bool evaluateConditional(const QString &cond, const QString &where, int line = -1); +#ifdef PROEVALUATOR_FULL + void checkRequirements(const ProStringList &deps); +#endif + + void updateMkspecPaths(); + void updateFeaturePaths(); + + bool isActiveConfig(const QString &config, bool regex = false); + + void populateDeps( + const ProStringList &deps, const ProString &prefix, + QHash<ProKey, QSet<ProKey> > &dependencies, + ProValueMap &dependees, ProStringList &rootSet) const; + + VisitReturn writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode, + const QString &contents); +#ifndef QT_BOOTSTRAPPED + void runProcess(QProcess *proc, const QString &command) const; +#endif + QByteArray getCommandOutput(const QString &args) const; + + static void removeEach(ProStringList *varlist, const ProStringList &value); + + QMakeEvaluator *m_caller; +#ifdef PROEVALUATOR_CUMULATIVE + bool m_cumulative; + int m_skipLevel; +#else + enum { m_cumulative = 0 }; + enum { m_skipLevel = 0 }; +#endif + +#ifdef PROEVALUATOR_DEBUG + void debugMsgInternal(int level, const char *fmt, ...) const; + void traceMsgInternal(const char *fmt, ...) const; + static QString formatValue(const ProString &val, bool forceQuote = false); + static QString formatValueList(const ProStringList &vals, bool commas = false); + static QString formatValueListList(const QList<ProStringList> &vals); + + const int m_debugLevel; +#else + ALWAYS_INLINE void debugMsgInternal(int, const char *, ...) const {} + ALWAYS_INLINE void traceMsgInternal(const char *, ...) const {} + + enum { m_debugLevel = 0 }; +#endif + + struct Location { + Location() : pro(0), line(0) {} + Location(ProFile *_pro, ushort _line) : pro(_pro), line(_line) {} + void clear() { pro = 0; line = 0; } + ProFile *pro; + ushort line; + }; + + Location m_current; // Currently evaluated location + QStack<Location> m_locationStack; // All execution location changes + QStack<ProFile *> m_profileStack; // Includes only + +#ifdef QT_BUILD_QMAKE + ProValueMap m_extraVars; + ProStringList m_extraConfigs; +#endif + QString m_outputDir; + + int m_listCount; + bool m_valuemapInited; + bool m_hostBuild; + QString m_qmakespec; + QString m_qmakespecName; + QString m_superfile; + QString m_conffile; + QString m_cachefile; + QString m_sourceRoot; + QString m_buildRoot; + QStringList m_qmakepath; + QStringList m_qmakefeatures; + QStringList m_mkspecPaths; + QStringList m_featureRoots; + ProString m_dirSep; + ProFunctionDefs m_functionDefs; + ProStringList m_returnValue; + ProValueMapStack m_valuemapStack; // VariableName must be us-ascii, the content however can be non-us-ascii. + QString m_tmp1, m_tmp2, m_tmp3, m_tmp[2]; // Temporaries for efficient toQString + mutable QString m_mtmp; + + QMakeGlobals *m_option; + QMakeParser *m_parser; + QMakeHandler *m_handler; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeEvaluator::LoadFlags) + +QT_END_NAMESPACE + +#endif // QMAKEEVALUATOR_H diff --git a/QMakeFileReader/evaluator/qmakeevaluator_p.h b/QMakeFileReader/evaluator/qmakeevaluator_p.h new file mode 100644 index 0000000..88a3f96 --- /dev/null +++ b/QMakeFileReader/evaluator/qmakeevaluator_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAKEEVALUATOR_P_H +#define QMAKEEVALUATOR_P_H + +#include "proitems.h" + +#include <qregexp.h> + +#define debugMsg if (!m_debugLevel) {} else debugMsgInternal +#define traceMsg if (!m_debugLevel) {} else traceMsgInternal +#ifdef PROEVALUATOR_DEBUG +# define dbgBool(b) (b ? "true" : "false") +# define dbgReturn(r) \ + (r == ReturnError ? "error" : \ + r == ReturnBreak ? "break" : \ + r == ReturnNext ? "next" : \ + r == ReturnReturn ? "return" : \ + "<invalid>") +# define dbgKey(s) qPrintable(s.toString().toQString()) +# define dbgStr(s) qPrintable(formatValue(s, true)) +# define dbgStrList(s) qPrintable(formatValueList(s)) +# define dbgSepStrList(s) qPrintable(formatValueList(s, true)) +# define dbgStrListList(s) qPrintable(formatValueListList(s)) +# define dbgQStr(s) dbgStr(ProString(s)) +#else +# define dbgBool(b) 0 +# define dbgReturn(r) 0 +# define dbgKey(s) 0 +# define dbgStr(s) 0 +# define dbgStrList(s) 0 +# define dbgSepStrList(s) 0 +# define dbgStrListList(s) 0 +# define dbgQStr(s) 0 +#endif + +QT_BEGIN_NAMESPACE + +namespace QMakeInternal { + +struct QMakeStatics { + QString field_sep; + QString strtrue; + QString strfalse; + ProKey strCONFIG; + ProKey strARGS; + QString strDot; + QString strDotDot; + QString strever; + QString strforever; + QString strhost_build; + ProKey strTEMPLATE; +#ifdef PROEVALUATOR_FULL + ProKey strREQUIRES; +#endif + QHash<ProKey, int> expands; + QHash<ProKey, int> functions; + QHash<ProKey, ProKey> varMap; + ProStringList fakeValue; +}; + +extern QMakeStatics statics; + +} + +QT_END_NAMESPACE + +#endif // QMAKEEVALUATOR_P_H diff --git a/QMakeFileReader/evaluator/qmakeglobals.cpp b/QMakeFileReader/evaluator/qmakeglobals.cpp new file mode 100644 index 0000000..19928cc --- /dev/null +++ b/QMakeFileReader/evaluator/qmakeglobals.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmakeglobals.h" + +#include "qmakeevaluator.h" +#include "ioutils.h" + +#include <qbytearray.h> +#include <qdatetime.h> +#include <qdebug.h> +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qlist.h> +#include <qregexp.h> +#include <qset.h> +#include <qstack.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qtextstream.h> +#ifdef PROEVALUATOR_THREAD_SAFE +# include <qthreadpool.h> +#endif + +#ifdef Q_OS_UNIX +#include <unistd.h> +#include <sys/utsname.h> +#else +#include <windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> + +#ifdef Q_OS_WIN32 +#define QT_POPEN _popen +#define QT_PCLOSE _pclose +#else +#define QT_POPEN popen +#define QT_PCLOSE pclose +#endif + +QT_BEGIN_NAMESPACE + +#define fL1S(s) QString::fromLatin1(s) + +namespace { // MSVC doesn't seem to know the semantics of "static" ... + +static struct { + QRegExp reg_variableName; +} statics; + +} + +static void initStatics() +{ + if (!statics.reg_variableName.isEmpty()) + return; + + statics.reg_variableName.setPattern(QLatin1String("\\$\\(.*\\)")); + statics.reg_variableName.setMinimal(true); +} + +QMakeGlobals::QMakeGlobals() +{ + initStatics(); + + do_cache = true; + +#ifdef PROEVALUATOR_DEBUG + debugLevel = 0; +#endif +#ifdef Q_OS_WIN + dirlist_sep = QLatin1Char(';'); + dir_sep = QLatin1Char('\\'); +#else + dirlist_sep = QLatin1Char(':'); + dir_sep = QLatin1Char('/'); +#endif + qmakespec = getEnv(QLatin1String("QMAKESPEC")); +} + +QMakeGlobals::~QMakeGlobals() +{ + qDeleteAll(baseEnvs); +} + +QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec) +{ + QString ret = QDir::cleanPath(spec); + if (ret.contains(QLatin1Char('/'))) { + QString absRet = QDir(state.pwd).absoluteFilePath(ret); + if (QFile::exists(absRet)) + ret = QDir::cleanPath(absRet); + } + return ret; +} + +QMakeGlobals::ArgumentReturn QMakeGlobals::addCommandLineArguments( + QMakeCmdLineParserState &state, QStringList &args, int *pos) +{ + enum { ArgNone, ArgConfig, ArgSpec, ArgXSpec, ArgTmpl, ArgTmplPfx, ArgCache } argState = ArgNone; + for (; *pos < args.count(); (*pos)++) { + QString arg = args.at(*pos); + switch (argState) { + case ArgConfig: + if (state.after) + state.postconfigs << arg; + else + state.preconfigs << arg; + break; + case ArgSpec: + qmakespec = args[*pos] = cleanSpec(state, arg); + break; + case ArgXSpec: + xqmakespec = args[*pos] = cleanSpec(state, arg); + break; + case ArgTmpl: + user_template = arg; + break; + case ArgTmplPfx: + user_template_prefix = arg; + break; + case ArgCache: + cachefile = args[*pos] = QDir::cleanPath(QDir(state.pwd).absoluteFilePath(arg)); + break; + default: + if (arg.startsWith(QLatin1Char('-'))) { + if (arg == QLatin1String("-after")) { + state.after = true; + } else if (arg == QLatin1String("-config")) { + argState = ArgConfig; + } else if (arg == QLatin1String("-nocache")) { + do_cache = false; + } else if (arg == QLatin1String("-cache")) { + argState = ArgCache; + } else if (arg == QLatin1String("-platform") || arg == QLatin1String("-spec")) { + argState = ArgSpec; + } else if (arg == QLatin1String("-xplatform") || arg == QLatin1String("-xspec")) { + argState = ArgXSpec; + } else if (arg == QLatin1String("-template") || arg == QLatin1String("-t")) { + argState = ArgTmpl; + } else if (arg == QLatin1String("-template_prefix") || arg == QLatin1String("-tp")) { + argState = ArgTmplPfx; + } else if (arg == QLatin1String("-win32")) { + dir_sep = QLatin1Char('\\'); + } else if (arg == QLatin1String("-unix")) { + dir_sep = QLatin1Char('/'); + } else { + return ArgumentUnknown; + } + } else if (arg.contains(QLatin1Char('='))) { + if (state.after) + state.postcmds << arg; + else + state.precmds << arg; + } else { + return ArgumentUnknown; + } + continue; + } + argState = ArgNone; + } + if (argState != ArgNone) + return ArgumentMalformed; + return ArgumentsOk; +} + +void QMakeGlobals::commitCommandLineArguments(QMakeCmdLineParserState &state) +{ + if (!state.preconfigs.isEmpty()) + state.precmds << (fL1S("CONFIG += ") + state.preconfigs.join(fL1S(" "))); + precmds = state.precmds.join(fL1S("\n")); + if (!state.postconfigs.isEmpty()) + state.postcmds << (fL1S("CONFIG += ") + state.postconfigs.join(fL1S(" "))); + postcmds = state.postcmds.join(fL1S("\n")); + + if (xqmakespec.isEmpty()) + xqmakespec = qmakespec; +} + +void QMakeGlobals::useEnvironment() +{ + if (xqmakespec.isEmpty()) + xqmakespec = getEnv(QLatin1String("XQMAKESPEC")); + if (qmakespec.isEmpty()) { + qmakespec = getEnv(QLatin1String("QMAKESPEC")); + if (xqmakespec.isEmpty()) + xqmakespec = qmakespec; + } +} + +void QMakeGlobals::setCommandLineArguments(const QString &pwd, const QStringList &_args) +{ + QStringList args = _args; + + QMakeCmdLineParserState state(pwd); + for (int pos = 0; pos < args.size(); pos++) + addCommandLineArguments(state, args, &pos); + commitCommandLineArguments(state); + useEnvironment(); +} + +void QMakeGlobals::setDirectories(const QString &input_dir, const QString &output_dir) +{ + if (input_dir != output_dir && !output_dir.isEmpty()) { + QString srcpath = input_dir; + if (!srcpath.endsWith(QLatin1Char('/'))) + srcpath += QLatin1Char('/'); + QString dstpath = output_dir; + if (!dstpath.endsWith(QLatin1Char('/'))) + dstpath += QLatin1Char('/'); + int srcLen = srcpath.length(); + int dstLen = dstpath.length(); + int lastSl = -1; + while (++lastSl, srcpath.at(--srcLen) == dstpath.at(--dstLen)) + if (srcpath.at(srcLen) == QLatin1Char('/')) + lastSl = 0; + source_root = srcpath.left(srcLen + lastSl); + build_root = dstpath.left(dstLen + lastSl); + } +} + +QString QMakeGlobals::shadowedPath(const QString &fileName) const +{ + if (source_root.isEmpty()) + return fileName; + if (fileName.startsWith(source_root) + && (fileName.length() == source_root.length() + || fileName.at(source_root.length()) == QLatin1Char('/'))) { + return build_root + fileName.mid(source_root.length()); + } + return QString(); +} + +QString QMakeGlobals::getEnv(const QString &var) const +{ +#ifdef PROEVALUATOR_SETENV + return environment.value(var); +#else + return QString::fromLocal8Bit(qgetenv(var.toLocal8Bit().constData())); +#endif +} + +QStringList QMakeGlobals::getPathListEnv(const QString &var) const +{ + QStringList ret; + QString val = getEnv(var); + if (!val.isEmpty()) { + QDir bdir; + QStringList vals = val.split(dirlist_sep); + ret.reserve(vals.length()); + foreach (const QString &it, vals) + ret << QDir::cleanPath(bdir.absoluteFilePath(it)); + } + return ret; +} + +QString QMakeGlobals::expandEnvVars(const QString &str) const +{ + QString string = str; + int rep; + QRegExp reg_variableName = statics.reg_variableName; // Copy for thread safety + while ((rep = reg_variableName.indexIn(string)) != -1) + string.replace(rep, reg_variableName.matchedLength(), + getEnv(string.mid(rep + 2, reg_variableName.matchedLength() - 3))); + return string; +} + +#ifndef QT_BUILD_QMAKE +#ifdef PROEVALUATOR_INIT_PROPS +bool QMakeGlobals::initProperties() +{ + QByteArray data; +#ifndef QT_BOOTSTRAPPED + QProcess proc; + proc.start(qmake_abslocation, QStringList() << QLatin1String("-query")); + if (!proc.waitForFinished()) + return false; + data = proc.readAll(); +#else + if (FILE *proc = QT_POPEN(QString(QMakeInternal::IoUtils::shellQuote(qmake_abslocation) + + QLatin1String(" -query")).toLocal8Bit(), "r")) { + char buff[1024]; + while (!feof(proc)) + data.append(buff, int(fread(buff, 1, 1023, proc))); + QT_PCLOSE(proc); + } +#endif + foreach (QByteArray line, data.split('\n')) + if (!line.startsWith("QMAKE_")) { + int off = line.indexOf(':'); + if (off < 0) // huh? + continue; + if (line.endsWith('\r')) + line.chop(1); + QString name = QString::fromLatin1(line.left(off)); + ProString value = ProString(QDir::fromNativeSeparators( + QString::fromLocal8Bit(line.mid(off + 1)))); + properties.insert(ProKey(name), value); + if (name.startsWith(QLatin1String("QT_")) && !name.contains(QLatin1Char('/'))) { + if (name.startsWith(QLatin1String("QT_INSTALL_"))) { + properties.insert(ProKey(name + QLatin1String("/raw")), value); + properties.insert(ProKey(name + QLatin1String("/get")), value); + if (name == QLatin1String("QT_INSTALL_PREFIX") + || name == QLatin1String("QT_INSTALL_DATA") + || name == QLatin1String("QT_INSTALL_BINS")) { + name.replace(3, 7, QLatin1String("HOST")); + properties.insert(ProKey(name), value); + properties.insert(ProKey(name + QLatin1String("/get")), value); + } + } else if (name.startsWith(QLatin1String("QT_HOST_"))) { + properties.insert(ProKey(name + QLatin1String("/get")), value); + } + } + } + properties.insert(ProKey("QMAKE_VERSION"), ProString("2.01a")); + return true; +} +#else +void QMakeGlobals::setProperties(const QHash<QString, QString> &props) +{ + QHash<QString, QString>::ConstIterator it = props.constBegin(), eit = props.constEnd(); + for (; it != eit; ++it) + properties.insert(ProKey(it.key()), ProString(it.value())); +} +#endif +#endif // QT_BUILD_QMAKE + +QT_END_NAMESPACE diff --git a/QMakeFileReader/evaluator/qmakeglobals.h b/QMakeFileReader/evaluator/qmakeglobals.h new file mode 100644 index 0000000..5e39ecc --- /dev/null +++ b/QMakeFileReader/evaluator/qmakeglobals.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAKEGLOBALS_H +#define QMAKEGLOBALS_H + +#include "qmake_global.h" +#include "proitems.h" + +#ifdef QT_BUILD_QMAKE +# include <property.h> +#endif + +#include <qhash.h> +#include <qstringlist.h> +#ifndef QT_BOOTSTRAPPED +# include <qprocess.h> +#endif +#ifdef PROEVALUATOR_THREAD_SAFE +# include <qmutex.h> +# include <qwaitcondition.h> +#endif + +QT_BEGIN_NAMESPACE + +class QMakeEvaluator; + +class QMakeBaseKey +{ +public: + QMakeBaseKey(const QString &_root, bool _hostBuild); + + QString root; + bool hostBuild; +}; + +uint qHash(const QMakeBaseKey &key); +bool operator==(const QMakeBaseKey &one, const QMakeBaseKey &two); + +class QMakeBaseEnv +{ +public: + QMakeBaseEnv(); + ~QMakeBaseEnv(); + +#ifdef PROEVALUATOR_THREAD_SAFE + QMutex mutex; + QWaitCondition cond; + bool inProgress; + // The coupling of this flag to thread safety exists because for other + // use cases failure is immediately fatal anyway. + bool isOk; +#endif + QMakeEvaluator *evaluator; +}; + +class QMAKE_EXPORT QMakeCmdLineParserState +{ +public: + QMakeCmdLineParserState(const QString &_pwd) : pwd(_pwd), after(false) {} + QString pwd; + QStringList precmds, preconfigs, postcmds, postconfigs; + bool after; +}; + +class QMAKE_EXPORT QMakeGlobals +{ +public: + QMakeGlobals(); + ~QMakeGlobals(); + + bool do_cache; + QString dir_sep; + QString dirlist_sep; + QString cachefile; +#ifdef PROEVALUATOR_SETENV + QProcessEnvironment environment; +#endif + QString qmake_abslocation; + + QString qmakespec, xqmakespec; + QString user_template, user_template_prefix; + QString precmds, postcmds; + +#ifdef PROEVALUATOR_DEBUG + int debugLevel; +#endif + + enum ArgumentReturn { ArgumentUnknown, ArgumentMalformed, ArgumentsOk }; + ArgumentReturn addCommandLineArguments(QMakeCmdLineParserState &state, + QStringList &args, int *pos); + void commitCommandLineArguments(QMakeCmdLineParserState &state); + void setCommandLineArguments(const QString &pwd, const QStringList &args); + void useEnvironment(); + void setDirectories(const QString &input_dir, const QString &output_dir); +#ifdef QT_BUILD_QMAKE + void setQMakeProperty(QMakeProperty *prop) { property = prop; } + ProString propertyValue(const ProKey &name) const { return property->value(name); } +#else +# ifdef PROEVALUATOR_INIT_PROPS + bool initProperties(); +# else + void setProperties(const QHash<QString, QString> &props); +# endif + ProString propertyValue(const ProKey &name) const { return properties.value(name); } +#endif + + QString expandEnvVars(const QString &str) const; + QString shadowedPath(const QString &fileName) const; + +private: + QString getEnv(const QString &) const; + QStringList getPathListEnv(const QString &var) const; + + QString cleanSpec(QMakeCmdLineParserState &state, const QString &spec); + + QString source_root, build_root; + +#ifdef QT_BUILD_QMAKE + QMakeProperty *property; +#else + QHash<ProKey, ProString> properties; +#endif + +#ifdef PROEVALUATOR_THREAD_SAFE + QMutex mutex; +#endif + QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs; + + friend class QMakeEvaluator; +}; + +QT_END_NAMESPACE + +#endif // QMAKEGLOBALS_H diff --git a/QMakeFileReader/evaluator/qmakeparser.cpp b/QMakeFileReader/evaluator/qmakeparser.cpp new file mode 100644 index 0000000..d354bbd --- /dev/null +++ b/QMakeFileReader/evaluator/qmakeparser.cpp @@ -0,0 +1,1212 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmakeparser.h" + +#include "ioutils.h" +using namespace QMakeInternal; + +#include <qfile.h> +#ifdef PROPARSER_THREAD_SAFE +# include <qthreadpool.h> +#endif + +QT_BEGIN_NAMESPACE + +/////////////////////////////////////////////////////////////////////// +// +// ProFileCache +// +/////////////////////////////////////////////////////////////////////// + +ProFileCache::~ProFileCache() +{ + foreach (const Entry &ent, parsed_files) + if (ent.pro) + ent.pro->deref(); +} + +void ProFileCache::discardFile(const QString &fileName) +{ +#ifdef PROPARSER_THREAD_SAFE + QMutexLocker lck(&mutex); +#endif + QHash<QString, Entry>::Iterator it = parsed_files.find(fileName); + if (it != parsed_files.end()) { + if (it->pro) + it->pro->deref(); + parsed_files.erase(it); + } +} + +void ProFileCache::discardFiles(const QString &prefix) +{ +#ifdef PROPARSER_THREAD_SAFE + QMutexLocker lck(&mutex); +#endif + QHash<QString, Entry>::Iterator + it = parsed_files.begin(), + end = parsed_files.end(); + while (it != end) + if (it.key().startsWith(prefix)) { + if (it->pro) + it->pro->deref(); + it = parsed_files.erase(it); + } else { + ++it; + } +} + + +////////// Parser /////////// + +#define fL1S(s) QString::fromLatin1(s) + +namespace { // MSVC2010 doesn't seem to know the semantics of "static" ... + +static struct { + QString strelse; + QString strfor; + QString strdefineTest; + QString strdefineReplace; + QString stroption; + QString strreturn; + QString strnext; + QString strbreak; + QString strhost_build; + QString strLINE; + QString strFILE; + QString strLITERAL_HASH; + QString strLITERAL_DOLLAR; + QString strLITERAL_WHITESPACE; +} statics; + +} + +void QMakeParser::initialize() +{ + if (!statics.strelse.isNull()) + return; + + statics.strelse = QLatin1String("else"); + statics.strfor = QLatin1String("for"); + statics.strdefineTest = QLatin1String("defineTest"); + statics.strdefineReplace = QLatin1String("defineReplace"); + statics.stroption = QLatin1String("option"); + statics.strreturn = QLatin1String("return"); + statics.strnext = QLatin1String("next"); + statics.strbreak = QLatin1String("break"); + statics.strhost_build = QLatin1String("host_build"); + statics.strLINE = QLatin1String("_LINE_"); + statics.strFILE = QLatin1String("_FILE_"); + statics.strLITERAL_HASH = QLatin1String("LITERAL_HASH"); + statics.strLITERAL_DOLLAR = QLatin1String("LITERAL_DOLLAR"); + statics.strLITERAL_WHITESPACE = QLatin1String("LITERAL_WHITESPACE"); +} + +QMakeParser::QMakeParser(ProFileCache *cache, QMakeParserHandler *handler) + : m_cache(cache) + , m_handler(handler) +{ + // So that single-threaded apps don't have to call initialize() for now. + initialize(); +} + +ProFile *QMakeParser::parsedProFile(const QString &fileName, bool cache) +{ + ProFile *pro; + if (cache && m_cache) { + ProFileCache::Entry *ent; +#ifdef PROPARSER_THREAD_SAFE + QMutexLocker locker(&m_cache->mutex); +#endif + QHash<QString, ProFileCache::Entry>::Iterator it = m_cache->parsed_files.find(fileName); + if (it != m_cache->parsed_files.end()) { + ent = &*it; +#ifdef PROPARSER_THREAD_SAFE + if (ent->locker && !ent->locker->done) { + ++ent->locker->waiters; + QThreadPool::globalInstance()->releaseThread(); + ent->locker->cond.wait(locker.mutex()); + QThreadPool::globalInstance()->reserveThread(); + if (!--ent->locker->waiters) { + delete ent->locker; + ent->locker = 0; + } + } +#endif + if ((pro = ent->pro)) + pro->ref(); + } else { + ent = &m_cache->parsed_files[fileName]; +#ifdef PROPARSER_THREAD_SAFE + ent->locker = new ProFileCache::Entry::Locker; + locker.unlock(); +#endif + pro = new ProFile(fileName); + if (!read(pro)) { + delete pro; + pro = 0; + } else { + pro->itemsRef()->squeeze(); + pro->ref(); + } + ent->pro = pro; +#ifdef PROPARSER_THREAD_SAFE + locker.relock(); + if (ent->locker->waiters) { + ent->locker->done = true; + ent->locker->cond.wakeAll(); + } else { + delete ent->locker; + ent->locker = 0; + } +#endif + } + } else { + pro = new ProFile(fileName); + if (!read(pro)) { + delete pro; + pro = 0; + } + } + return pro; +} + +ProFile *QMakeParser::parsedProBlock( + const QString &contents, const QString &name, int line, SubGrammar grammar) +{ + ProFile *pro = new ProFile(name); + if (!read(pro, contents, line, grammar)) { + delete pro; + pro = 0; + } + return pro; +} + +void QMakeParser::discardFileFromCache(const QString &fileName) +{ + if (m_cache) + m_cache->discardFile(fileName); +} + +bool QMakeParser::read(ProFile *pro) +{ + QFile file(pro->fileName()); + if (!file.open(QIODevice::ReadOnly)) { + if (m_handler && IoUtils::exists(pro->fileName())) + m_handler->message(QMakeParserHandler::ParserIoError, + fL1S("Cannot read %1: %2").arg(pro->fileName(), file.errorString())); + return false; + } + + QByteArray bcont = file.readAll(); + if (bcont.startsWith(QByteArray("\xef\xbb\xbf"))) { + // UTF-8 BOM will cause subtle errors + m_handler->message(QMakeParserHandler::ParserIoError, + fL1S("Unexpected UTF-8 BOM in %1").arg(pro->fileName())); + return false; + } + QString content(QString::fromLocal8Bit(bcont)); + bcont.clear(); + file.close(); + return read(pro, content, 1, FullGrammar); +} + +void QMakeParser::putTok(ushort *&tokPtr, ushort tok) +{ + *tokPtr++ = tok; +} + +void QMakeParser::putBlockLen(ushort *&tokPtr, uint len) +{ + *tokPtr++ = (ushort)len; + *tokPtr++ = (ushort)(len >> 16); +} + +void QMakeParser::putBlock(ushort *&tokPtr, const ushort *buf, uint len) +{ + memcpy(tokPtr, buf, len * 2); + tokPtr += len; +} + +void QMakeParser::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len) +{ + uint hash = ProString::hash((const QChar *)buf, len); + ushort *tokPtr = pTokPtr; + *tokPtr++ = (ushort)hash; + *tokPtr++ = (ushort)(hash >> 16); + *tokPtr++ = (ushort)len; + memcpy(tokPtr, buf, len * 2); + pTokPtr = tokPtr + len; +} + +void QMakeParser::finalizeHashStr(ushort *buf, uint len) +{ + buf[-4] = TokHashLiteral; + buf[-1] = len; + uint hash = ProString::hash((const QChar *)buf, len); + buf[-3] = (ushort)hash; + buf[-2] = (ushort)(hash >> 16); +} + +bool QMakeParser::read(ProFile *pro, const QString &in, int line, SubGrammar grammar) +{ + m_proFile = pro; + m_lineNo = line; + + // Final precompiled token stream buffer + QString tokBuff; + // Worst-case size calculations: + // - line marker adds 1 (2-nl) to 1st token of each line + // - empty assignment "A=":2 => + // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) + + // TokValueTerminator(1) == 7 (8) + // - non-empty assignment "A=B C":5 => + // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) + + // TokLiteral(1) + len(1) + "B"(1) + + // TokLiteral(1) + len(1) + "C"(1) + TokValueTerminator(1) == 13 (14) + // - variable expansion: "$$f":3 => + // TokVariable(1) + hash(2) + len(1) + "f"(1) = 5 + // - function expansion: "$$f()":5 => + // TokFuncName(1) + hash(2) + len(1) + "f"(1) + TokFuncTerminator(1) = 6 + // - scope: "X:":2 => + // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokCondition(1) + + // TokBranch(1) + len(2) + ... + len(2) + ... == 10 + // - test: "X():":4 => + // TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokTestCall(1) + TokFuncTerminator(1) + + // TokBranch(1) + len(2) + ... + len(2) + ... == 11 + // - "for(A,B):":9 => + // TokForLoop(1) + hash(2) + len(1) + "A"(1) + + // len(2) + TokLiteral(1) + len(1) + "B"(1) + TokValueTerminator(1) + + // len(2) + ... + TokTerminator(1) == 14 (15) + tokBuff.reserve((in.size() + 1) * 5); + ushort *tokPtr = (ushort *)tokBuff.constData(); // Current writing position + + // Expression precompiler buffer. + QString xprBuff; + xprBuff.reserve(tokBuff.capacity()); // Excessive, but simple + ushort *buf = (ushort *)xprBuff.constData(); + + // Parser state + m_blockstack.clear(); + m_blockstack.resize(1); + + QStack<ParseCtx> xprStack; + xprStack.reserve(10); + + // We rely on QStrings being null-terminated, so don't maintain a global end pointer. + const ushort *cur = (const ushort *)in.unicode(); + m_canElse = false; + freshLine: + m_state = StNew; + m_invert = false; + m_operator = NoOperator; + m_markLine = m_lineNo; + m_inError = false; + int parens = 0; // Braces in value context + int argc = 0; + int wordCount = 0; // Number of words in currently accumulated expression + int lastIndent = 0; // Previous line's indentation, to detect accidental continuation abuse + bool lineMarked = true; // For in-expression markers + ushort needSep = TokNewStr; // Met unquoted whitespace + ushort quote = 0; + ushort term = 0; + + Context context; + ushort *ptr; + if (grammar == ValueGrammar) { + context = CtxPureValue; + ptr = tokPtr + 2; + } else { + context = CtxTest; + ptr = buf + 4; + } + ushort *xprPtr = ptr; + +#define FLUSH_LHS_LITERAL() \ + do { \ + if ((tlen = ptr - xprPtr)) { \ + finalizeHashStr(xprPtr, tlen); \ + if (needSep) { \ + wordCount++; \ + needSep = 0; \ + } \ + } else { \ + ptr -= 4; \ + } \ + } while (0) + +#define FLUSH_RHS_LITERAL() \ + do { \ + if ((tlen = ptr - xprPtr)) { \ + xprPtr[-2] = TokLiteral | needSep; \ + xprPtr[-1] = tlen; \ + if (needSep) { \ + wordCount++; \ + needSep = 0; \ + } \ + } else { \ + ptr -= 2; \ + } \ + } while (0) + +#define FLUSH_LITERAL() \ + do { \ + if (context == CtxTest) \ + FLUSH_LHS_LITERAL(); \ + else \ + FLUSH_RHS_LITERAL(); \ + } while (0) + +#define FLUSH_VALUE_LIST() \ + do { \ + if (wordCount > 1) { \ + xprPtr = tokPtr; \ + if (*xprPtr == TokLine) \ + xprPtr += 2; \ + tokPtr[-1] = ((*xprPtr & TokMask) == TokLiteral) ? wordCount : 0; \ + } else { \ + tokPtr[-1] = 0; \ + } \ + tokPtr = ptr; \ + putTok(tokPtr, TokValueTerminator); \ + } while (0) + + const ushort *end; // End of this line + const ushort *cptr; // Start of next line + bool lineCont; + int indent; + + if (context == CtxPureValue) { + end = (const ushort *)in.unicode() + in.length(); + cptr = 0; + lineCont = false; + indent = 0; // just gcc being stupid + goto nextChr; + } + + forever { + ushort c; + + // First, skip leading whitespace + for (indent = 0; ; ++cur, ++indent) { + c = *cur; + if (c == '\n') { + ++cur; + goto flushLine; + } else if (!c) { + cur = 0; + goto flushLine; + } else if (c != ' ' && c != '\t' && c != '\r') { + break; + } + } + + // Then strip comments. Yep - no escaping is possible. + for (cptr = cur;; ++cptr) { + c = *cptr; + if (c == '#') { + for (end = cptr; (c = *++cptr);) { + if (c == '\n') { + ++cptr; + break; + } + } + if (end == cur) { // Line with only a comment (sans whitespace) + if (m_markLine == m_lineNo) + m_markLine++; + // Qmake bizarreness: such lines do not affect line continuations + goto ignore; + } + break; + } + if (!c) { + end = cptr; + break; + } + if (c == '\n') { + end = cptr++; + break; + } + } + + // Then look for line continuations. Yep - no escaping here as well. + forever { + // We don't have to check for underrun here, as we already determined + // that the line is non-empty. + ushort ec = *(end - 1); + if (ec == '\\') { + --end; + lineCont = true; + break; + } + if (ec != ' ' && ec != '\t' && ec != '\r') { + lineCont = false; + break; + } + --end; + } + + // Finally, do the tokenization + ushort tok, rtok; + int tlen; + newWord: + do { + if (cur == end) + goto lineEnd; + c = *cur++; + } while (c == ' ' || c == '\t'); + forever { + if (c == '$') { + if (*cur == '$') { // may be EOF, EOL, WS, '#' or '\\' if past end + cur++; + FLUSH_LITERAL(); + if (!lineMarked) { + lineMarked = true; + *ptr++ = TokLine; + *ptr++ = (ushort)m_lineNo; + } + term = 0; + tok = TokVariable; + c = *cur; + if (c == '[') { + ptr += 4; + tok = TokProperty; + term = ']'; + c = *++cur; + } else if (c == '{') { + ptr += 4; + term = '}'; + c = *++cur; + } else if (c == '(') { + ptr += 2; + tok = TokEnvVar; + term = ')'; + c = *++cur; + } else { + ptr += 4; + } + xprPtr = ptr; + rtok = tok; + while ((c & 0xFF00) || c == '.' || c == '_' || + (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || (c == '/' && term)) { + *ptr++ = c; + if (++cur == end) { + c = 0; + goto notfunc; + } + c = *cur; + } + if (tok == TokVariable && c == '(') + tok = TokFuncName; + notfunc: + if (ptr == xprPtr) + languageWarning(fL1S("Missing name in expansion")); + if (quote) + tok |= TokQuoted; + if (needSep) { + tok |= needSep; + wordCount++; + } + tlen = ptr - xprPtr; + if (rtok != TokVariable + || !resolveVariable(xprPtr, tlen, needSep, &ptr, + &buf, &xprBuff, &tokPtr, &tokBuff, cur, in)) { + if (rtok == TokVariable || rtok == TokProperty) { + xprPtr[-4] = tok; + uint hash = ProString::hash((const QChar *)xprPtr, tlen); + xprPtr[-3] = (ushort)hash; + xprPtr[-2] = (ushort)(hash >> 16); + xprPtr[-1] = tlen; + } else { + xprPtr[-2] = tok; + xprPtr[-1] = tlen; + } + } + if ((tok & TokMask) == TokFuncName) { + cur++; + funcCall: + { + xprStack.resize(xprStack.size() + 1); + ParseCtx &top = xprStack.top(); + top.parens = parens; + top.quote = quote; + top.terminator = term; + top.context = context; + top.argc = argc; + top.wordCount = wordCount; + } + parens = 0; + quote = 0; + term = 0; + argc = 1; + context = CtxArgs; + nextToken: + wordCount = 0; + nextWord: + ptr += (context == CtxTest) ? 4 : 2; + xprPtr = ptr; + needSep = TokNewStr; + goto newWord; + } + if (term) { + checkTerm: + if (c != term) { + parseError(fL1S("Missing %1 terminator [found %2]") + .arg(QChar(term)) + .arg(c ? QString(c) : QString::fromLatin1("end-of-line"))); + pro->setOk(false); + m_inError = true; + // Just parse on, as if there was a terminator ... + } else { + cur++; + } + } + joinToken: + ptr += (context == CtxTest) ? 4 : 2; + xprPtr = ptr; + needSep = 0; + goto nextChr; + } + } else if (c == '\\') { + static const char symbols[] = "[]{}()$\\'\""; + ushort c2; + if (cur != end && !((c2 = *cur) & 0xff00) && strchr(symbols, c2)) { + c = c2; + cur++; + } else { + deprecationWarning(fL1S("Unescaped backslashes are deprecated")); + } + } else if (quote) { + if (c == quote) { + quote = 0; + goto nextChr; + } else if (c == '!' && ptr == xprPtr && context == CtxTest) { + m_invert ^= true; + goto nextChr; + } + } else if (c == '\'' || c == '"') { + quote = c; + goto nextChr; + } else if (context == CtxArgs) { + // Function arg context + if (c == ' ' || c == '\t') { + FLUSH_RHS_LITERAL(); + goto nextWord; + } else if (c == '(') { + ++parens; + } else if (c == ')') { + if (--parens < 0) { + FLUSH_RHS_LITERAL(); + *ptr++ = TokFuncTerminator; + int theargc = argc; + { + ParseCtx &top = xprStack.top(); + parens = top.parens; + quote = top.quote; + term = top.terminator; + context = top.context; + argc = top.argc; + wordCount = top.wordCount; + xprStack.resize(xprStack.size() - 1); + } + if (term == ':') { + finalizeCall(tokPtr, buf, ptr, theargc); + goto nextItem; + } else if (term == '}') { + c = (cur == end) ? 0 : *cur; + goto checkTerm; + } else { + Q_ASSERT(!term); + goto joinToken; + } + } + } else if (!parens && c == ',') { + FLUSH_RHS_LITERAL(); + *ptr++ = TokArgSeparator; + argc++; + goto nextToken; + } + } else if (context == CtxTest) { + // Test or LHS context + if (c == ' ' || c == '\t') { + FLUSH_LHS_LITERAL(); + goto nextWord; + } else if (c == '(') { + FLUSH_LHS_LITERAL(); + if (wordCount != 1) { + if (wordCount) + parseError(fL1S("Extra characters after test expression.")); + else + parseError(fL1S("Opening parenthesis without prior test name.")); + pro->setOk(false); + ptr = buf; // Put empty function name + } + *ptr++ = TokTestCall; + term = ':'; + goto funcCall; + } else if (c == '!' && ptr == xprPtr) { + m_invert ^= true; + goto nextChr; + } else if (c == ':') { + FLUSH_LHS_LITERAL(); + finalizeCond(tokPtr, buf, ptr, wordCount); + if (m_state == StNew) + parseError(fL1S("And operator without prior condition.")); + else + m_operator = AndOperator; + nextItem: + ptr = buf; + goto nextToken; + } else if (c == '|') { + FLUSH_LHS_LITERAL(); + finalizeCond(tokPtr, buf, ptr, wordCount); + if (m_state != StCond) + parseError(fL1S("Or operator without prior condition.")); + else + m_operator = OrOperator; + goto nextItem; + } else if (c == '{') { + FLUSH_LHS_LITERAL(); + finalizeCond(tokPtr, buf, ptr, wordCount); + flushCond(tokPtr); + ++m_blockstack.top().braceLevel; + if (grammar == TestGrammar) { + parseError(fL1S("Opening scope not permitted in this context.")); + pro->setOk(false); + } + goto nextItem; + } else if (c == '}') { + FLUSH_LHS_LITERAL(); + finalizeCond(tokPtr, buf, ptr, wordCount); + flushScopes(tokPtr); + closeScope: + if (!m_blockstack.top().braceLevel) { + parseError(fL1S("Excess closing brace.")); + } else if (!--m_blockstack.top().braceLevel + && m_blockstack.count() != 1) { + leaveScope(tokPtr); + m_state = StNew; + m_canElse = false; + m_markLine = m_lineNo; + } + goto nextItem; + } else if (c == '+') { + tok = TokAppend; + goto do2Op; + } else if (c == '-') { + tok = TokRemove; + goto do2Op; + } else if (c == '*') { + tok = TokAppendUnique; + goto do2Op; + } else if (c == '~') { + tok = TokReplace; + do2Op: + if (*cur == '=') { + cur++; + goto doOp; + } + } else if (c == '=') { + tok = TokAssign; + doOp: + FLUSH_LHS_LITERAL(); + flushCond(tokPtr); + putLineMarker(tokPtr); + if (grammar == TestGrammar) { + parseError(fL1S("Assignment not permitted in this context.")); + pro->setOk(false); + } else if (wordCount != 1) { + parseError(fL1S("Assignment needs exactly one word on the left hand side.")); + pro->setOk(false); + // Put empty variable name. + } else { + putBlock(tokPtr, buf, ptr - buf); + } + putTok(tokPtr, tok); + context = CtxValue; + ptr = ++tokPtr; + goto nextToken; + } + } else if (context == CtxValue) { + if (c == ' ' || c == '\t') { + FLUSH_RHS_LITERAL(); + goto nextWord; + } else if (c == '{') { + ++parens; + } else if (c == '}') { + if (!parens) { + FLUSH_RHS_LITERAL(); + FLUSH_VALUE_LIST(); + context = CtxTest; + goto closeScope; + } + --parens; + } else if (c == '=') { + if (indent < lastIndent) + languageWarning(fL1S("Possible accidental line continuation")); + } + } + *ptr++ = c; + nextChr: + if (cur == end) + goto lineEnd; + c = *cur++; + } + + lineEnd: + if (lineCont) { + if (quote) { + *ptr++ = ' '; + } else { + FLUSH_LITERAL(); + needSep = TokNewStr; + ptr += (context == CtxTest) ? 4 : 2; + xprPtr = ptr; + } + } else { + cur = cptr; + flushLine: + FLUSH_LITERAL(); + if (quote) { + parseError(fL1S("Missing closing %1 quote").arg(QChar(quote))); + if (!xprStack.isEmpty()) { + context = xprStack.at(0).context; + xprStack.clear(); + } + goto flErr; + } else if (!xprStack.isEmpty()) { + parseError(fL1S("Missing closing parenthesis in function call")); + context = xprStack.at(0).context; + xprStack.clear(); + flErr: + pro->setOk(false); + if (context == CtxValue) { + tokPtr[-1] = 0; // sizehint + putTok(tokPtr, TokValueTerminator); + } else if (context == CtxPureValue) { + putTok(tokPtr, TokValueTerminator); + } else { + bogusTest(tokPtr); + } + } else if (context == CtxValue) { + FLUSH_VALUE_LIST(); + if (parens) + languageWarning(fL1S("Possible braces mismatch")); + } else if (context == CtxPureValue) { + tokPtr = ptr; + putTok(tokPtr, TokValueTerminator); + } else { + finalizeCond(tokPtr, buf, ptr, wordCount); + } + if (!cur) + break; + ++m_lineNo; + goto freshLine; + } + + lastIndent = indent; + lineMarked = false; + ignore: + cur = cptr; + ++m_lineNo; + } + + flushScopes(tokPtr); + if (m_blockstack.size() > 1) { + parseError(fL1S("Missing closing brace(s).")); + pro->setOk(false); + } + while (m_blockstack.size()) + leaveScope(tokPtr); + tokBuff.resize(tokPtr - (ushort *)tokBuff.constData()); // Reserved capacity stays + *pro->itemsRef() = tokBuff; + return true; + +#undef FLUSH_VALUE_LIST +#undef FLUSH_LITERAL +#undef FLUSH_LHS_LITERAL +#undef FLUSH_RHS_LITERAL +} + +void QMakeParser::putLineMarker(ushort *&tokPtr) +{ + if (m_markLine) { + *tokPtr++ = TokLine; + *tokPtr++ = (ushort)m_markLine; + m_markLine = 0; + } +} + +void QMakeParser::enterScope(ushort *&tokPtr, bool special, ScopeState state) +{ + uchar nest = m_blockstack.top().nest; + m_blockstack.resize(m_blockstack.size() + 1); + m_blockstack.top().special = special; + m_blockstack.top().start = tokPtr; + m_blockstack.top().nest = nest; + tokPtr += 2; + m_state = state; + m_canElse = false; + if (special) + m_markLine = m_lineNo; +} + +void QMakeParser::leaveScope(ushort *&tokPtr) +{ + if (m_blockstack.top().inBranch) { + // Put empty else block + putBlockLen(tokPtr, 0); + } + if (ushort *start = m_blockstack.top().start) { + putTok(tokPtr, TokTerminator); + uint len = tokPtr - start - 2; + start[0] = (ushort)len; + start[1] = (ushort)(len >> 16); + } + m_blockstack.resize(m_blockstack.size() - 1); +} + +// If we are on a fresh line, close all open one-line scopes. +void QMakeParser::flushScopes(ushort *&tokPtr) +{ + if (m_state == StNew) { + while (!m_blockstack.top().braceLevel && m_blockstack.size() > 1) + leaveScope(tokPtr); + if (m_blockstack.top().inBranch) { + m_blockstack.top().inBranch = false; + // Put empty else block + putBlockLen(tokPtr, 0); + } + m_canElse = false; + } +} + +// If there is a pending conditional, enter a new scope, otherwise flush scopes. +void QMakeParser::flushCond(ushort *&tokPtr) +{ + if (m_state == StCond) { + putTok(tokPtr, TokBranch); + m_blockstack.top().inBranch = true; + enterScope(tokPtr, false, StNew); + } else { + flushScopes(tokPtr); + } +} + +void QMakeParser::finalizeTest(ushort *&tokPtr) +{ + flushScopes(tokPtr); + putLineMarker(tokPtr); + if (m_operator != NoOperator) { + putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr); + m_operator = NoOperator; + } + if (m_invert) { + putTok(tokPtr, TokNot); + m_invert = false; + } + m_state = StCond; + m_canElse = true; +} + +void QMakeParser::bogusTest(ushort *&tokPtr) +{ + flushScopes(tokPtr); + m_operator = NoOperator; + m_invert = false; + m_state = StCond; + m_canElse = true; + m_proFile->setOk(false); +} + +void QMakeParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount) +{ + if (wordCount != 1) { + if (wordCount) { + parseError(fL1S("Extra characters after test expression.")); + bogusTest(tokPtr); + } + return; + } + + // Check for magic tokens + if (*uc == TokHashLiteral) { + uint nlen = uc[3]; + ushort *uce = uc + 4 + nlen; + if (uce == ptr) { + m_tmp.setRawData((QChar *)uc + 4, nlen); + if (!m_tmp.compare(statics.strelse, Qt::CaseInsensitive)) { + if (m_invert || m_operator != NoOperator) { + parseError(fL1S("Unexpected operator in front of else.")); + return; + } + BlockScope &top = m_blockstack.top(); + if (m_canElse && (!top.special || top.braceLevel)) { + // A list of tests (the last one likely with side effects), + // but no assignment, scope, etc. + putTok(tokPtr, TokBranch); + // Put empty then block + putBlockLen(tokPtr, 0); + enterScope(tokPtr, false, StCtrl); + return; + } + forever { + BlockScope &top = m_blockstack.top(); + if (top.inBranch && (!top.special || top.braceLevel)) { + top.inBranch = false; + enterScope(tokPtr, false, StCtrl); + return; + } + if (top.braceLevel || m_blockstack.size() == 1) + break; + leaveScope(tokPtr); + } + parseError(fL1S("Unexpected 'else'.")); + return; + } + } + } + + finalizeTest(tokPtr); + putBlock(tokPtr, uc, ptr - uc); + putTok(tokPtr, TokCondition); +} + +void QMakeParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc) +{ + // Check for magic tokens + if (*uc == TokHashLiteral) { + uint nlen = uc[3]; + ushort *uce = uc + 4 + nlen; + if (*uce == TokTestCall) { + uce++; + m_tmp.setRawData((QChar *)uc + 4, nlen); + const QString *defName; + ushort defType; + uchar nest; + if (m_tmp == statics.strfor) { + if (m_invert || m_operator == OrOperator) { + // '|' could actually work reasonably, but qmake does nonsense here. + parseError(fL1S("Unexpected operator in front of for().")); + bogusTest(tokPtr); + return; + } + flushCond(tokPtr); + putLineMarker(tokPtr); + if (*uce == (TokLiteral|TokNewStr)) { + nlen = uce[1]; + uc = uce + 2 + nlen; + if (*uc == TokFuncTerminator) { + // for(literal) (only "ever" would be legal if qmake was sane) + putTok(tokPtr, TokForLoop); + putHashStr(tokPtr, (ushort *)0, (uint)0); + putBlockLen(tokPtr, 1 + 3 + nlen + 1); + putTok(tokPtr, TokHashLiteral); + putHashStr(tokPtr, uce + 2, nlen); + didFor: + putTok(tokPtr, TokValueTerminator); + enterScope(tokPtr, true, StCtrl); + m_blockstack.top().nest |= NestLoop; + return; + } else if (*uc == TokArgSeparator && argc == 2) { + // for(var, something) + uc++; + putTok(tokPtr, TokForLoop); + putHashStr(tokPtr, uce + 2, nlen); + doFor: + nlen = ptr - uc; + putBlockLen(tokPtr, nlen + 1); + putBlock(tokPtr, uc, nlen); + goto didFor; + } + } else if (argc == 1) { + // for(non-literal) (this wouldn't be here if qmake was sane) + putTok(tokPtr, TokForLoop); + putHashStr(tokPtr, (ushort *)0, (uint)0); + uc = uce; + goto doFor; + } + parseError(fL1S("Syntax is for(var, list), for(var, forever) or for(ever).")); + return; + } else if (m_tmp == statics.strdefineReplace) { + defName = &statics.strdefineReplace; + defType = TokReplaceDef; + goto deffunc; + } else if (m_tmp == statics.strdefineTest) { + defName = &statics.strdefineTest; + defType = TokTestDef; + deffunc: + if (m_invert) { + parseError(fL1S("Unexpected operator in front of function definition.")); + bogusTest(tokPtr); + return; + } + flushScopes(tokPtr); + putLineMarker(tokPtr); + if (*uce == (TokLiteral|TokNewStr)) { + uint nlen = uce[1]; + if (uce[nlen + 2] == TokFuncTerminator) { + if (m_operator != NoOperator) { + putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr); + m_operator = NoOperator; + } + putTok(tokPtr, defType); + putHashStr(tokPtr, uce + 2, nlen); + enterScope(tokPtr, true, StCtrl); + m_blockstack.top().nest = NestFunction; + return; + } + } + parseError(fL1S("%1(function) requires one literal argument.").arg(*defName)); + return; + } else if (m_tmp == statics.strreturn) { + if (argc > 1) { + parseError(fL1S("return() requires zero or one argument.")); + bogusTest(tokPtr); + return; + } + defType = TokReturn; + nest = NestFunction; + goto ctrlstm2; + } else if (m_tmp == statics.strnext) { + defType = TokNext; + goto ctrlstm; + } else if (m_tmp == statics.strbreak) { + defType = TokBreak; + ctrlstm: + if (*uce != TokFuncTerminator) { + parseError(fL1S("%1() requires zero arguments.").arg(m_tmp)); + bogusTest(tokPtr); + return; + } + nest = NestLoop; + ctrlstm2: + if (m_invert) { + parseError(fL1S("Unexpected NOT operator in front of %1().").arg(m_tmp)); + bogusTest(tokPtr); + return; + } + if (!(m_blockstack.top().nest & nest)) { + parseError(fL1S("Unexpected %1().").arg(m_tmp)); + bogusTest(tokPtr); + return; + } + finalizeTest(tokPtr); + putBlock(tokPtr, uce, ptr - uce - 1); // Only for TokReturn + putTok(tokPtr, defType); + return; + } else if (m_tmp == statics.stroption) { + if (m_state != StNew || m_blockstack.top().braceLevel || m_blockstack.size() > 1 + || m_invert || m_operator != NoOperator) { + parseError(fL1S("option() must appear outside any control structures.")); + bogusTest(tokPtr); + return; + } + if (*uce == (TokLiteral|TokNewStr)) { + uint nlen = uce[1]; + if (uce[nlen + 2] == TokFuncTerminator) { + m_tmp.setRawData((QChar *)uce + 2, nlen); + if (m_tmp == statics.strhost_build) { + m_proFile->setHostBuild(true); + } else { + parseError(fL1S("Unknown option() %1.").arg(m_tmp)); + } + return; + } + } + parseError(fL1S("option() requires one literal argument.")); + return; + } + } + } + + finalizeTest(tokPtr); + putBlock(tokPtr, uc, ptr - uc); +} + +bool QMakeParser::resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr, + ushort **buf, QString *xprBuff, + ushort **tokPtr, QString *tokBuff, + const ushort *cur, const QString &in) +{ + QString out; + m_tmp.setRawData((const QChar *)xprPtr, tlen); + if (m_tmp == statics.strLINE) { + out.setNum(m_lineNo); + } else if (m_tmp == statics.strFILE) { + out = m_proFile->fileName(); + // The string is typically longer than the variable reference, so we need + // to ensure that there is enough space in the output buffer - as unlikely + // as an overflow is to actually happen in practice. + int need = (in.length() - (cur - (const ushort *)in.constData()) + 2) * 5 + out.length(); + int tused = *tokPtr - (ushort *)tokBuff->constData(); + int xused; + int total; + bool ptrFinal = xprPtr >= (ushort *)tokBuff->constData() + && xprPtr < (ushort *)tokBuff->constData() + tokBuff->capacity(); + if (ptrFinal) { + xused = xprPtr - (ushort *)tokBuff->constData(); + total = xused + need; + } else { + xused = xprPtr - *buf; + total = tused + xused + need; + } + if (tokBuff->capacity() < total) { + tokBuff->reserve(total); + *tokPtr = (ushort *)tokBuff->constData() + tused; + xprBuff->reserve(total); + *buf = (ushort *)xprBuff->constData(); + xprPtr = (ptrFinal ? (ushort *)tokBuff->constData() : *buf) + xused; + } + } else if (m_tmp == statics.strLITERAL_HASH) { + out = QLatin1String("#"); + } else if (m_tmp == statics.strLITERAL_DOLLAR) { + out = QLatin1String("$"); + } else if (m_tmp == statics.strLITERAL_WHITESPACE) { + out = QLatin1String("\t"); + } else { + return false; + } + xprPtr -= 2; // Was set up for variable reference + xprPtr[-2] = TokLiteral | needSep; + xprPtr[-1] = out.length(); + memcpy(xprPtr, out.constData(), out.length() * 2); + *ptr = xprPtr + out.length(); + return true; +} + +void QMakeParser::message(int type, const QString &msg) const +{ + if (!m_inError && m_handler) + m_handler->message(type, msg, m_proFile->fileName(), m_lineNo); +} + +QT_END_NAMESPACE diff --git a/QMakeFileReader/evaluator/qmakeparser.h b/QMakeFileReader/evaluator/qmakeparser.h new file mode 100644 index 0000000..d9a0223 --- /dev/null +++ b/QMakeFileReader/evaluator/qmakeparser.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAKEPARSER_H +#define QMAKEPARSER_H + +#include "qmake_global.h" +#include "proitems.h" + +#include <qhash.h> +#include <qstack.h> +#ifdef PROPARSER_THREAD_SAFE +# include <qmutex.h> +# include <qwaitcondition.h> +#endif + +QT_BEGIN_NAMESPACE +class QMAKE_EXPORT QMakeParserHandler +{ +public: + enum { + CategoryMask = 0xf00, + WarningMessage = 0x000, + ErrorMessage = 0x100, + + SourceMask = 0xf0, + SourceParser = 0, + + CodeMask = 0xf, + WarnLanguage = 0, + WarnDeprecated, + + ParserWarnLanguage = SourceParser | WarningMessage | WarnLanguage, + ParserWarnDeprecated = SourceParser | WarningMessage | WarnDeprecated, + + ParserIoError = ErrorMessage | SourceParser, + ParserError + }; + virtual void message(int type, const QString &msg, + const QString &fileName = QString(), int lineNo = 0) = 0; +}; + +class ProFileCache; + +class QMAKE_EXPORT QMakeParser +{ +public: + // Call this from a concurrency-free context + static void initialize(); + + QMakeParser(ProFileCache *cache, QMakeParserHandler *handler); + + enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar }; + // fileName is expected to be absolute and cleanPath()ed. + ProFile *parsedProFile(const QString &fileName, bool cache = false); + ProFile *parsedProBlock(const QString &contents, const QString &name, int line = 0, + SubGrammar grammar = FullGrammar); + + void discardFileFromCache(const QString &fileName); + +private: + enum ScopeNesting { + NestNone = 0, + NestLoop = 1, + NestFunction = 2 + }; + + struct BlockScope { + BlockScope() : start(0), braceLevel(0), special(false), inBranch(false), nest(NestNone) {} + BlockScope(const BlockScope &other) { *this = other; } + ushort *start; // Where this block started; store length here + int braceLevel; // Nesting of braces in scope + bool special; // Single-line conditionals inside loops, etc. cannot have else branches + bool inBranch; // The 'else' branch of the previous TokBranch is still open + uchar nest; // Into what control structures we are nested + }; + + enum ScopeState { + StNew, // Fresh scope + StCtrl, // Control statement (for or else) met on current line + StCond // Conditionals met on current line + }; + + enum Context { CtxTest, CtxValue, CtxPureValue, CtxArgs }; + struct ParseCtx { + int parens; // Nesting of non-functional parentheses + int argc; // Number of arguments in current function call + int wordCount; // Number of words in current expression + Context context; + ushort quote; // Enclosing quote type + ushort terminator; // '}' if replace function call is braced, ':' if test function + }; + + bool read(ProFile *pro); + bool read(ProFile *pro, const QString &content, int line, SubGrammar grammar); + + ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok); + ALWAYS_INLINE void putBlockLen(ushort *&tokPtr, uint len); + ALWAYS_INLINE void putBlock(ushort *&tokPtr, const ushort *buf, uint len); + void putHashStr(ushort *&pTokPtr, const ushort *buf, uint len); + void finalizeHashStr(ushort *buf, uint len); + void putLineMarker(ushort *&tokPtr); + ALWAYS_INLINE bool resolveVariable(ushort *xprPtr, int tlen, int needSep, ushort **ptr, + ushort **buf, QString *xprBuff, + ushort **tokPtr, QString *tokBuff, + const ushort *cur, const QString &in); + void finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount); + void finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc); + void finalizeTest(ushort *&tokPtr); + void bogusTest(ushort *&tokPtr); + void enterScope(ushort *&tokPtr, bool special, ScopeState state); + void leaveScope(ushort *&tokPtr); + void flushCond(ushort *&tokPtr); + void flushScopes(ushort *&tokPtr); + + void message(int type, const QString &msg) const; + void parseError(const QString &msg) const + { message(QMakeParserHandler::ParserError, msg); } + void languageWarning(const QString &msg) const + { message(QMakeParserHandler::ParserWarnLanguage, msg); } + void deprecationWarning(const QString &msg) const + { message(QMakeParserHandler::ParserWarnDeprecated, msg); } + + // Current location + ProFile *m_proFile; + int m_lineNo; + + QStack<BlockScope> m_blockstack; + ScopeState m_state; + int m_markLine; // Put marker for this line + bool m_inError; // Current line had a parsing error; suppress followup error messages + bool m_canElse; // Conditionals met on previous line, but no scope was opened + bool m_invert; // Pending conditional is negated + enum { NoOperator, AndOperator, OrOperator } m_operator; // Pending conditional is ORed/ANDed + + QString m_tmp; // Temporary for efficient toQString + + ProFileCache *m_cache; + QMakeParserHandler *m_handler; + + // This doesn't help gcc 3.3 ... + template<typename T> friend class QTypeInfo; + + friend class ProFileCache; +}; + +class QMAKE_EXPORT ProFileCache +{ +public: + ProFileCache() {} + ~ProFileCache(); + + void discardFile(const QString &fileName); + void discardFiles(const QString &prefix); + +private: + struct Entry { + ProFile *pro; +#ifdef PROPARSER_THREAD_SAFE + struct Locker { + Locker() : waiters(0), done(false) {} + QWaitCondition cond; + int waiters; + bool done; + }; + Locker *locker; +#endif + }; + + QHash<QString, Entry> parsed_files; +#ifdef PROPARSER_THREAD_SAFE + QMutex mutex; +#endif + + friend class QMakeParser; +}; + +#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) +Q_DECLARE_TYPEINFO(QMakeParser::BlockScope, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(QMakeParser::Context, Q_PRIMITIVE_TYPE); +#endif + +QT_END_NAMESPACE + +#endif // PROFILEPARSER_H diff --git a/QMakeFileReader/main.cpp b/QMakeFileReader/main.cpp new file mode 100644 index 0000000..a4277d1 --- /dev/null +++ b/QMakeFileReader/main.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmakedataprovider.h" +#include <QCoreApplication> +#include <QStringList> +#include <QFileInfo> +#include <QXmlStreamWriter> + +QString toString(bool b) +{ + return b ? QStringLiteral("true") : QStringLiteral("false"); +} + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + fputs("Usage: qmakefilereader <QtDir> <filePath>\n", stderr); + return -1; + } + + QCoreApplication app(argc, argv); + const QStringList args = app.arguments(); + const QString qtDir = args.at(1); + const QString filePath = QFileInfo(args.at(2)).absoluteFilePath(); + + QMakeDataProvider dataProvider; + dataProvider.setQtDir(qtDir); + if (!dataProvider.readFile(filePath)) + return 1; + + QFile fout; + if (!fout.open(stdout, QFile::WriteOnly)) + return 2; + + QXmlStreamWriter stream(&fout); + stream.setAutoFormatting(true); + stream.writeStartDocument(); + stream.writeStartElement("content"); + stream.writeAttribute("valid", toString(dataProvider.isValid())); + stream.writeAttribute("flat", toString(dataProvider.isFlat())); + stream.writeStartElement("SOURCES"); + foreach (const QString &str, dataProvider.getSourceFiles()) + stream.writeTextElement("file", str); + stream.writeEndElement(); + stream.writeStartElement("HEADERS"); + foreach (const QString &str, dataProvider.getHeaderFiles()) + stream.writeTextElement("file", str); + stream.writeEndElement(); + stream.writeStartElement("RESOURCES"); + foreach (const QString &str, dataProvider.getResourceFiles()) + stream.writeTextElement("file", str); + stream.writeEndElement(); + stream.writeStartElement("FORMS"); + foreach (const QString &str, dataProvider.getFormFiles()) + stream.writeTextElement("file", str); + stream.writeEndElement(); + stream.writeEndElement(); // content + stream.writeEndDocument(); + return 0; +} + diff --git a/QMakeFileReader/qmakedataprovider.cpp b/QMakeFileReader/qmakedataprovider.cpp new file mode 100644 index 0000000..dceb62b --- /dev/null +++ b/QMakeFileReader/qmakedataprovider.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmakedataprovider.h" +#include "evalhandler.h" +#include <qmakeevaluator.h> +#include <qmakeglobals.h> +#include <QtCore/QFileInfo> +#include <QtCore/QList> +#include <QtCore/QPair> + +class QMakeDataProviderPrivate +{ +public: + QStringList m_headerFiles; + QStringList m_sourceFiles; + QStringList m_resourceFiles; + QStringList m_formFiles; + typedef QPair<QStringList *, ProKey> Mapping; + QList<Mapping> m_variableMappings; + bool m_valid; + bool m_flat; + QString m_qtdir; + + QMakeDataProviderPrivate() + { + m_variableMappings + << qMakePair(&m_headerFiles, ProKey("HEADERS")) + << qMakePair(&m_sourceFiles, ProKey("SOURCES")) + << qMakePair(&m_resourceFiles, ProKey("RESOURCES")) + << qMakePair(&m_formFiles, ProKey("FORMS")); + } + + bool readFile(const QString &fileName) + { + QFileInfo fi(fileName); + if (fi.isRelative()) + qWarning("qmakewrapper: expecting an absolute filename."); + + m_headerFiles.clear(); + m_sourceFiles.clear(); + m_resourceFiles.clear(); + m_formFiles.clear(); + m_valid = false; + m_flat = true; + + QMakeGlobals globals; + ProFileCache proFileCache; + EvalHandler handler; + QMakeParser parser(&proFileCache, &handler); + QMakeEvaluator evaluator(&globals, &parser, &handler); + if (evaluator.evaluateFile(fileName, QMakeHandler::EvalProjectFile, + QMakeEvaluator::LoadProOnly) != QMakeEvaluator::ReturnTrue) + { + qWarning("qmakewrapper: failed to parse %s", qPrintable(fileName)); + return false; + } + + m_valid = true; + m_flat = evaluator.isActiveConfig(QStringLiteral("flat")); + + foreach (const Mapping &mapping, m_variableMappings) + *mapping.first = evaluator.values(mapping.second).toQStringList(); + + return true; + } +}; + +QMakeDataProvider::QMakeDataProvider() + : d(new QMakeDataProviderPrivate()) +{ +} + +QMakeDataProvider::~QMakeDataProvider() +{ + delete d; +} + +bool QMakeDataProvider::readFile(const QString &fileName) +{ + return d->readFile(fileName); +} + +void QMakeDataProvider::setQtDir(const QString &qtdir) +{ + d->m_qtdir = qtdir; +} + +QStringList QMakeDataProvider::getFormFiles() const +{ + return d->m_formFiles; +} + +QStringList QMakeDataProvider::getHeaderFiles() const +{ + return d->m_headerFiles; +} + +QStringList QMakeDataProvider::getResourceFiles() const +{ + return d->m_resourceFiles; +} + +QStringList QMakeDataProvider::getSourceFiles() const +{ + return d->m_sourceFiles; +} + +bool QMakeDataProvider::isFlat() const +{ + return d->m_flat; +} + +bool QMakeDataProvider::isValid() const +{ + return d->m_valid; +} diff --git a/QMakeFileReader/qmakedataprovider.h b/QMakeFileReader/qmakedataprovider.h new file mode 100644 index 0000000..b712327 --- /dev/null +++ b/QMakeFileReader/qmakedataprovider.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAKEDATAPROVIDER_H +#define QMAKEDATAPROVIDER_H + +#include <QtCore/QString> +#include <QtCore/QStringList> + +class QMakeDataProviderPrivate; + +class QMakeDataProvider { + + QMakeDataProviderPrivate * const d; + +public: + QMakeDataProvider(); + ~QMakeDataProvider(); + + bool readFile(const QString &fileName); + void setQtDir(const QString &qtdir); + QStringList getFormFiles() const; + QStringList getHeaderFiles() const; + QStringList getResourceFiles() const; + QStringList getSourceFiles() const; + bool isFlat() const; + bool isValid() const; +}; + +#endif // QMAKEDATAPROVIDER_H + diff --git a/QMakeFileReader/qmakefilereader.ico b/QMakeFileReader/qmakefilereader.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/QMakeFileReader/qmakefilereader.ico Binary files differ diff --git a/QMakeFileReader/qmakefilereader.rc b/QMakeFileReader/qmakefilereader.rc new file mode 100644 index 0000000..136fcc1 --- /dev/null +++ b/QMakeFileReader/qmakefilereader.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "qmakefilereader.ico" diff --git a/QMakeFileReader/qmakefilereader.vcxproj b/QMakeFileReader/qmakefilereader.vcxproj new file mode 100644 index 0000000..f887eeb --- /dev/null +++ b/QMakeFileReader/qmakefilereader.vcxproj @@ -0,0 +1,257 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{70711A97-D9B0-3A86-9756-9FF47337908B}</ProjectGuid> + <Keyword>QtVS_v303</Keyword> + <RootNamespace>QMakeFileReader</RootNamespace> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='15.0'">10.0.17763.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='16.0'">10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='17.0'">10.0</WindowsTargetPlatformVersion> + <QtMsBuild>$(SolutionDir)\qtmsbuild\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='15.0'">v141</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='16.0'">v142</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='17.0'">v143</PlatformToolset> + </PropertyGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir> + <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + <Import Project="..\QtCppConfig.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <QtModules>core</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <QtModules>core</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <QtModules>core</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <QtModules>core</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"> + <Import Project="$(QtMsBuild)\qt.props" /> + </ImportGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>evaluator;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <PreprocessToFile>false</PreprocessToFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <OutputFile>$(OutDir)\QMakeFileReader.exe</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>evaluator;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <Optimization>MaxSpeed</Optimization> + <PreprocessToFile>false</PreprocessToFile> + <ProgramDataBaseFileName> + </ProgramDataBaseFileName> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <OutputFile>$(OutDir)\QMakeFileReader.exe</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <LinkIncremental>false</LinkIncremental> + <OptimizeReferences>true</OptimizeReferences> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>evaluator;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <PreprocessToFile>false</PreprocessToFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <OutputFile>$(OutDir)\QMakeFileReader.exe</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>evaluator;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <Optimization>MaxSpeed</Optimization> + <PreprocessToFile>false</PreprocessToFile> + <ProgramDataBaseFileName> + </ProgramDataBaseFileName> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <OutputFile>$(OutDir)\QMakeFileReader.exe</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <LinkIncremental>false</LinkIncremental> + <OptimizeReferences>true</OptimizeReferences> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="evalhandler.cpp" /> + <ClCompile Include="evaluator\ioutils.cpp" /> + <ClCompile Include="evaluator\proitems.cpp" /> + <ClCompile Include="evaluator\qmakebuiltins.cpp" /> + <ClCompile Include="qmakedataprovider.cpp" /> + <ClCompile Include="evaluator\qmakeevaluator.cpp" /> + <ClCompile Include="evaluator\qmakeglobals.cpp" /> + <ClCompile Include="evaluator\qmakeparser.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="evalhandler.h" /> + <ClInclude Include="evaluator\ioutils.h" /> + <ClInclude Include="evaluator\proitems.h" /> + <ClInclude Include="evaluator\qmake_global.h" /> + <ClInclude Include="qmakedataprovider.h" /> + <ClInclude Include="evaluator\qmakeevaluator.h" /> + <ClInclude Include="evaluator\qmakeevaluator_p.h" /> + <ClInclude Include="evaluator\qmakeglobals.h" /> + <ClInclude Include="evaluator\qmakeparser.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="qmakefilereader.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets" /> + <ItemGroup> + <ProjectReference Include="..\qtmsbuild\QtMSBuild.csproj"> + <Project>{A618D28B-9352-44F4-AA71-609BF68BF871}</Project> + </ProjectReference> + </ItemGroup> + <Target Name="qmakeFileReader_PreBuild"> + <ItemGroup> + <ProjectReference Remove="..\qtmsbuild\QtMSBuild.csproj"/> + </ItemGroup> + </Target> + <PropertyGroup> + <ResolveReferencesDependsOn>qmakeFileReader_PreBuild;$(ResolveReferencesDependsOn)</ResolveReferencesDependsOn> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/QMakeFileReader/qmakefilereader.vcxproj.filters b/QMakeFileReader/qmakefilereader.vcxproj.filters new file mode 100644 index 0000000..d193c99 --- /dev/null +++ b/QMakeFileReader/qmakefilereader.vcxproj.filters @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Generated Files"> + <UniqueIdentifier>{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}</UniqueIdentifier> + <Extensions>cpp;c;cxx;moc;h;def;odl;idl;res;</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="evalhandler.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="evaluator\ioutils.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="evaluator\proitems.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="evaluator\qmakebuiltins.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="qmakedataprovider.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="evaluator\qmakeevaluator.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="evaluator\qmakeglobals.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="evaluator\qmakeparser.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="evalhandler.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="evaluator\ioutils.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="evaluator\proitems.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="evaluator\qmake_global.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="qmakedataprovider.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="evaluator\qmakeevaluator.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="evaluator\qmakeevaluator_p.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="evaluator\qmakeglobals.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="evaluator\qmakeparser.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="qmakefilereader.rc" /> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/QrcEditor/main.cpp b/QrcEditor/main.cpp new file mode 100644 index 0000000..31ee285 --- /dev/null +++ b/QrcEditor/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrceditor.h" +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow mw; + mw.show(); + if (argc == 2) + mw.openFile(argv[1]); + return app.exec(); +} diff --git a/QrcEditor/mainwindow.cpp b/QrcEditor/mainwindow.cpp new file mode 100644 index 0000000..2923229 --- /dev/null +++ b/QrcEditor/mainwindow.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "qrceditor.h" + +#include <QAction> +#include <QDebug> +#include <QFileDialog> +#include <QMenuBar> +#include <QStatusBar> +#include <QVBoxLayout> +#include <QMessageBox> +#include <QToolBar> +#include <QProcess> + +#include <windows.h> +#include <Tlhelp32.h> + +MainWindow::MainWindow() : + m_qrcEditor(new SharedTools::QrcEditor()) +{ + m_qrcEditor->setResourceDragEnabled(true); + setWindowTitle(tr("Qt Resource Editor")); + QMenu* fMenu = menuBar()->addMenu(tr("&File")); + QToolBar* tb = new QToolBar("Title", this); + tb->setMovable(false); + addToolBar(Qt::TopToolBarArea, tb); + + QAction* oa = fMenu->addAction(tr("&Open...")); + oa->setShortcut(tr("Ctrl+O", "File|Open")); + oa->setIcon(style()->standardIcon(QStyle::SP_DialogOpenButton)); + tb->addAction(oa); + connect(oa, SIGNAL(triggered()), this, SLOT(slotOpen())); + + QAction* sa = fMenu->addAction(tr("&Save")); + sa->setShortcut(tr("Ctrl+S", "File|Save")); + sa->setIcon(style()->standardIcon(QStyle::SP_DialogSaveButton)); + tb->addAction(sa); + connect(sa, SIGNAL(triggered()), this, SLOT(slotSave())); + + fMenu->addSeparator(); + + QAction* xa = fMenu->addAction(tr("E&xit")); + xa->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton)); + connect(xa, SIGNAL(triggered()), this, SLOT(close())); + + QMenu* hMenu = menuBar()->addMenu(tr("&Help")); + QAction* actionAbout = hMenu->addAction(tr("&About")); + connect(actionAbout, SIGNAL(triggered()), this, SLOT(slotAbout())); + + QAction* actionAboutQt = hMenu->addAction(tr("A&bout Qt")); + connect(actionAboutQt, SIGNAL(triggered()), this, SLOT(slotAboutQt())); + + QWidget *cw = new QWidget(); + setCentralWidget(cw); + QVBoxLayout *lt = new QVBoxLayout(cw); + lt->addWidget(m_qrcEditor); + setMinimumSize(QSize(500, 500)); +} + +void MainWindow::openFile(QString fileName) +{ + if (fileName.isEmpty()) + return; + + if (m_qrcEditor->isDirty()) { + int ret = fileChangedDialog(); + switch (ret) { + case QMessageBox::Yes: + slotSave(); + case QMessageBox::No: + break; + default: + return; + break; + } + } + + if (m_qrcEditor->load(fileName)) { + statusBar()->showMessage(tr("%1 opened").arg(fileName)); + QFileInfo fi(fileName); + setWindowTitle(tr("Qt Resource Editor") + " - " + fi.fileName()); + } + else + statusBar()->showMessage(tr("Unable to open %1.").arg(fileName)); +} + +void MainWindow::slotOpen() +{ + const QString fileName = QFileDialog::getOpenFileName(this, tr("Choose Resource File"), + QString(), + tr("Resource files (*.qrc)")); + this->openFile(fileName); +} + +void MainWindow::slotSave() +{ + const QString oldFileName = m_qrcEditor->fileName(); + QString fileName = oldFileName; + + if (fileName.isEmpty()) { + fileName = QFileDialog::getSaveFileName(this, tr("Save Resource File"), + QString(), + tr("Resource files (*.qrc)")); + if (fileName.isEmpty()) + return; + } + + m_qrcEditor->setFileName(fileName); + if (m_qrcEditor->save()) { + statusBar()->showMessage(tr("%1 written").arg(fileName)); + } else { + statusBar()->showMessage(tr("Unable to write %1.").arg(fileName)); + m_qrcEditor->setFileName(oldFileName); + } +} + +void MainWindow::slotAbout() +{ + QMessageBox::about(this, tr("About Qt Resource Editor"), + tr("Qt Resource Editor") + "\n\n" + tr("Copyright (C) 2016 The Qt Company Ltd.")); +} + +void MainWindow::slotAboutQt() +{ + QMessageBox::aboutQt(this); +} + +void MainWindow::closeEvent(QCloseEvent *e) +{ + if (m_qrcEditor->isDirty()) { + int ret = fileChangedDialog(); + switch (ret) { + case QMessageBox::Yes: + slotSave(); + case QMessageBox::No: + QMainWindow::close(); + break; + default: + e->ignore(); + return; + break; + } + } + e->accept(); +} + +int MainWindow::fileChangedDialog() +{ + QMessageBox message(this); + message.setText(tr("The .qrc file has been modified.")); + message.setWindowTitle("Qt Resource Editor"); + message.setInformativeText(tr("Do you want to save the changes?")); + message.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + message.setDefaultButton(QMessageBox::Yes); + return message.exec(); +} diff --git a/QrcEditor/mainwindow.h b/QrcEditor/mainwindow.h new file mode 100644 index 0000000..18d02f1 --- /dev/null +++ b/QrcEditor/mainwindow.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> +#include <QString> +#include <QCloseEvent> + +namespace SharedTools { + class QrcEditor; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + void openFile(QString fileName); + +protected: + void closeEvent(QCloseEvent *e); + +private slots: + void slotOpen(); + void slotSave(); + void slotAbout(); + void slotAboutQt(); + +private: + int fileChangedDialog(); + +private: + SharedTools::QrcEditor *m_qrcEditor; + QString m_devenvPIDArg; +}; + +#endif // MAINWINDOW_H diff --git a/QrcEditor/qrceditor.ico b/QrcEditor/qrceditor.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/QrcEditor/qrceditor.ico Binary files differ diff --git a/QrcEditor/qrceditor.rc b/QrcEditor/qrceditor.rc new file mode 100644 index 0000000..a4dcdad --- /dev/null +++ b/QrcEditor/qrceditor.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "qrceditor.ico" diff --git a/QrcEditor/qrceditor.vcxproj b/QrcEditor/qrceditor.vcxproj new file mode 100644 index 0000000..de8174c --- /dev/null +++ b/QrcEditor/qrceditor.vcxproj @@ -0,0 +1,253 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}</ProjectGuid> + <Keyword>QtVS_v303</Keyword> + <RootNamespace>QrcEditor</RootNamespace> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='15.0'">10.0.17763.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='16.0'">10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='17.0'">10.0</WindowsTargetPlatformVersion> + <QtMsBuild>$(SolutionDir)\qtmsbuild\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='15.0'">v141</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='16.0'">v142</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='17.0'">v143</PlatformToolset> + </PropertyGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir> + <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + <Import Project="..\QtCppConfig.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <QtModules>core;xml;gui;widgets</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <QtModules>core;xml;gui;widgets</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <QtModules>core;xml;gui;widgets</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <QtModules>core;xml;gui;widgets</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"> + <Import Project="$(QtMsBuild)\qt.props" /> + </ImportGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <PreprocessToFile>false</PreprocessToFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\QrcEditor.exe</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <Optimization>MaxSpeed</Optimization> + <PreprocessToFile>false</PreprocessToFile> + <ProgramDataBaseFileName> + </ProgramDataBaseFileName> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\QrcEditor.exe</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <LinkIncremental>false</LinkIncremental> + <OptimizeReferences>true</OptimizeReferences> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <PreprocessToFile>false</PreprocessToFile> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\QrcEditor.exe</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <AdditionalIncludeDirectories>shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions> + <BrowseInformation>false</BrowseInformation> + <DebugInformationFormat>None</DebugInformationFormat> + <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> + <ExceptionHandling>Sync</ExceptionHandling> + <Optimization>MaxSpeed</Optimization> + <PreprocessToFile>false</PreprocessToFile> + <ProgramDataBaseFileName> + </ProgramDataBaseFileName> + <SuppressStartupBanner>true</SuppressStartupBanner> + <WarningLevel>Level3</WarningLevel> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\QrcEditor.exe</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions> + <DataExecutionPrevention>true</DataExecutionPrevention> + <IgnoreImportLibrary>true</IgnoreImportLibrary> + <LinkIncremental>false</LinkIncremental> + <OptimizeReferences>true</OptimizeReferences> + <RandomizedBaseAddress>true</RandomizedBaseAddress> + <SuppressStartupBanner>true</SuppressStartupBanner> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="mainwindow.cpp" /> + <ClCompile Include="shared\qrceditor.cpp" /> + <ClCompile Include="shared\resourcefile.cpp" /> + <ClCompile Include="shared\resourceview.cpp" /> + <ClCompile Include="shared\undocommands.cpp" /> + </ItemGroup> + <ItemGroup> + <QtMoc Include="mainwindow.h" /> + <QtMoc Include="shared\qrceditor.h" /> + <QtMoc Include="shared\resourcefile_p.h" /> + <QtMoc Include="shared\resourceview.h" /> + <ClInclude Include="shared\undocommands_p.h" /> + </ItemGroup> + <ItemGroup> + <QtUic Include="shared\qrceditor.ui" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="qrceditor.rc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets" /> + <ItemGroup> + <ProjectReference Include="..\qtmsbuild\QtMSBuild.csproj"> + <Project>{A618D28B-9352-44F4-AA71-609BF68BF871}</Project> + </ProjectReference> + </ItemGroup> + <Target Name="QRCEditor_PreBuild"> + <ItemGroup> + <ProjectReference Remove="..\qtmsbuild\QtMSBuild.csproj"/> + </ItemGroup> + </Target> + <PropertyGroup> + <ResolveReferencesDependsOn>QRCEditor_PreBuild;$(ResolveReferencesDependsOn)</ResolveReferencesDependsOn> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/QrcEditor/qrceditor.vcxproj.filters b/QrcEditor/qrceditor.vcxproj.filters new file mode 100644 index 0000000..1199e21 --- /dev/null +++ b/QrcEditor/qrceditor.vcxproj.filters @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + <ParseFiles>false</ParseFiles> + </Filter> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + <ParseFiles>false</ParseFiles> + </Filter> + <Filter Include="Generated Files"> + <UniqueIdentifier>{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}</UniqueIdentifier> + <Extensions>cpp;c;cxx;moc;h;def;odl;idl;res;</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="mainwindow.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="shared\qrceditor.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="shared\resourcefile.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="shared\resourceview.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="shared\undocommands.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <QtMoc Include="mainwindow.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="shared\qrceditor.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="shared\resourcefile_p.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="shared\resourceview.h"> + <Filter>Header Files</Filter> + </QtMoc> + <ClInclude Include="shared\undocommands_p.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <QtUic Include="shared\qrceditor.ui"> + <Filter>Form Files</Filter> + </QtUic> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="qrceditor.rc" /> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/QrcEditor/shared/qrceditor.cpp b/QrcEditor/shared/qrceditor.cpp new file mode 100644 index 0000000..53d4684 --- /dev/null +++ b/QrcEditor/shared/qrceditor.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrceditor.h" +#include "undocommands_p.h" + +#include <QtCore/QDebug> +#include <QtWidgets/QMenu> +#include <QtWidgets/QFileDialog> +#include <QtWidgets/QMessageBox> + +using namespace SharedTools; + +QrcEditor::QrcEditor(QWidget *parent) + : QWidget(parent), + m_treeview(new ResourceView(&m_history)), + m_addFileAction(0) +{ + m_ui.setupUi(this); + QHBoxLayout *layout = new QHBoxLayout; + layout->setSpacing(0); + layout->setMargin(0); + m_ui.centralWidget->setLayout(layout); + + m_treeview->enableContextMenu(false); + layout->addWidget(m_treeview); + connect(m_ui.removeButton, SIGNAL(clicked()), this, SLOT(onRemove())); + + // 'Add' button with menu + QMenu *addMenu = new QMenu(this); + m_addFileAction = addMenu->addAction(tr("Add Files"), this, SLOT(onAddFiles())); + addMenu->addAction(tr("Add Prefix"), this, SLOT(onAddPrefix())); + m_ui.addButton->setMenu(addMenu); + + connect(m_treeview, SIGNAL(addPrefixTriggered()), this, SLOT(onAddPrefix())); + connect(m_treeview, SIGNAL(addFilesTriggered(QString)), this, SLOT(onAddFiles())); + connect(m_treeview, SIGNAL(removeItem()), this, SLOT(onRemove())); + connect(m_treeview, SIGNAL(currentIndexChanged()), this, SLOT(updateCurrent())); + connect(m_treeview, SIGNAL(dirtyChanged(bool)), this, SIGNAL(dirtyChanged(bool))); + m_treeview->setFocus(); + + connect(m_ui.aliasText, SIGNAL(textEdited(QString)), + this, SLOT(onAliasChanged(QString))); + connect(m_ui.prefixText, SIGNAL(textEdited(QString)), + this, SLOT(onPrefixChanged(QString))); + connect(m_ui.languageText, SIGNAL(textEdited(QString)), + this, SLOT(onLanguageChanged(QString))); + + // Prevent undo command merging after a switch of focus: + // (0) The initial text is "Green". + // (1) The user appends " is a color." --> text is "Green is a color." + // (2) The user clicks into some other line edit --> loss of focus + // (3) The user gives focuse again and substitutes "Green" with "Red" + // --> text now is "Red is a color." + // (4) The user hits undo --> text now is "Green is a color." + // Without calling advanceMergeId() it would have been "Green", instead. + connect(m_ui.aliasText, SIGNAL(editingFinished()), + m_treeview, SLOT(advanceMergeId())); + connect(m_ui.prefixText, SIGNAL(editingFinished()), + m_treeview, SLOT(advanceMergeId())); + connect(m_ui.languageText, SIGNAL(editingFinished()), + m_treeview, SLOT(advanceMergeId())); + + connect(m_treeview, SIGNAL(addFilesTriggered(const QString&)), + this, SIGNAL(addFilesTriggered(const QString&))); + + connect(&m_history, SIGNAL(canRedoChanged(bool)), this, SLOT(updateHistoryControls())); + connect(&m_history, SIGNAL(canUndoChanged(bool)), this, SLOT(updateHistoryControls())); + updateHistoryControls(); + updateCurrent(); +} + +QrcEditor::~QrcEditor() +{ +} + +QString QrcEditor::fileName() const +{ + return m_treeview->fileName(); +} + +void QrcEditor::setFileName(const QString &fileName) +{ + m_treeview->setFileName(fileName); +} + +bool QrcEditor::load(const QString &fileName) +{ + const bool success = m_treeview->load(fileName); + if (success) { + // Set "focus" + m_treeview->setCurrentIndex(m_treeview->model()->index(0,0)); + + // Expand prefix nodes + m_treeview->expandAll(); + } + return success; +} + +bool QrcEditor::save() +{ + return m_treeview->save(); +} + +bool QrcEditor::isDirty() +{ + return m_treeview->isDirty(); +} + +void QrcEditor::setDirty(bool dirty) +{ + m_treeview->setDirty(dirty); +} + +// Propagates a change of selection in the tree +// to the alias/prefix/language edit controls +void QrcEditor::updateCurrent() +{ + const bool isValid = m_treeview->currentIndex().isValid(); + const bool isPrefix = m_treeview->isPrefix(m_treeview->currentIndex()) && isValid; + const bool isFile = !isPrefix && isValid; + + m_ui.aliasLabel->setEnabled(isFile); + m_ui.aliasText->setEnabled(isFile); + m_currentAlias = m_treeview->currentAlias(); + m_ui.aliasText->setText(m_currentAlias); + + m_ui.prefixLabel->setEnabled(isPrefix); + m_ui.prefixText->setEnabled(isPrefix); + m_currentPrefix = m_treeview->currentPrefix(); + m_ui.prefixText->setText(m_currentPrefix); + + m_ui.languageLabel->setEnabled(isPrefix); + m_ui.languageText->setEnabled(isPrefix); + m_currentLanguage = m_treeview->currentLanguage(); + m_ui.languageText->setText(m_currentLanguage); + + m_ui.urlLabel->setEnabled(isFile); + m_ui.urlText->setEnabled(isFile); + if (isFile) { + QString url = QLatin1String(":"); + url += m_currentPrefix; + if (!url.endsWith(QLatin1Char('/'))) + url += QLatin1Char('/'); + if (m_currentAlias.isEmpty()) + url += m_treeview->currentIndex().data().toString(); + else + url += m_currentAlias; + m_ui.urlText->setText(url); + } else { + m_ui.urlText->clear(); + } + + m_ui.addButton->setEnabled(true); + m_addFileAction->setEnabled(isValid); + m_ui.removeButton->setEnabled(isValid); +} + +void QrcEditor::updateHistoryControls() +{ + emit undoStackChanged(m_history.canUndo(), m_history.canRedo()); +} + +void QrcEditor::resolveLocationIssues(QStringList &files) +{ + const QDir dir = QFileInfo(m_treeview->fileName()).absoluteDir(); + const QString dotdotSlash = QLatin1String("../"); + int i = 0; + int count = files.count(); + int initialCount = files.count(); + + // Find first troublesome file + for (; i < count; i++) { + QString const &file = files.at(i); + const QString relativePath = dir.relativeFilePath(file); + if (relativePath.startsWith(dotdotSlash)) + break; + } + + // All paths fine -> no interaction needed + if (i == count) { + return; + } + + // Interact with user from now on + bool abort = false; + for (; i < count; i++) { + // Path fine -> skip file + QString const &file = files.at(i); + QString const relativePath = dir.relativeFilePath(file); + if (!relativePath.startsWith(dotdotSlash)) { + continue; + } + + // Path troublesome and aborted -> remove file + if (abort) { + files.removeAt(i); + count--; + i--; + continue; + } else { + // Path troublesome -> query user + QMessageBox message(this); + message.setWindowTitle(tr("Invalid File Location")); + message.setIcon(QMessageBox::Warning); + QPushButton * const copyButton = message.addButton(tr("Copy"), QMessageBox::ActionRole); + QPushButton * skipButton = NULL; + if (initialCount > 1) + { + skipButton = message.addButton(tr("Skip"), QMessageBox::DestructiveRole); + message.setEscapeButton(skipButton); + } + QPushButton * const abortButton = message.addButton(tr("Abort"), QMessageBox::RejectRole); + message.setDefaultButton(copyButton); + message.setText(tr("The file %1 is not in a subdirectory of the resource file. You now have the option to copy this file to a valid location.") + .arg(QDir::toNativeSeparators(file))); + message.exec(); + if (message.clickedButton() == skipButton) { + files.removeAt(i); + count--; + i--; // Compensate i++ + } else if (message.clickedButton() == copyButton) { + const QFileInfo fi(file); + QFileInfo suggestion; + QDir tmpTarget(dir.path() + QString(QDir::separator()) + QString("Resources"));; + if (tmpTarget.exists()) + suggestion.setFile(tmpTarget, fi.fileName()); + else + suggestion.setFile(dir, fi.fileName()); + const QString copyName = QFileDialog::getSaveFileName(this, tr("Choose copy location"), + suggestion.absoluteFilePath()); + if (!copyName.isEmpty()) { + QString relPath = dir.relativeFilePath(copyName); + if (relPath.startsWith(dotdotSlash)) { // directory is still invalid + i--; // Compensate i++ and try again + continue; + } + if (QFile::exists(copyName)) { + if (!QFile::remove(copyName)) { + QMessageBox::critical(this, tr("Overwrite Failed"), + tr("Could not overwrite file %1.") + .arg(QDir::toNativeSeparators(copyName))); + // Remove file + files.removeAt(i); + count--; + i--; // Compensate i++ + continue; + } + } + if (!QFile::copy(file, copyName)) { + QMessageBox::critical(this, tr("Copying Failed"), + tr("Could not copy the file to %1.") + .arg(QDir::toNativeSeparators(copyName))); + // Remove file + files.removeAt(i); + count--; + i--; // Compensate i++ + continue; + } + files[i] = copyName; + } else { + // Remove file + files.removeAt(i); + count--; + i--; // Compensate i++ + } + } else if (message.clickedButton() == abortButton) { + abort = true; + + files.removeAt(i); + count--; + i--; // Compensate i++ + } + } + } +} + +void QrcEditor::setResourceDragEnabled(bool e) +{ + m_treeview->setResourceDragEnabled(e); +} + +bool QrcEditor::resourceDragEnabled() const +{ + return m_treeview->resourceDragEnabled(); +} + +void QrcEditor::setDefaultAddFileEnabled(bool enable) +{ + m_treeview->setDefaultAddFileEnabled(enable); +} + +bool QrcEditor::defaultAddFileEnabled() const +{ + return m_treeview->defaultAddFileEnabled(); +} + +void QrcEditor::addFile(const QString &prefix, const QString &file) +{ + // TODO: make this function UNDO / REDO aware + m_treeview->addFile(prefix, file); +} + +/* +void QrcEditor::removeFile(const QString &prefix, const QString &file) +{ + m_treeview->removeFile(prefix, file); +} +*/ +// Slot for change of line edit content 'alias' +void QrcEditor::onAliasChanged(const QString &alias) +{ + const QString &before = m_currentAlias; + const QString &after = alias; + m_treeview->setCurrentAlias(before, after); + m_currentAlias = alias; + updateCurrent(); + updateHistoryControls(); +} + +// Slot for change of line edit content 'prefix' +void QrcEditor::onPrefixChanged(const QString &prefix) +{ + const QString &before = m_currentPrefix; + const QString &after = prefix; + m_treeview->setCurrentPrefix(before, after); + m_currentPrefix = prefix; + updateCurrent(); + updateHistoryControls(); +} + +// Slot for change of line edit content 'language' +void QrcEditor::onLanguageChanged(const QString &language) +{ + const QString &before = m_currentLanguage; + const QString &after = language; + m_treeview->setCurrentLanguage(before, after); + m_currentLanguage = language; + updateHistoryControls(); +} + +// Slot for 'Remove' button +void QrcEditor::onRemove() +{ + // Find current item, push and execute command + const QModelIndex current = m_treeview->currentIndex(); + int afterDeletionArrayIndex = current.row(); + QModelIndex afterDeletionParent = current.parent(); + m_treeview->findSamePlacePostDeletionModelIndex(afterDeletionArrayIndex, afterDeletionParent); + QUndoCommand * const removeCommand = new RemoveEntryCommand(m_treeview, current); + m_history.push(removeCommand); + const QModelIndex afterDeletionModelIndex + = m_treeview->model()->index(afterDeletionArrayIndex, 0, afterDeletionParent); + m_treeview->setCurrentIndex(afterDeletionModelIndex); + updateHistoryControls(); +} + +// Slot for 'Add File' button +void QrcEditor::onAddFiles() +{ + QModelIndex const current = m_treeview->currentIndex(); + int const currentIsPrefixNode = m_treeview->isPrefix(current); + int const prefixArrayIndex = currentIsPrefixNode ? current.row() + : m_treeview->model()->parent(current).row(); + int const cursorFileArrayIndex = currentIsPrefixNode ? 0 : current.row(); + QStringList fileNames = m_treeview->fileNamesToAdd(); + resolveLocationIssues(fileNames); + if (fileNames.isEmpty()) + return; + QUndoCommand * const addFilesCommand = new AddFilesCommand( + m_treeview, prefixArrayIndex, cursorFileArrayIndex, fileNames); + m_history.push(addFilesCommand); + updateHistoryControls(); +} + +// Slot for 'Add Prefix' button +void QrcEditor::onAddPrefix() +{ + QUndoCommand * const addEmptyPrefixCommand = new AddEmptyPrefixCommand(m_treeview); + m_history.push(addEmptyPrefixCommand); + updateHistoryControls(); +} + +// Slot for 'Undo' button +void QrcEditor::onUndo() +{ + m_history.undo(); + updateCurrent(); + updateHistoryControls(); +} + +// Slot for 'Redo' button +void QrcEditor::onRedo() +{ + m_history.redo(); + updateCurrent(); + updateHistoryControls(); +} diff --git a/QrcEditor/shared/qrceditor.h b/QrcEditor/shared/qrceditor.h new file mode 100644 index 0000000..846607c --- /dev/null +++ b/QrcEditor/shared/qrceditor.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRCEDITOR_H +#define QRCEDITOR_H + +#include "ui_qrceditor.h" +#include "resourceview.h" + +#include <QtWidgets/QWidget> +#include <QtWidgets/QUndoStack> + +namespace SharedTools { + +class QrcEditor : public QWidget +{ + Q_OBJECT + +public: + QrcEditor(QWidget *parent = 0); + virtual ~QrcEditor(); + + bool load(const QString &fileName); + bool save(); + + bool isDirty(); + void setDirty(bool dirty); + + QString fileName() const; + void setFileName(const QString &fileName); + + void setResourceDragEnabled(bool e); + bool resourceDragEnabled() const; + + void setDefaultAddFileEnabled(bool enable); + bool defaultAddFileEnabled() const; + + void addFile(const QString &prefix, const QString &file); +// void removeFile(const QString &prefix, const QString &file); + +signals: + void dirtyChanged(bool dirty); + void addFilesTriggered(const QString &prefix); + +private slots: + void updateCurrent(); + void updateHistoryControls(); + +private: + void resolveLocationIssues(QStringList &files); + +private slots: + void onAliasChanged(const QString &alias); + void onPrefixChanged(const QString &prefix); + void onLanguageChanged(const QString &language); + void onRemove(); + void onAddFiles(); + void onAddPrefix(); + +signals: + void undoStackChanged(bool canUndo, bool canRedo); + +public slots: + void onUndo(); + void onRedo(); + +private: + Ui::QrcEditor m_ui; + QUndoStack m_history; + ResourceView *m_treeview; + QAction *m_addFileAction; + + QString m_currentAlias; + QString m_currentPrefix; + QString m_currentLanguage; +}; + +} + +#endif diff --git a/QrcEditor/shared/qrceditor.pri b/QrcEditor/shared/qrceditor.pri new file mode 100644 index 0000000..25898db --- /dev/null +++ b/QrcEditor/shared/qrceditor.pri @@ -0,0 +1,19 @@ +INCLUDEPATH *= $$PWD +DEPENDPATH *= $$PWD + +QT *= xml + +# Input +SOURCES += \ + $$PWD/resourcefile.cpp \ + $$PWD/resourceview.cpp \ + $$PWD/qrceditor.cpp \ + $$PWD/undocommands.cpp + +HEADERS += \ + $$PWD/resourcefile_p.h \ + $$PWD/resourceview.h \ + $$PWD/qrceditor.h \ + $$PWD/undocommands_p.h + +FORMS += $$PWD/qrceditor.ui diff --git a/QrcEditor/shared/qrceditor.ui b/QrcEditor/shared/qrceditor.ui new file mode 100644 index 0000000..4572f36 --- /dev/null +++ b/QrcEditor/shared/qrceditor.ui @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QrcEditor</class> + <widget class="QWidget" name="QrcEditor"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>491</width> + <height>381</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="margin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="centralWidget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="addButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeButton"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Properties</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinAndMaxSize</enum> + </property> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="aliasLabel"> + <property name="text"> + <string>Alias:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="aliasText"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="prefixLabel"> + <property name="text"> + <string>Prefix:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="prefixText"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="languageLabel"> + <property name="text"> + <string>Language:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="languageText"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="urlLabel"> + <property name="text"> + <string>Resource URL:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="urlText"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/QrcEditor/shared/resourcefile.cpp b/QrcEditor/shared/resourcefile.cpp new file mode 100644 index 0000000..6a3fa7f --- /dev/null +++ b/QrcEditor/shared/resourcefile.cpp @@ -0,0 +1,992 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "resourcefile_p.h" + +#include <QCoreApplication> +#include <QDebug> +#include <QDir> +#include <QFile> +#include <QMimeData> +#include <QtAlgorithms> +#include <QTextStream> + +#include <QIcon> +#include <QImageReader> + +#include <QDomDocument> + +QT_BEGIN_NAMESPACE + + +/* +TRANSLATOR qdesigner_internal::ResourceModel +*/ + +static QString msgFileNameEmpty() +{ + return QCoreApplication::translate("Designer", "The file name is empty."); +} + +namespace qdesigner_internal { + + +/****************************************************************************** +** FileList +*/ + +bool FileList::containsFile(File *file) +{ + foreach (const File *tmpFile, *this) + if (tmpFile->name == file->name && tmpFile->prefix() == file->prefix()) + return true; + return false; +} + +/****************************************************************************** +** ResourceFile +*/ + +ResourceFile::ResourceFile(const QString &file_name) +{ + setFileName(file_name); +} + +ResourceFile::~ResourceFile() +{ + clearPrefixList(); +} + +bool ResourceFile::load() +{ + m_error_message.clear(); + + if (m_file_name.isEmpty()) { + m_error_message = msgFileNameEmpty(); + return false; + } + + QFile file(m_file_name); + if (!file.open(QIODevice::ReadOnly)) { + m_error_message = file.errorString(); + return false; + } + + clearPrefixList(); + + QDomDocument doc; + + QString error_msg; + int error_line, error_col; + if (!doc.setContent(&file, &error_msg, &error_line, &error_col)) { + m_error_message = QCoreApplication::translate("Designer", "XML error on line %1, col %2: %3") + .arg(error_line).arg(error_col).arg(error_msg); + return false; + } + + QDomElement root = doc.firstChildElement(QLatin1String("RCC")); + if (root.isNull()) { + m_error_message = QCoreApplication::translate("Designer", "The <RCC> root element is missing."); + return false; + } + + QDomElement relt = root.firstChildElement(QLatin1String("qresource")); + for (; !relt.isNull(); relt = relt.nextSiblingElement(QLatin1String("qresource"))) { + + QString prefix = fixPrefix(relt.attribute(QLatin1String("prefix"))); + if (prefix.isEmpty()) + prefix = QString(QLatin1Char('/')); + const QString language = relt.attribute(QLatin1String("lang")); + + const int idx = indexOfPrefix(prefix); + Prefix * p = 0; + if (idx == -1) { + p = new Prefix(prefix, language); + m_prefix_list.append(p); + } else { + p = m_prefix_list[idx]; + } + Q_ASSERT(p); + + QDomElement felt = relt.firstChildElement(QLatin1String("file")); + for (; !felt.isNull(); felt = felt.nextSiblingElement(QLatin1String("file"))) { + const QString fileName = absolutePath(felt.text()); + const QString alias = felt.attribute(QLatin1String("alias")); + File * const file = new File(p, fileName, alias); + p->file_list.append(file); + } + } + + return true; +} + +bool ResourceFile::save() +{ + m_error_message.clear(); + + if (m_file_name.isEmpty()) { + m_error_message = msgFileNameEmpty(); + return false; + } + + QFile file(m_file_name); + if (!file.open(QIODevice::WriteOnly)) { + m_error_message = file.errorString(); + return false; + } + + QDomDocument doc; + QDomElement root = doc.createElement(QLatin1String("RCC")); + doc.appendChild(root); + + const QStringList name_list = prefixList(); + + foreach (const QString &name, name_list) { + FileList file_list; + QString lang; + foreach (const Prefix *pref, m_prefix_list) { + if (pref->name == name){ + file_list += pref->file_list; + lang = pref->lang; + } + } + + QDomElement relt = doc.createElement(QLatin1String("qresource")); + root.appendChild(relt); + relt.setAttribute(QLatin1String("prefix"), name); + if (!lang.isEmpty()) + relt.setAttribute(QLatin1String("lang"), lang); + + foreach (const File *f, file_list) { + const File &file = *f; + QDomElement felt = doc.createElement(QLatin1String("file")); + relt.appendChild(felt); + const QString conv_file = relativePath(file.name).replace(QDir::separator(), QLatin1Char('/')); + const QDomText text = doc.createTextNode(conv_file); + felt.appendChild(text); + if (!file.alias.isEmpty()) + felt.setAttribute(QLatin1String("alias"), file.alias); + } + } + + QTextStream stream(&file); + doc.save(stream, 4); + + return true; +} + +bool ResourceFile::split(const QString &_path, QString *prefix, QString *file) const +{ + prefix->clear(); + file->clear(); + + QString path = _path; + if (!path.startsWith(QLatin1Char(':'))) + return false; + path = path.mid(1); + + for (int i = 0; i < m_prefix_list.size(); ++i) { + Prefix const * const &pref = m_prefix_list.at(i); + if (!path.startsWith(pref->name)) + continue; + + *prefix = pref->name; + if (pref->name == QString(QLatin1Char('/'))) + *file = path.mid(1); + else + *file = path.mid(pref->name.size() + 1); + + const QString filePath = absolutePath(*file); + + for (int j = 0; j < pref->file_list.count(); j++) { + File const * const &f = pref->file_list.at(j); + if (!f->alias.isEmpty()) { + if (absolutePath(f->alias) == filePath) { + *file = f->name; + return true; + } + } else if (f->name == filePath) + return true; + } + } + + return false; +} + +QString ResourceFile::resolvePath(const QString &path) const +{ + QString prefix, file; + if (split(path, &prefix, &file)) + return absolutePath(file); + + return QString(); +} + +QStringList ResourceFile::prefixList() const +{ + QStringList result; + for (int i = 0; i < m_prefix_list.size(); ++i) + result.append(m_prefix_list.at(i)->name); + return result; +} + +bool ResourceFile::isEmpty() const +{ + return m_file_name.isEmpty() && m_prefix_list.isEmpty(); +} + +QStringList ResourceFile::fileList(int pref_idx) const +{ + QStringList result; + Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count()); + const FileList &abs_file_list = m_prefix_list.at(pref_idx)->file_list; + foreach (const File *abs_file, abs_file_list) + result.append(relativePath(abs_file->name)); + return result; +} + +void ResourceFile::addFile(int prefix_idx, const QString &file, int file_idx) +{ + Prefix * const p = m_prefix_list[prefix_idx]; + Q_ASSERT(p); + FileList &files = p->file_list; + Q_ASSERT(file_idx >= -1 && file_idx <= files.size()); + if (file_idx == -1) + file_idx = files.size(); + files.insert(file_idx, new File(p, absolutePath(file))); +} + +void ResourceFile::addPrefix(const QString &prefix, int prefix_idx) +{ + QString fixed_prefix = fixPrefix(prefix); + if (indexOfPrefix(fixed_prefix) != -1) + return; + + Q_ASSERT(prefix_idx >= -1 && prefix_idx <= m_prefix_list.size()); + if (prefix_idx == -1) + prefix_idx = m_prefix_list.size(); + m_prefix_list.insert(prefix_idx, new Prefix(fixed_prefix)); +} + +void ResourceFile::removePrefix(int prefix_idx) +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + Prefix * const p = m_prefix_list.at(prefix_idx); + delete p; + m_prefix_list.removeAt(prefix_idx); +} + +void ResourceFile::removeFile(int prefix_idx, int file_idx) +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + FileList &fileList = m_prefix_list[prefix_idx]->file_list; + Q_ASSERT(file_idx >= 0 && file_idx < fileList.count()); + delete fileList.at(file_idx); + fileList.removeAt(file_idx); +} + +void ResourceFile::replacePrefix(int prefix_idx, const QString &prefix) +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + m_prefix_list[prefix_idx]->name = fixPrefix(prefix); +} + +void ResourceFile::replaceLang(int prefix_idx, const QString &lang) +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + m_prefix_list[prefix_idx]->lang = lang; +} + +void ResourceFile::replaceAlias(int prefix_idx, int file_idx, const QString &alias) +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + FileList &fileList = m_prefix_list.at(prefix_idx)->file_list; + Q_ASSERT(file_idx >= 0 && file_idx < fileList.count()); + fileList[file_idx]->alias = alias; +} + + +void ResourceFile::replaceFile(int pref_idx, int file_idx, const QString &file) +{ + Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count()); + FileList &fileList = m_prefix_list.at(pref_idx)->file_list; + Q_ASSERT(file_idx >= 0 && file_idx < fileList.count()); + fileList[file_idx]->name = file; +} + +int ResourceFile::indexOfPrefix(const QString &prefix) const +{ + QString fixed_prefix = fixPrefix(prefix); + for (int i = 0; i < m_prefix_list.size(); ++i) { + if (m_prefix_list.at(i)->name == fixed_prefix) + return i; + } + return -1; +} + +int ResourceFile::indexOfFile(int pref_idx, const QString &file) const +{ + Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count()); + Prefix * const p = m_prefix_list.at(pref_idx); + File equalFile(p, absolutePath(file)); + return p->file_list.indexOf(&equalFile); +} + +QString ResourceFile::relativePath(const QString &abs_path) const +{ + if (m_file_name.isEmpty() || QFileInfo(abs_path).isRelative()) + return abs_path; + + QFileInfo fileInfo(m_file_name); + return fileInfo.absoluteDir().relativeFilePath(abs_path); +} + +QString ResourceFile::absolutePath(const QString &rel_path) const +{ + const QFileInfo fi(rel_path); + if (fi.isAbsolute()) + return rel_path; + + QString rc = QFileInfo(m_file_name).path(); + rc += QDir::separator(); + rc += rel_path; + return QDir::cleanPath(rc); +} + +bool ResourceFile::contains(const QString &prefix, const QString &file) const +{ + int pref_idx = indexOfPrefix(prefix); + if (pref_idx == -1) + return false; + if (file.isEmpty()) + return true; + Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count()); + Prefix * const p = m_prefix_list.at(pref_idx); + Q_ASSERT(p); + File equalFile(p, absolutePath(file)); + return p->file_list.containsFile(&equalFile); +} + +bool ResourceFile::contains(int pref_idx, const QString &file) const +{ + Q_ASSERT(pref_idx >= 0 && pref_idx < m_prefix_list.count()); + Prefix * const p = m_prefix_list.at(pref_idx); + File equalFile(p, absolutePath(file)); + return p->file_list.containsFile(&equalFile); +} + +/*static*/ QString ResourceFile::fixPrefix(const QString &prefix) +{ + const QChar slash = QLatin1Char('/'); + QString result = QString(slash); + for (int i = 0; i < prefix.size(); ++i) { + const QChar c = prefix.at(i); + if (c == slash && result.at(result.size() - 1) == slash) + continue; + result.append(c); + } + + if (result.size() > 1 && result.endsWith(slash)) + result = result.mid(0, result.size() - 1); + + return result; +} + +int ResourceFile::prefixCount() const +{ + return m_prefix_list.size(); +} + +QString ResourceFile::prefix(int idx) const +{ + Q_ASSERT((idx >= 0) && (idx < m_prefix_list.count())); + return m_prefix_list.at(idx)->name; +} + +QString ResourceFile::lang(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < m_prefix_list.count()); + return m_prefix_list.at(idx)->lang; +} + +int ResourceFile::fileCount(int prefix_idx) const +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + return m_prefix_list.at(prefix_idx)->file_list.size(); +} + +QString ResourceFile::file(int prefix_idx, int file_idx) const +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + FileList &fileList = m_prefix_list.at(prefix_idx)->file_list; + Q_ASSERT(file_idx >= 0 && file_idx < fileList.count()); + return fileList.at(file_idx)->name; +} + +QString ResourceFile::alias(int prefix_idx, int file_idx) const +{ + Q_ASSERT(prefix_idx >= 0 && prefix_idx < m_prefix_list.count()); + FileList &fileList = m_prefix_list.at(prefix_idx)->file_list; + Q_ASSERT(file_idx >= 0 && file_idx < fileList.count()); + return fileList.at(file_idx)->alias; +} + +void * ResourceFile::prefixPointer(int prefixIndex) const +{ + Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_prefix_list.count()); + return m_prefix_list.at(prefixIndex); +} + +void * ResourceFile::filePointer(int prefixIndex, int fileIndex) const +{ + Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_prefix_list.count()); + FileList &fileList = m_prefix_list.at(prefixIndex)->file_list; + Q_ASSERT(fileIndex >= 0 && fileIndex < fileList.count()); + return fileList.at(fileIndex); +} + +int ResourceFile::prefixPointerIndex(const Prefix *prefix) const +{ + int const count = m_prefix_list.count(); + for (int i = 0; i < count; i++) { + Prefix * const other = m_prefix_list.at(i); + if (*other == *prefix) + return i; + } + return -1; +} + +void ResourceFile::clearPrefixList() +{ + qDeleteAll(m_prefix_list); + m_prefix_list.clear(); +} + +/****************************************************************************** +** ResourceModel +*/ + +ResourceModel::ResourceModel(const ResourceFile &resource_file, QObject *parent) + : QAbstractItemModel(parent), m_resource_file(resource_file), m_dirty(false) +{ +} + +void ResourceModel::setDirty(bool b) +{ + if (b == m_dirty) + return; + + m_dirty = b; + emit dirtyChanged(b); +} + +QModelIndex ResourceModel::index(int row, int column, const QModelIndex &parent) const +{ + if (column != 0) + return QModelIndex(); + + void * internalPointer = 0; + if (parent.isValid()) { + void * const pip = parent.internalPointer(); + if (pip == 0) + return QModelIndex(); + + // File node + Node * const node = reinterpret_cast<Node *>(pip); + Prefix * const prefix = node->prefix(); + Q_ASSERT(prefix); + if (row < 0 || row >= prefix->file_list.count()) + return QModelIndex(); + const int prefixIndex = m_resource_file.prefixPointerIndex(prefix); + const int fileIndex = row; + internalPointer = m_resource_file.filePointer(prefixIndex, fileIndex); + } else { + // Prefix node + if (row < 0 || row >= m_resource_file.prefixCount()) + return QModelIndex(); + internalPointer = m_resource_file.prefixPointer(row); + } + Q_ASSERT(internalPointer); + return createIndex(row, 0, internalPointer); +} + +QModelIndex ResourceModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + void * const internalPointer = index.internalPointer(); + if (internalPointer == 0) + return QModelIndex(); + Node * const node = reinterpret_cast<Node *>(internalPointer); + Prefix * const prefix = node->prefix(); + Q_ASSERT(prefix); + bool const isFileNode = (prefix != node); + + if (isFileNode) { + const int row = m_resource_file.prefixPointerIndex(prefix); + Q_ASSERT(row >= 0); + return createIndex(row, 0, prefix); + } else { + return QModelIndex(); + } +} + +int ResourceModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + void * const internalPointer = parent.internalPointer(); + Node * const node = reinterpret_cast<Node *>(internalPointer); + Prefix * const prefix = node->prefix(); + Q_ASSERT(prefix); + bool const isFileNode = (prefix != node); + + if (isFileNode) { + return 0; + } else { + return prefix->file_list.count(); + } + } else { + return m_resource_file.prefixCount(); + } +} + +int ResourceModel::columnCount(const QModelIndex &) const +{ + return 1; +} + +bool ResourceModel::hasChildren(const QModelIndex &parent) const +{ + return rowCount(parent) != 0; +} + +Qt::DropActions ResourceModel::supportedDropActions() const +{ + // Only action that works for QListWidget and the like. + return Qt::CopyAction; +} + +bool ResourceModel::iconFileExtension(const QString &path) +{ + static QStringList ext_list; + if (ext_list.isEmpty()) { + const QList<QByteArray> _ext_list = QImageReader::supportedImageFormats(); + foreach (const QByteArray &ext, _ext_list) { + QString dotExt = QString(QLatin1Char('.')); + dotExt += QString::fromLocal8Bit(ext); + ext_list.append(dotExt); + } + } + + foreach (const QString &ext, ext_list) { + if (path.endsWith(ext, Qt::CaseInsensitive)) + return true; + } + + return false; +} + +static inline void appendParenthesized(const QString &what, QString &s) +{ + s += QLatin1String(" ("); + s += what; + s += QLatin1Char(')'); +} + +QVariant ResourceModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + const void *internalPointer = index.internalPointer(); + const Node *node = reinterpret_cast<const Node *>(internalPointer); + const Prefix *prefix = node->prefix(); + File *file = node->file(); + Q_ASSERT(prefix); + const bool isFileNode = (prefix != node); + + QVariant result; + + switch (role) { + case Qt::DisplayRole: + { + QString stringRes; + if (!isFileNode) { + // Prefix node + stringRes = prefix->name; + const QString &lang = prefix->lang; + if (!lang.isEmpty()) + appendParenthesized(lang, stringRes); + } else { + // File node + Q_ASSERT(file); + QString conv_file = m_resource_file.relativePath(file->name); + stringRes = conv_file.replace(QDir::separator(), QLatin1Char('/')); + const QString alias = file->alias; + if (!alias.isEmpty()) + appendParenthesized(alias, stringRes); + } + result = stringRes; + } + break; + case Qt::DecorationRole: + if (isFileNode) { + // File node + Q_ASSERT(file); + if (file->icon.isNull()) { + const QString path = m_resource_file.absolutePath(file->name); + if (iconFileExtension(path)) + file->icon = QIcon(path); + } + if (!file->icon.isNull()) + result = file->icon; + } + break; + default: + break; + } + return result; +} + +void ResourceModel::getItem(const QModelIndex &index, QString &prefix, QString &file) const +{ + prefix.clear(); + file.clear(); + + if (!index.isValid()) + return; + + const void *internalPointer = index.internalPointer(); + const Node *node = reinterpret_cast<const Node *>(internalPointer); + const Prefix *p = node->prefix(); + Q_ASSERT(p); + const bool isFileNode = (p != node); + + if (isFileNode) { + const File *f = node->file(); + Q_ASSERT(f); + if (!f->alias.isEmpty()) + file = f->alias; + else + file = f->name; + } else { + prefix = p->name; + } +} + +QString ResourceModel::lang(const QModelIndex &index) const +{ + if (!index.isValid()) + return QString(); + + return m_resource_file.lang(index.row()); +} + +QString ResourceModel::alias(const QModelIndex &index) const +{ + if (!index.isValid() || !index.parent().isValid()) + return QString(); + return m_resource_file.alias(index.parent().row(), index.row()); +} + +QString ResourceModel::file(const QModelIndex &index) const +{ + if (!index.isValid() || !index.parent().isValid()) + return QString(); + return m_resource_file.file(index.parent().row(), index.row()); +} + +QModelIndex ResourceModel::getIndex(const QString &prefixed_file) +{ + QString prefix, file; + if (!m_resource_file.split(prefixed_file, &prefix, &file)) + return QModelIndex(); + return getIndex(prefix, file); +} + +QModelIndex ResourceModel::getIndex(const QString &prefix, const QString &file) +{ + if (prefix.isEmpty()) + return QModelIndex(); + + const int pref_idx = m_resource_file.indexOfPrefix(prefix); + if (pref_idx == -1) + return QModelIndex(); + + const QModelIndex pref_model_idx = index(pref_idx, 0, QModelIndex()); + if (file.isEmpty()) + return pref_model_idx; + + const int file_idx = m_resource_file.indexOfFile(pref_idx, file); + if (file_idx == -1) + return QModelIndex(); + + return index(file_idx, 0, pref_model_idx); +} + +QModelIndex ResourceModel::prefixIndex(const QModelIndex &sel_idx) const +{ + if (!sel_idx.isValid()) + return QModelIndex(); + const QModelIndex parentIndex = parent(sel_idx); + return parentIndex.isValid() ? parentIndex : sel_idx; +} + +QModelIndex ResourceModel::addNewPrefix() +{ + const QString format = QLatin1String("/new/prefix%1"); + int i = 1; + QString prefix = format.arg(i); + for ( ; m_resource_file.contains(prefix); i++) + prefix = format.arg(i); + + i = rowCount(QModelIndex()); + beginInsertRows(QModelIndex(), i, i); + m_resource_file.addPrefix(prefix); + endInsertRows(); + + setDirty(true); + + return index(i, 0, QModelIndex()); +} + +QModelIndex ResourceModel::addFiles(const QModelIndex &model_idx, const QStringList &file_list) +{ + const QModelIndex prefixModelIndex = prefixIndex(model_idx); + const int prefixArrayIndex = prefixModelIndex.row(); + const int cursorFileArrayIndex = (prefixModelIndex == model_idx) ? 0 : model_idx.row(); + int dummy; + int lastFileArrayIndex; + addFiles(prefixArrayIndex, file_list, cursorFileArrayIndex, dummy, lastFileArrayIndex); + return index(lastFileArrayIndex, 0, prefixModelIndex); +} + +void ResourceModel::addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile, + int &firstFile, int &lastFile) +{ + Q_UNUSED(cursorFile) + const QModelIndex prefix_model_idx = index(prefixIndex, 0, QModelIndex()); + const QStringList &file_list = fileNames; + firstFile = -1; + lastFile = -1; + + if (!prefix_model_idx.isValid()) { + return; + } + const int prefix_idx = prefixIndex; + + QStringList unique_list; + foreach (const QString &file, file_list) { + if (!m_resource_file.contains(prefix_idx, file) && !unique_list.contains(file)) + unique_list.append(file); + } + + if (unique_list.isEmpty()) { + return; + } + const int cnt = m_resource_file.fileCount(prefix_idx); + beginInsertRows(prefix_model_idx, cnt, cnt + unique_list.count() - 1); // ### FIXME + + foreach (const QString &file, unique_list) + m_resource_file.addFile(prefix_idx, file); + + const QFileInfo fi(file_list.last()); + m_lastResourceDir = fi.absolutePath(); + + endInsertRows(); + setDirty(true); + + firstFile = cnt; + lastFile = cnt + unique_list.count() - 1; +} + + +void ResourceModel::insertPrefix(int prefixIndex, const QString &prefix, + const QString &lang) +{ + beginInsertRows(QModelIndex(), prefixIndex, prefixIndex); + m_resource_file.addPrefix(prefix, prefixIndex); + m_resource_file.replaceLang(prefixIndex, lang); + endInsertRows(); + setDirty(true); +} + +void ResourceModel::insertFile(int prefixIndex, int fileIndex, + const QString &fileName, const QString &alias) +{ + const QModelIndex parent = index(prefixIndex, 0, QModelIndex()); + beginInsertRows(parent, fileIndex, fileIndex); + m_resource_file.addFile(prefixIndex, fileName, fileIndex); + m_resource_file.replaceAlias(prefixIndex, fileIndex, alias); + endInsertRows(); + setDirty(true); +} + +void ResourceModel::changePrefix(const QModelIndex &model_idx, const QString &prefix) +{ + if (!model_idx.isValid()) + return; + + const QModelIndex prefix_model_idx = prefixIndex(model_idx); + const int prefix_idx = model_idx.row(); + if (m_resource_file.prefix(prefix_idx) == ResourceFile::fixPrefix(prefix)) + return; + + if (m_resource_file.contains(prefix)) + return; + + m_resource_file.replacePrefix(prefix_idx, prefix); + emit dataChanged(prefix_model_idx, prefix_model_idx); + setDirty(true); +} + +void ResourceModel::changeLang(const QModelIndex &model_idx, const QString &lang) +{ + if (!model_idx.isValid()) + return; + + const QModelIndex prefix_model_idx = prefixIndex(model_idx); + const int prefix_idx = model_idx.row(); + if (m_resource_file.lang(prefix_idx) == lang) + return; + + m_resource_file.replaceLang(prefix_idx, lang); + emit dataChanged(prefix_model_idx, prefix_model_idx); + setDirty(true); +} + +void ResourceModel::changeAlias(const QModelIndex &index, const QString &alias) +{ + if (!index.parent().isValid()) + return; + + if (m_resource_file.alias(index.parent().row(), index.row()) == alias) + return; + m_resource_file.replaceAlias(index.parent().row(), index.row(), alias); + emit dataChanged(index, index); + setDirty(true); +} + +QModelIndex ResourceModel::deleteItem(const QModelIndex &idx) +{ + if (!idx.isValid()) + return QModelIndex(); + + QString dummy, file; + getItem(idx, dummy, file); + int prefix_idx = -1; + int file_idx = -1; + + beginRemoveRows(parent(idx), idx.row(), idx.row()); + if (file.isEmpty()) { + // Remove prefix + prefix_idx = idx.row(); + m_resource_file.removePrefix(prefix_idx); + if (prefix_idx == m_resource_file.prefixCount()) + --prefix_idx; + } else { + // Remove file + prefix_idx = prefixIndex(idx).row(); + file_idx = idx.row(); + m_resource_file.removeFile(prefix_idx, file_idx); + if (file_idx == m_resource_file.fileCount(prefix_idx)) + --file_idx; + } + endRemoveRows(); + + setDirty(true); + + if (prefix_idx == -1) + return QModelIndex(); + const QModelIndex prefix_model_idx = index(prefix_idx, 0, QModelIndex()); + if (file_idx == -1) + return prefix_model_idx; + return index(file_idx, 0, prefix_model_idx); +} + +bool ResourceModel::reload() +{ + beginResetModel(); + const bool result = m_resource_file.load(); + if (result) + setDirty(false); + endResetModel(); + return result; +} + +bool ResourceModel::save() +{ + const bool result = m_resource_file.save(); + if (result) + setDirty(false); + return result; +} + +QString ResourceModel::lastResourceOpenDirectory() const +{ + if (m_lastResourceDir.isEmpty()) + return absolutePath(QString()); + return m_lastResourceDir; +} + +// Create a resource path 'prefix:/file' +QString ResourceModel::resourcePath(const QString &prefix, const QString &file) +{ + QString rc = QString(QLatin1Char(':')); + rc += prefix; + rc += QLatin1Char('/'); + rc += file; + return QDir::cleanPath(rc); +} + +QMimeData *ResourceModel::mimeData(const QModelIndexList &indexes) const +{ + if (indexes.size() != 1) + return 0; + + QString prefix, file; + getItem(indexes.front(), prefix, file); + if (prefix.isEmpty() || file.isEmpty()) + return 0; + + // DnD format of Designer 4.4 + QDomDocument doc; + QDomElement elem = doc.createElement(QLatin1String("resource")); + elem.setAttribute(QLatin1String("type"), QLatin1String("image")); + elem.setAttribute(QLatin1String("file"), resourcePath(prefix, file)); + doc.appendChild(elem); + + QMimeData *rc = new QMimeData; + rc->setText(doc.toString()); + return rc; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/QrcEditor/shared/resourcefile_p.h b/QrcEditor/shared/resourcefile_p.h new file mode 100644 index 0000000..2870a6b --- /dev/null +++ b/QrcEditor/shared/resourcefile_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RESOURCEFILE_P_H +#define RESOURCEFILE_P_H + +#include <QtCore/QAbstractItemModel> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtGui/QIcon> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +struct File; +struct Prefix; + +/*! + \class Node + + Forms the base class for nodes in a \l ResourceFile tree. +*/ +class Node +{ +protected: + Node(File *file, Prefix *prefix) : m_file(file), m_prefix(prefix) + { + Q_ASSERT(m_prefix); + } +public: + File *file() const { return m_file; } + Prefix *prefix() const { return m_prefix; } +private: + File *m_file; + Prefix *m_prefix; +}; + +/*! + \class File + + Represents a file node in a \l ResourceFile tree. +*/ +struct File : public Node { + File(Prefix *prefix, const QString &_name = QString(), const QString &_alias = QString()) + : Node(this, prefix), name(_name), alias(_alias) {} + bool operator < (const File &other) const { return name < other.name; } + bool operator == (const File &other) const { return name == other.name; } + bool operator != (const File &other) const { return name != other.name; } + QString name; + QString alias; + QIcon icon; +}; + +class FileList : public QList<File *> +{ +public: + bool containsFile(File *file); +}; + +/*! + \class Prefix + + Represents a prefix node in a \l ResourceFile tree. +*/ +struct Prefix : public Node +{ + Prefix(const QString &_name = QString(), const QString &_lang = QString(), const FileList &_file_list = FileList()) + : Node(NULL, this), name(_name), lang(_lang), file_list(_file_list) {} + ~Prefix() + { + qDeleteAll(file_list); + file_list.clear(); + } + bool operator == (const Prefix &other) const { return (name == other.name) && (lang == other.lang); } + QString name; + QString lang; + FileList file_list; +}; +typedef QList<Prefix *> PrefixList; + +/*! + \class ResourceFile + + Represents the structure of a Qt Resource File (.qrc) file. +*/ +class ResourceFile +{ +public: + ResourceFile(const QString &file_name = QString()); + ~ResourceFile(); + + void setFileName(const QString &file_name) { m_file_name = file_name; } + QString fileName() const { return m_file_name; } + bool load(); + bool save(); + QString errorMessage() const { return m_error_message; } + +private: + QString resolvePath(const QString &path) const; + QStringList prefixList() const; + QStringList fileList(int pref_idx) const; + +public: + int prefixCount() const; + QString prefix(int idx) const; + QString lang(int idx) const; + + int fileCount(int prefix_idx) const; + + QString file(int prefix_idx, int file_idx) const; + QString alias(int prefix_idx, int file_idx) const; + + void addFile(int prefix_idx, const QString &file, int file_idx = -1); + void addPrefix(const QString &prefix, int prefix_idx = -1); + + void removePrefix(int prefix_idx); + void removeFile(int prefix_idx, int file_idx); + + void replacePrefix(int prefix_idx, const QString &prefix); + void replaceLang(int prefix_idx, const QString &lang); + void replaceAlias(int prefix_idx, int file_idx, const QString &alias); + +private: + void replaceFile(int pref_idx, int file_idx, const QString &file); +public: + int indexOfPrefix(const QString &prefix) const; + int indexOfFile(int pref_idx, const QString &file) const; + + bool contains(const QString &prefix, const QString &file = QString()) const; + bool contains(int pref_idx, const QString &file) const; + + QString relativePath(const QString &abs_path) const; + QString absolutePath(const QString &rel_path) const; + + static QString fixPrefix(const QString &prefix); + bool split(const QString &path, QString *prefix, QString *file) const; + +private: + bool isEmpty() const; + +private: + PrefixList m_prefix_list; + QString m_file_name; + QString m_error_message; + +public: + void * prefixPointer(int prefixIndex) const; + void * filePointer(int prefixIndex, int fileIndex) const; + int prefixPointerIndex(const Prefix *prefix) const; + +private: + void clearPrefixList(); +}; + +/*! + \class ResourceModel + + Wraps a \l ResourceFile as a single-column tree model. +*/ +class ResourceModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + ResourceModel(const ResourceFile &resource_file, QObject *parent = 0); + + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + bool hasChildren(const QModelIndex &parent) const; + Qt::DropActions supportedDropActions() const; + +protected: + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +public: + QString fileName() const { return m_resource_file.fileName(); } + void setFileName(const QString &file_name) { m_resource_file.setFileName(file_name); } + void getItem(const QModelIndex &index, QString &prefix, QString &file) const; + + QString lang(const QModelIndex &index) const; + QString alias(const QModelIndex &index) const; + QString file(const QModelIndex &index) const; + + virtual QModelIndex addNewPrefix(); + virtual QModelIndex addFiles(const QModelIndex &idx, const QStringList &file_list); + void addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile, int &firstFile, int &lastFile); + void insertPrefix(int prefixIndex, const QString &prefix, const QString &lang); + void insertFile(int prefixIndex, int fileIndex, const QString &fileName, const QString &alias); + virtual void changePrefix(const QModelIndex &idx, const QString &prefix); + virtual void changeLang(const QModelIndex &idx, const QString &lang); + virtual void changeAlias(const QModelIndex &idx, const QString &alias); + virtual QModelIndex deleteItem(const QModelIndex &idx); + QModelIndex getIndex(const QString &prefix, const QString &file); + QModelIndex getIndex(const QString &prefixed_file); + QModelIndex prefixIndex(const QModelIndex &sel_idx) const; + + QString absolutePath(const QString &path) const + { return m_resource_file.absolutePath(path); } + +private: + QString relativePath(const QString &path) const + { return m_resource_file.relativePath(path); } + QString lastResourceOpenDirectory() const; + +public: + virtual bool reload(); + virtual bool save(); + // QString errorMessage() const { return m_resource_file.errorMessage(); } + + bool dirty() const { return m_dirty; } + void setDirty(bool b); + +private: + virtual QMimeData *mimeData (const QModelIndexList & indexes) const; + + static bool iconFileExtension(const QString &path); + static QString resourcePath(const QString &prefix, const QString &file); + +signals: + void dirtyChanged(bool b); + +private: + ResourceFile m_resource_file; + bool m_dirty; + QString m_lastResourceDir; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // RESOURCEFILE_P_H diff --git a/QrcEditor/shared/resourceview.cpp b/QrcEditor/shared/resourceview.cpp new file mode 100644 index 0000000..c9ce293 --- /dev/null +++ b/QrcEditor/shared/resourceview.cpp @@ -0,0 +1,663 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "resourceview.h" + +#include "undocommands_p.h" + +#include <QtCore/QDebug> + +#include <QtWidgets/QAction> +#include <QtWidgets/QApplication> +#include <QtWidgets/QFileDialog> +#include <QtWidgets/QHeaderView> +#include <QtWidgets/QInputDialog> +#include <QtWidgets/QMenu> +#include <QtGui/QMouseEvent> +#include <QtWidgets/QUndoStack> + +namespace SharedTools { + +/*! + \class FileEntryBackup + + Backups a file node. +*/ +class FileEntryBackup : public EntryBackup +{ +private: + int m_fileIndex; + QString m_alias; + +public: + FileEntryBackup(ResourceModel &model, int prefixIndex, int fileIndex, + const QString &fileName, const QString &alias) + : EntryBackup(model, prefixIndex, fileName), m_fileIndex(fileIndex), + m_alias(alias) { } + void restore() const; +}; + +void FileEntryBackup::restore() const +{ + m_model->insertFile(m_prefixIndex, m_fileIndex, m_name, m_alias); +} + +/*! + \class PrefixEntryBackup + + Backups a prefix node including children. +*/ +class PrefixEntryBackup : public EntryBackup +{ +private: + QString m_language; + QList<FileEntryBackup> m_files; + +public: + PrefixEntryBackup(ResourceModel &model, int prefixIndex, const QString &prefix, + const QString &language, const QList<FileEntryBackup> &files) + : EntryBackup(model, prefixIndex, prefix), m_language(language), m_files(files) { } + void restore() const; +}; + +void PrefixEntryBackup::restore() const +{ + m_model->insertPrefix(m_prefixIndex, m_name, m_language); + foreach (const FileEntryBackup &entry, m_files) { + entry.restore(); + } +} + +namespace Internal { + +class RelativeResourceModel : public ResourceModel +{ +public: + RelativeResourceModel(const ResourceFile &resource_file, QObject *parent = 0); + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + if (!index.isValid()) + return QVariant(); +/* + void const * const internalPointer = index.internalPointer(); + + if ((role == Qt::DisplayRole) && (internalPointer != NULL)) + return ResourceModel::data(index, Qt::ToolTipRole); +*/ + return ResourceModel::data(index, role); + } + + void setResourceDragEnabled(bool e) { m_resourceDragEnabled = e; } + bool resourceDragEnabled() const { return m_resourceDragEnabled; } + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + EntryBackup * removeEntry(const QModelIndex &index); + +private: + bool m_resourceDragEnabled; +}; + +RelativeResourceModel::RelativeResourceModel(const ResourceFile &resource_file, QObject *parent) : + ResourceModel(resource_file, parent), + m_resourceDragEnabled(false) +{ +} + +Qt::ItemFlags RelativeResourceModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags rc = ResourceModel::flags(index); + if ((rc & Qt::ItemIsEnabled) && m_resourceDragEnabled) + rc |= Qt::ItemIsDragEnabled; + return rc; +} + +EntryBackup * RelativeResourceModel::removeEntry(const QModelIndex &index) +{ + const QModelIndex prefixIndex = this->prefixIndex(index); + const bool isPrefixNode = (prefixIndex == index); + + // Create backup, remove, return backup + if (isPrefixNode) { + QString dummy; + QString prefixBackup; + getItem(index, prefixBackup, dummy); + const QString languageBackup = lang(index); + const int childCount = rowCount(index); + QList<FileEntryBackup> filesBackup; + for (int i = 0; i < childCount; i++) { + const QModelIndex childIndex = this->index(i, 0, index); + const QString fileNameBackup = file(childIndex); + const QString aliasBackup = alias(childIndex); + FileEntryBackup entry(*this, index.row(), i, fileNameBackup, aliasBackup); + filesBackup << entry; + } + deleteItem(index); + return new PrefixEntryBackup(*this, index.row(), prefixBackup, languageBackup, filesBackup); + } else { + const QString fileNameBackup = file(index); + const QString aliasBackup = alias(index); + deleteItem(index); + return new FileEntryBackup(*this, prefixIndex.row(), index.row(), fileNameBackup, aliasBackup); + } +} + +} // namespace Internal + +ResourceView::ResourceView(QUndoStack *history, QWidget *parent) : + QTreeView(parent), + m_qrcModel(new Internal::RelativeResourceModel(m_qrcFile, this)), + m_addFile(0), + m_editAlias(0), + m_removeItem(0), + m_addPrefix(0), + m_editPrefix(0), + m_editLang(0), + m_viewMenu(0), + m_defaultAddFile(false), + m_history(history), + m_mergeId(-1) +{ + advanceMergeId(); + setModel(m_qrcModel); + + header()->hide(); + + connect(m_qrcModel, SIGNAL(dirtyChanged(bool)), + this, SIGNAL(dirtyChanged(bool))); + + setupMenu(); + + setDefaultAddFileEnabled(true); + enableContextMenu(true); +} + +ResourceView::~ResourceView() +{ +} + +void ResourceView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + Q_UNUSED(current) + Q_UNUSED(previous) + emit currentIndexChanged(); +} + +bool ResourceView::isDirty() const +{ + return m_qrcModel->dirty(); +} + +void ResourceView::setDirty(bool dirty) +{ + m_qrcModel->setDirty(dirty); +} + +void ResourceView::setDefaultAddFileEnabled(bool enable) +{ + m_defaultAddFile = enable; +} + +bool ResourceView::defaultAddFileEnabled() const +{ + return m_defaultAddFile; +} + +void ResourceView::findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const +{ + // Concept: + // - Make selection stay on same Y level + // - Enable user to hit delete several times in row + const bool hasLowerBrother = m_qrcModel->hasIndex(row + 1, + 0, parent); + if (hasLowerBrother) { + // First or mid child -> lower brother + // o + // +--o + // +-[o] <-- deleted + // +--o <-- chosen + // o + // --> return unmodified + } else { + if (parent == QModelIndex()) { + // Last prefix node + if (row == 0) { + // Last and only prefix node + // [o] <-- deleted + // +--o + // +--o + row = -1; + parent = QModelIndex(); + } else { + const QModelIndex upperBrother = m_qrcModel->index(row - 1, + 0, parent); + if (m_qrcModel->hasChildren(upperBrother)) { + // o + // +--o <-- selected + // [o] <-- deleted + row = m_qrcModel->rowCount(upperBrother) - 1; + parent = upperBrother; + } else { + // o + // o <-- selected + // [o] <-- deleted + row--; + } + } + } else { + // Last file node + const bool hasPrefixBelow = m_qrcModel->hasIndex(parent.row() + 1, + parent.column(), QModelIndex()); + if (hasPrefixBelow) { + // Last child or parent with lower brother -> lower brother of parent + // o + // +--o + // +-[o] <-- deleted + // o <-- chosen + row = parent.row() + 1; + parent = QModelIndex(); + } else { + const bool onlyChild = row == 0; + if (onlyChild) { + // Last and only child of last parent -> parent + // o <-- chosen + // +-[o] <-- deleted + row = parent.row(); + parent = m_qrcModel->parent(parent); + } else { + // Last child of last parent -> upper brother + // o + // +--o <-- chosen + // +-[o] <-- deleted + row--; + } + } + } + } +} + +EntryBackup * ResourceView::removeEntry(const QModelIndex &index) +{ + Q_ASSERT(m_qrcModel); + return m_qrcModel->removeEntry(index); +} + +void ResourceView::addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile, + int &firstFile, int &lastFile) +{ + Q_ASSERT(m_qrcModel); + m_qrcModel->addFiles(prefixIndex, fileNames, cursorFile, firstFile, lastFile); + + // Expand prefix node + const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex()); + if (prefixModelIndex.isValid()) { + this->setExpanded(prefixModelIndex, true); + } +} + +void ResourceView::removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex) +{ + Q_ASSERT(prefixIndex >= 0 && prefixIndex < m_qrcModel->rowCount(QModelIndex())); + const QModelIndex prefixModelIndex = m_qrcModel->index(prefixIndex, 0, QModelIndex()); + Q_ASSERT(prefixModelIndex != QModelIndex()); + Q_ASSERT(firstFileIndex >= 0 && firstFileIndex < m_qrcModel->rowCount(prefixModelIndex)); + Q_ASSERT(lastFileIndex >= 0 && lastFileIndex < m_qrcModel->rowCount(prefixModelIndex)); + + for (int i = lastFileIndex; i >= firstFileIndex; i--) { + const QModelIndex index = m_qrcModel->index(i, 0, prefixModelIndex); + delete removeEntry(index); + } +} + +void ResourceView::enableContextMenu(bool enable) +{ + if (enable) { + connect(this, SIGNAL(clicked(const QModelIndex &)), + this, SLOT(popupMenu(const QModelIndex &))); + } else { + disconnect(this, SIGNAL(clicked(const QModelIndex &)), + this, SLOT(popupMenu(const QModelIndex &))); + } +} + +void ResourceView::setupMenu() +{ + m_viewMenu = new QMenu(this); +/* + m_addFile = m_viewMenu->addAction(tr("Add Files..."), this, SIGNAL(addFiles())); + m_editAlias = m_viewMenu->addAction(tr("Change Alias..."), this, SLOT(onEditAlias())); + m_addPrefix = m_viewMenu->addAction(tr("Add Prefix..."), this, SLOT(addPrefix())); + m_editPrefix = m_viewMenu->addAction(tr("Change Prefix..."), this, SLOT(onEditPrefix())); + m_editLang = m_viewMenu->addAction(tr("Change Language..."), this, SLOT(onEditLang())); + m_viewMenu->addSeparator(); + m_removeItem = m_viewMenu->addAction(tr("Remove Item"), this, SLOT(removeItem())); +*/ + m_addFile = m_viewMenu->addAction(tr("Add Files..."), this, SLOT(onAddFiles())); + m_editAlias = m_viewMenu->addAction(tr("Change Alias..."), this, SLOT(onEditAlias())); + m_addPrefix = m_viewMenu->addAction(tr("Add Prefix..."), this, SIGNAL(addPrefixTriggered())); + m_editPrefix = m_viewMenu->addAction(tr("Change Prefix..."), this, SLOT(onEditPrefix())); + m_editLang = m_viewMenu->addAction(tr("Change Language..."), this, SLOT(onEditLang())); + m_viewMenu->addSeparator(); + m_removeItem = m_viewMenu->addAction(tr("Remove Item"), this, SIGNAL(removeItem())); +} + +void ResourceView::mouseReleaseEvent(QMouseEvent *e) +{ + m_releasePos = e->globalPos(); + if (e->button() != Qt::RightButton) + m_releasePos = QPoint(); + + QTreeView::mouseReleaseEvent(e); +} + +void ResourceView::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Delete) + removeItem(); + else + QTreeView::keyPressEvent(e); +} + +void ResourceView::popupMenu(const QModelIndex &index) +{ + if (!m_releasePos.isNull()) { + m_addFile->setEnabled(index.isValid()); + m_editPrefix->setEnabled(index.isValid()); + m_editLang->setEnabled(index.isValid()); + m_removeItem->setEnabled(index.isValid()); + + m_viewMenu->popup(m_releasePos); + } +} + +QModelIndex ResourceView::addPrefix() +{ + const QModelIndex idx = m_qrcModel->addNewPrefix(); + selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); + return idx; +} + +QStringList ResourceView::fileNamesToAdd() +{ + return QFileDialog::getOpenFileNames(this, tr("Open File"), + m_qrcModel->absolutePath(QString()), + tr("All files (*)")); +} + +void ResourceView::onAddFiles() +{ + emit addFilesTriggered(currentPrefix()); +} + +void ResourceView::addFiles(QStringList fileList, const QModelIndex &index) +{ + if (fileList.isEmpty()) + return; + QModelIndex idx = index; + if (!m_qrcModel->hasChildren(QModelIndex())) { + idx = addPrefix(); + expand(idx); + } + + idx = m_qrcModel->addFiles(idx, fileList); + + if (idx.isValid()) { + const QModelIndex preindex = m_qrcModel->prefixIndex(index); + setExpanded(preindex, true); + selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect); + QString prefix, file; + m_qrcModel->getItem(preindex, prefix, file); +// XXX emit filesAdded(prefix, fileList); + } +} + +void ResourceView::addFile(const QString &prefix, const QString &file) +{ + const QModelIndex preindex = m_qrcModel->getIndex(prefix, QString()); + addFiles(QStringList(file), preindex); +} + +/* +void ResourceView::removeItem() +{ + const QModelIndex index = currentIndex(); + m_qrcModel->deleteItem(index); +} + +void ResourceView::removeFile(const QString &prefix, const QString &file) +{ + const QModelIndex index = m_qrcModel->getIndex(prefix, file); + if (index.isValid()) + m_qrcModel->deleteItem(index); +} +*/ +void ResourceView::onEditPrefix() +{ + QModelIndex index = currentIndex(); + changePrefix(index); +} + +void ResourceView::onEditLang() +{ + const QModelIndex index = currentIndex(); + changeLang(index); +} + +void ResourceView::onEditAlias() +{ + const QModelIndex index = currentIndex(); + changeAlias(index); +} + +bool ResourceView::load(const QString &fileName) +{ + const QFileInfo fi(fileName); + m_qrcModel->setFileName(fi.absoluteFilePath()); + + if (!fi.exists()) + return false; + + return m_qrcModel->reload(); +} + +bool ResourceView::save() +{ + return m_qrcModel->save(); +} + +void ResourceView::changePrefix(const QModelIndex &index) +{ + bool ok = false; + const QModelIndex preindex = m_qrcModel->prefixIndex(index); + + QString prefixBefore; + QString dummy; + m_qrcModel->getItem(preindex, prefixBefore, dummy); + + QString const prefixAfter = QInputDialog::getText(this, tr("Change Prefix"), tr("Input prefix:"), + QLineEdit::Normal, prefixBefore, &ok); + + if (ok) + addUndoCommand(preindex, PrefixProperty, prefixBefore, prefixAfter); +} + +void ResourceView::changeLang(const QModelIndex &index) +{ + bool ok = false; + const QModelIndex preindex = m_qrcModel->prefixIndex(index); + + QString const langBefore = m_qrcModel->lang(preindex); + QString const langAfter = QInputDialog::getText(this, tr("Change Language"), tr("Language:"), + QLineEdit::Normal, langBefore, &ok); + + if (ok) { + addUndoCommand(preindex, LanguageProperty, langBefore, langAfter); + } +} + +void ResourceView::changeAlias(const QModelIndex &index) +{ + if (!index.parent().isValid()) + return; + + bool ok = false; + + QString const aliasBefore = m_qrcModel->alias(index); + QString const aliasAfter = QInputDialog::getText(this, tr("Change File Alias"), tr("Alias:"), + QLineEdit::Normal, aliasBefore, &ok); + + if (ok) + addUndoCommand(index, AliasProperty, aliasBefore, aliasAfter); +} + +QString ResourceView::currentAlias() const +{ + const QModelIndex current = currentIndex(); + if (!current.isValid()) + return QString(); + return m_qrcModel->alias(current); +} + +QString ResourceView::currentPrefix() const +{ + const QModelIndex current = currentIndex(); + if (!current.isValid()) + return QString(); + const QModelIndex preindex = m_qrcModel->prefixIndex(current); + QString prefix, file; + m_qrcModel->getItem(preindex, prefix, file); + return prefix; +} + +QString ResourceView::currentLanguage() const +{ + const QModelIndex current = currentIndex(); + if (!current.isValid()) + return QString(); + const QModelIndex preindex = m_qrcModel->prefixIndex(current); + return m_qrcModel->lang(preindex); +} + +QString ResourceView::getCurrentValue(NodeProperty property) const +{ + switch (property) { + case AliasProperty: return currentAlias(); + case PrefixProperty: return currentPrefix(); + case LanguageProperty: return currentLanguage(); + default: Q_ASSERT(false); return QString(); // Kill warning + } +} + +void ResourceView::changeValue(const QModelIndex &nodeIndex, NodeProperty property, + const QString &value) +{ + switch (property) { + case AliasProperty: m_qrcModel->changeAlias(nodeIndex, value); return; + case PrefixProperty: m_qrcModel->changePrefix(nodeIndex, value); return; + case LanguageProperty: m_qrcModel->changeLang(nodeIndex, value); return; + default: Q_ASSERT(false); + } +} + +void ResourceView::advanceMergeId() +{ + m_mergeId++; + if (m_mergeId < 0) + m_mergeId = 0; +} + +void ResourceView::addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property, + const QString &before, const QString &after) +{ + QUndoCommand * const command = new ModifyPropertyCommand(this, nodeIndex, property, + m_mergeId, before, after); + m_history->push(command); +} + +void ResourceView::setCurrentAlias(const QString &before, const QString &after) +{ + const QModelIndex current = currentIndex(); + if (!current.isValid()) + return; + + addUndoCommand(current, AliasProperty, before, after); +} + +void ResourceView::setCurrentPrefix(const QString &before, const QString &after) +{ + const QModelIndex current = currentIndex(); + if (!current.isValid()) + return; + const QModelIndex preindex = m_qrcModel->prefixIndex(current); + + addUndoCommand(preindex, PrefixProperty, before, after); +} + +void ResourceView::setCurrentLanguage(const QString &before, const QString &after) +{ + const QModelIndex current = currentIndex(); + if (!current.isValid()) + return; + const QModelIndex preindex = m_qrcModel->prefixIndex(current); + + addUndoCommand(preindex, LanguageProperty, before, after); +} + +bool ResourceView::isPrefix(const QModelIndex &index) const +{ + if (!index.isValid()) + return false; + const QModelIndex preindex = m_qrcModel->prefixIndex(index); + if (preindex == index) + return true; + return false; +} + +QString ResourceView::fileName() const +{ + return m_qrcModel->fileName(); +} + +void ResourceView::setFileName(const QString &fileName) +{ + m_qrcModel->setFileName(fileName); +} + +void ResourceView::setResourceDragEnabled(bool e) +{ + setDragEnabled(e); + m_qrcModel->setResourceDragEnabled(e); +} + +bool ResourceView::resourceDragEnabled() const +{ + return m_qrcModel->resourceDragEnabled(); +} + +} // namespace SharedTools diff --git a/QrcEditor/shared/resourceview.h b/QrcEditor/shared/resourceview.h new file mode 100644 index 0000000..cbca091 --- /dev/null +++ b/QrcEditor/shared/resourceview.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RESOURCEVIEW_H +#define RESOURCEVIEW_H + +#include "resourcefile_p.h" + +#include <QtWidgets/QTreeView> +#include <QtCore/QPoint> + +using namespace qdesigner_internal; + +QT_BEGIN_NAMESPACE +class QAction; +class QMenu; +class QMouseEvent; +class QUndoStack; +QT_END_NAMESPACE + +namespace SharedTools { + +/*! + \class EntryBackup + + Holds the backup of a tree node including children. +*/ +class EntryBackup +{ +protected: + ResourceModel *m_model; + int m_prefixIndex; + QString m_name; + + EntryBackup(ResourceModel &model, int prefixIndex, const QString &name) + : m_model(&model), m_prefixIndex(prefixIndex), m_name(name) { } + +public: + virtual void restore() const = 0; + virtual ~EntryBackup() { } +}; + +namespace Internal { + class RelativeResourceModel; +} + +class ResourceView : public QTreeView +{ + Q_OBJECT + +public: + enum NodeProperty { + AliasProperty, + PrefixProperty, + LanguageProperty + }; + + ResourceView(QUndoStack *history, QWidget *parent = 0); + ~ResourceView(); + + bool load(const QString &fileName); + bool save(); + QString fileName() const; + void setFileName(const QString &fileName); + + bool isDirty() const; + void setDirty(bool dirty); + + void enableContextMenu(bool enable); + + void addFiles(QStringList fileList, const QModelIndex &index); + + void addFile(const QString &prefix, const QString &file); +// void removeFile(const QString &prefix, const QString &file); + + bool isPrefix(const QModelIndex &index) const; + + QString currentAlias() const; + QString currentPrefix() const; + QString currentLanguage() const; + + void setResourceDragEnabled(bool e); + bool resourceDragEnabled() const; + + void setDefaultAddFileEnabled(bool enable); + bool defaultAddFileEnabled() const; + + void findSamePlacePostDeletionModelIndex(int &row, QModelIndex &parent) const; + EntryBackup *removeEntry(const QModelIndex &index); + void addFiles(int prefixIndex, const QStringList &fileNames, int cursorFile, + int &firstFile, int &lastFile); + void removeFiles(int prefixIndex, int firstFileIndex, int lastFileIndex); + QStringList fileNamesToAdd(); + QModelIndex addPrefix(); + +public slots: + void onAddFiles(); + void setCurrentAlias(const QString &before, const QString &after); + void setCurrentPrefix(const QString &before, const QString &after); + void setCurrentLanguage(const QString &before, const QString &after); + void advanceMergeId(); + +protected: + void setupMenu(); + void changePrefix(const QModelIndex &index); + void changeLang(const QModelIndex &index); + void changeAlias(const QModelIndex &index); + void mouseReleaseEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + +signals: + void removeItem(); + void dirtyChanged(bool b); + void currentIndexChanged(); + + void addFilesTriggered(const QString &prefix); + void addPrefixTriggered(); + +protected slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +private slots: + void onEditAlias(); + void onEditPrefix(); + void onEditLang(); + void popupMenu(const QModelIndex &index); + +public: + QString getCurrentValue(NodeProperty property) const; + void changeValue(const QModelIndex &nodeIndex, NodeProperty property, const QString &value); + +private: + void addUndoCommand(const QModelIndex &nodeIndex, NodeProperty property, + const QString &before, const QString &after); + + QPoint m_releasePos; + + qdesigner_internal::ResourceFile m_qrcFile; + Internal::RelativeResourceModel *m_qrcModel; + + QAction *m_addFile; + QAction *m_editAlias; + QAction *m_removeItem; + QAction *m_addPrefix; + QAction *m_editPrefix; + QAction *m_editLang; + QMenu *m_viewMenu; + bool m_defaultAddFile; + QUndoStack *m_history; + int m_mergeId; +}; + +} // namespace SharedTools + +#endif // RESOURCEVIEW_H diff --git a/QrcEditor/shared/undocommands.cpp b/QrcEditor/shared/undocommands.cpp new file mode 100644 index 0000000..490439d --- /dev/null +++ b/QrcEditor/shared/undocommands.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "undocommands_p.h" + +#include <QtCore/QModelIndex> + +namespace SharedTools { + +ViewCommand::ViewCommand(ResourceView *view) + : m_view(view) +{ } + +ViewCommand::~ViewCommand() +{ } + +ModelIndexViewCommand::ModelIndexViewCommand(ResourceView *view) + : ViewCommand(view) +{ } + +ModelIndexViewCommand::~ModelIndexViewCommand() +{ } + +void ModelIndexViewCommand::storeIndex(const QModelIndex &index) +{ + if (m_view->isPrefix(index)) { + m_prefixArrayIndex = index.row(); + m_fileArrayIndex = -1; + } else { + m_fileArrayIndex = index.row(); + m_prefixArrayIndex = m_view->model()->parent(index).row(); + } +} + +QModelIndex ModelIndexViewCommand::makeIndex() const +{ + const QModelIndex prefixModelIndex + = m_view->model()->index(m_prefixArrayIndex, 0, QModelIndex()); + if (m_fileArrayIndex != -1) { + // File node + const QModelIndex fileModelIndex + = m_view->model()->index(m_fileArrayIndex, 0, prefixModelIndex); + return fileModelIndex; + } else { + // Prefix node + return prefixModelIndex; + } +} + + + +ModifyPropertyCommand::ModifyPropertyCommand(ResourceView *view, const QModelIndex &nodeIndex, + ResourceView::NodeProperty property, const int mergeId, const QString &before, + const QString &after) + : ModelIndexViewCommand(view), m_property(property), m_before(before), m_after(after), + m_mergeId(mergeId) +{ + storeIndex(nodeIndex); +} + +bool ModifyPropertyCommand::mergeWith(const QUndoCommand * command) +{ + if (command->id() != id() || m_property != static_cast<const ModifyPropertyCommand *>(command)->m_property) + return false; + // Choose older command (this) and forgot the other + return true; +} + +void ModifyPropertyCommand::undo() +{ + Q_ASSERT(m_view); + + // Save current text in m_after for redo() + m_after = m_view->getCurrentValue(m_property); + + // Reset text to m_before + m_view->changeValue(makeIndex(), m_property, m_before); +} + +void ModifyPropertyCommand::redo() +{ + // Prevent execution from within QUndoStack::push + if (m_after.isNull()) + return; + + // Bring back text before undo + Q_ASSERT(m_view); + m_view->changeValue(makeIndex(), m_property, m_after); +} + +RemoveEntryCommand::RemoveEntryCommand(ResourceView *view, const QModelIndex &index) + : ModelIndexViewCommand(view), m_entry(0), m_isExpanded(true) +{ + storeIndex(index); +} + +RemoveEntryCommand::~RemoveEntryCommand() +{ + freeEntry(); +} + +void RemoveEntryCommand::redo() +{ + freeEntry(); + const QModelIndex index = makeIndex(); + m_isExpanded = m_view->isExpanded(index); + m_entry = m_view->removeEntry(index); +} + +void RemoveEntryCommand::undo() +{ + if (m_entry == 0) { + m_entry->restore(); + Q_ASSERT(m_view != 0); + const QModelIndex index = makeIndex(); + m_view->setExpanded(index, m_isExpanded); + m_view->setCurrentIndex(index); + freeEntry(); + } +} + +void RemoveEntryCommand::freeEntry() +{ + delete m_entry; + m_entry = 0; +} + +AddFilesCommand::AddFilesCommand(ResourceView *view, int prefixIndex, int cursorFileIndex, + const QStringList &fileNames) + : ViewCommand(view), m_prefixIndex(prefixIndex), m_cursorFileIndex(cursorFileIndex), + m_fileNames(fileNames), m_firstFile(-1), m_lastFile(-1) +{ } + +void AddFilesCommand::redo() +{ + m_view->addFiles(m_prefixIndex, m_fileNames, m_cursorFileIndex, m_firstFile, m_lastFile); +} + +void AddFilesCommand::undo() +{ + m_view->removeFiles(m_prefixIndex, m_firstFile, m_lastFile); +} + +AddEmptyPrefixCommand::AddEmptyPrefixCommand(ResourceView *view) + : ViewCommand(view) +{ } + +void AddEmptyPrefixCommand::redo() +{ + m_prefixArrayIndex = m_view->addPrefix().row(); +} + +void AddEmptyPrefixCommand::undo() +{ + const QModelIndex prefixModelIndex = m_view->model()->index( + m_prefixArrayIndex, 0, QModelIndex()); + delete m_view->removeEntry(prefixModelIndex); +} + +} // namespace SharedTools diff --git a/QrcEditor/shared/undocommands_p.h b/QrcEditor/shared/undocommands_p.h new file mode 100644 index 0000000..cee8efe --- /dev/null +++ b/QrcEditor/shared/undocommands_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UNDO_COMMANDS_H +#define UNDO_COMMANDS_H + +#include "resourceview.h" + +#include <QtCore/QString> +#include <QtWidgets/QUndoCommand> + +QT_BEGIN_NAMESPACE +class QModelIndex; +QT_END_NAMESPACE + +namespace SharedTools { + +/*! + \class ViewCommand + + Provides a base for \l ResourceView-related commands. +*/ +class ViewCommand : public QUndoCommand +{ +protected: + ResourceView *m_view; + + ViewCommand(ResourceView *view); + virtual ~ViewCommand(); +}; + +/*! + \class ModelIndexViewCommand + + Provides a mean to store/restore a \l QModelIndex as it cannot + be stored safely in most cases. This is an abstract class. +*/ +class ModelIndexViewCommand : public ViewCommand +{ + int m_prefixArrayIndex; + int m_fileArrayIndex; + +protected: + ModelIndexViewCommand(ResourceView *view); + virtual ~ModelIndexViewCommand(); + void storeIndex(const QModelIndex &index); + QModelIndex makeIndex() const; +}; + +/*! + \class ModifyPropertyCommand + + Modifies the name/prefix/language property of a prefix/file node. +*/ +class ModifyPropertyCommand : public ModelIndexViewCommand +{ + ResourceView::NodeProperty m_property; + QString m_before; + QString m_after; + int m_mergeId; + +public: + ModifyPropertyCommand(ResourceView *view, const QModelIndex &nodeIndex, + ResourceView::NodeProperty property, const int mergeId, const QString &before, + const QString &after = QString()); + +private: + int id() const { return m_mergeId; } + bool mergeWith(const QUndoCommand * command); + void undo(); + void redo(); +}; + +/*! + \class RemoveEntryCommand + + Removes a \l QModelIndex including all children from a \l ResourceView. +*/ +class RemoveEntryCommand : public ModelIndexViewCommand +{ + EntryBackup *m_entry; + bool m_isExpanded; + +public: + RemoveEntryCommand(ResourceView *view, const QModelIndex &index); + ~RemoveEntryCommand(); + +private: + void redo(); + void undo(); + void freeEntry(); +}; + +/*! + \class AddFilesCommand + + Adds a list of files to a given prefix node. +*/ +class AddFilesCommand : public ViewCommand +{ + int m_prefixIndex; + int m_cursorFileIndex; + int m_firstFile; + int m_lastFile; + const QStringList m_fileNames; + +public: + AddFilesCommand(ResourceView *view, int prefixIndex, int cursorFileIndex, + const QStringList &fileNames); + +private: + void redo(); + void undo(); +}; + +/*! + \class AddEmptyPrefixCommand + + Adds a new, empty prefix node. +*/ +class AddEmptyPrefixCommand : public ViewCommand +{ + int m_prefixArrayIndex; + +public: + AddEmptyPrefixCommand(ResourceView *view); + +private: + void redo(); + void undo(); +}; + +} // namespace SharedTools + +#endif // UNDO_COMMANDS_H diff --git a/QtCppConfig.props b/QtCppConfig.props new file mode 100644 index 0000000..45a6fc3 --- /dev/null +++ b/QtCppConfig.props @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ImportGroup Label="PropertySheets" /> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(QtBuild_Static_Vs2017)'==''"> + <QtBuild_Static_Vs2017>..\..\..\install\qt5-x86-vs2017-static</QtBuild_Static_Vs2017> + </PropertyGroup> + <PropertyGroup Condition="'$(QtBuild_Static_Vs2019)'==''"> + <QtBuild_Static_Vs2019>..\..\..\install\qt5-x86-vs2019-static</QtBuild_Static_Vs2019> + </PropertyGroup> + <PropertyGroup Condition="'$(QtBuild_Static_Vs2022)'==''"> + <QtBuild_Static_Vs2022>..\..\..\install\qt5-x86-vs2022-static</QtBuild_Static_Vs2022> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)'=='15.0'"> + <QtBuild>$(QtBuild_Static_Vs2017)</QtBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)'=='16.0'"> + <QtBuild>$(QtBuild_Static_Vs2019)</QtBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)'=='17.0'"> + <QtBuild>$(QtBuild_Static_Vs2022)</QtBuild> + </PropertyGroup> + <PropertyGroup> + <QtBuild>$([System.IO.Path]::Combine($(ProjectDir),$(QtBuild)))</QtBuild> + <QtBuild>$([System.IO.Path]::GetFullPath($(QtBuild)))</QtBuild> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/ITaskLoggingHelper.cs b/QtMSBuild/ITaskLoggingHelper.cs new file mode 100644 index 0000000..cbbb651 --- /dev/null +++ b/QtMSBuild/ITaskLoggingHelper.cs @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Build.Framework; + +namespace QtVsTools.QtMSBuild +{ + public interface ITaskLoggingHelper + { + bool HasLoggedErrors { get; } + + void LogCommandLine( + string commandLine); + + void LogCommandLine( + MessageImportance importance, + string commandLine); + + void LogCriticalMessage( + string subcategory, + string code, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs); + + void LogError( + string message, + params object[] messageArgs); + + void LogError( + string subcategory, + string errorCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs); + + void LogError( + string subcategory, + string errorCode, + string helpKeyword, + string helpLink, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs); + + void LogErrorFromException( + Exception exception); + + void LogErrorFromException( + Exception exception, + bool showStackTrace); + + void LogErrorFromException( + Exception exception, + bool showStackTrace, + bool showDetail, + string file); + + void LogErrorFromResources( + string messageResourceName, + params object[] messageArgs); + + void LogErrorFromResources( + string subcategoryResourceName, + string errorCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs); + + void LogErrorWithCodeFromResources( + string messageResourceName, + params object[] messageArgs); + + void LogErrorWithCodeFromResources( + string subcategoryResourceName, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs); + + void LogExternalProjectFinished( + string message, + string helpKeyword, + string projectFile, + bool succeeded); + + void LogExternalProjectStarted( + string message, + string helpKeyword, + string projectFile, + string targetNames); + + void LogMessage( + string message, + params object[] messageArgs); + + void LogMessage( + MessageImportance importance, + string message, + params object[] messageArgs); + + void LogMessage( + string subcategory, + string code, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + MessageImportance importance, + string message, + params object[] messageArgs); + + void LogMessageFromResources( + string messageResourceName, + params object[] messageArgs); + + void LogMessageFromResources( + MessageImportance importance, + string messageResourceName, + params object[] messageArgs); + + bool LogMessageFromText( + string lineOfText, + MessageImportance messageImportance); + + bool LogMessagesFromFile( + string fileName); + + bool LogMessagesFromFile( + string fileName, + MessageImportance messageImportance); + + bool LogMessagesFromStream( + TextReader stream, + MessageImportance messageImportance); + + bool LogsMessagesOfImportance( + MessageImportance importance); + + void LogTelemetry( + string eventName, + IDictionary<string, + string> properties); + + void LogWarning( + string message, + params object[] messageArgs); + + void LogWarning( + string subcategory, + string warningCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs); + + void LogWarning( + string subcategory, + string warningCode, + string helpKeyword, + string helpLink, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs); + + void LogWarningFromException( + Exception exception); + + void LogWarningFromException( + Exception exception, + bool showStackTrace); + + void LogWarningFromResources( + string messageResourceName, + params object[] messageArgs); + + void LogWarningFromResources( + string subcategoryResourceName, + string warningCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs); + + void LogWarningWithCodeFromResources( + string messageResourceName, + params object[] messageArgs); + + void LogWarningWithCodeFromResources( + string subcategoryResourceName, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs); + } +} diff --git a/QtMSBuild/Properties/AssemblyInfo.cs b/QtMSBuild/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..31a1877 --- /dev/null +++ b/QtMSBuild/Properties/AssemblyInfo.cs @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("QtMSBuild")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2021 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a618d28b-9352-44f4-aa71-609bf68bf871")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/QtMSBuild/QtMSBuild.csproj b/QtMSBuild/QtMSBuild.csproj new file mode 100644 index 0000000..5898f7f --- /dev/null +++ b/QtMSBuild/QtMSBuild.csproj @@ -0,0 +1,432 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <RootNamespace>QtMSBuild</RootNamespace> + <AssemblyName>QtMSBuild</AssemblyName> + <ProjectGuid>{A618D28B-9352-44F4-AA71-609BF68BF871}</ProjectGuid> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + <Reference Include="System" /> + <Reference Include="$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll" /> + <Reference Include="$(VCTargetsPath)\Application Type\Linux\1.0\liblinux.dll" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="Microsoft.Build" + Version="$(Version_Microsoft_Build)" /> + <PackageReference Include="Microsoft.Build.Framework" + Version="$(Version_Microsoft_Build_Framework)" /> + <PackageReference Include="Microsoft.Build.Tasks.Core" + Version="$(Version_Microsoft_Build_Tasks_Core)" /> + <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" + Version="$(Version_Microsoft_Bcl_AsyncInterfaces)" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="..\QtVsTools.RegExpr\QtVsTools.RegExpr.csproj"> + <Project>{a2831b9b-4d3b-46cb-85df-1b5c277c17db}</Project> + <Name>QtVsTools.RegExpr</Name> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="ITaskLoggingHelper.cs" /> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild common property pages and targets + // --> + <Content Include="QtMSBuild\qt_defaults.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\Qt.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qt_private.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qt.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qt_settings.xml"> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\qt_settings.targets"> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\qt_globals.targets"> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\qt_vars.targets"> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild moc property pages and targets + // --> + <Content Include="QtMSBuild\moc\qt_import.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\moc\qtmoc.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\moc\qtmoc.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\moc\qtmoc.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <T4Template Include="QtMSBuild\moc\qtmoc_v3.xml_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>moc\qtmoc_v3.xml</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>qtmoc_v3.xml</LastGenOutput> + <SubType>Designer</SubType> + </T4Template> + <Content Include="QtMSBuild\moc\qtmoc_v3.xml"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>qtmoc_v3.xml_TT</DependentUpon> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <T4Template Include="QtMSBuild\moc\qtmoc_cl.targets_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>moc\qtmoc_cl.targets</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <SubType>Designer</SubType> + <LastGenOutput>qtmoc_cl.targets</LastGenOutput> + </T4Template> + <Content Include="QtMSBuild\moc\qtmoc_cl.targets"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>qtmoc_cl.targets_TT</DependentUpon> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild qml property pages and targets + // --> + <Content Include="QtMSBuild\qml\qt_import.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qml\qtqml.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qml\qtqml.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qml\qtqml_cache.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qml\qtqml_cache.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qml\qtqml_static.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\qml\qtqml_static.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild rcc property pages and targets + // --> + <Content Include="QtMSBuild\rcc\qt_import.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\rcc\qtrcc.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\rcc\qtrcc.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\rcc\qtrcc.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <T4Template Include="QtMSBuild\rcc\qtrcc_v3.xml_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>rcc\qtrcc_v3.xml</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>qtrcc_v3.xml</LastGenOutput> + <SubType>Designer</SubType> + </T4Template> + <Content Include="QtMSBuild\rcc\qtrcc_v3.xml"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>qtrcc_v3.xml_TT</DependentUpon> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <T4Template Include="QtMSBuild\rcc\qtrcc_cl.targets_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>rcc\qtrcc_cl.targets</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <SubType>Designer</SubType> + <LastGenOutput>qtrcc_cl.targets</LastGenOutput> + </T4Template> + <Content Include="QtMSBuild\rcc\qtrcc_cl.targets"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>qtrcc_cl.targets_TT</DependentUpon> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild repc property pages and targets + // --> + <Content Include="QtMSBuild\repc\qt_import.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\repc\qtrepc.props"> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\repc\qtrepc.targets"> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\repc\qtrepc.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <T4Template Include="QtMSBuild\repc\qtrepc_v3.xml_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>repc\qtrepc_v3.xml</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>qtrepc_v3.xml</LastGenOutput> + <SubType>Designer</SubType> + </T4Template> + <Content Include="QtMSBuild\repc\qtrepc_v3.xml"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>qtrepc_v3.xml_TT</DependentUpon> + <SubType>Designer</SubType> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <T4Template Include="QtMSBuild\repc\qtrepc_cl.targets_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>repc\qtrepc_cl.targets</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <SubType>Designer</SubType> + <LastGenOutput>qtrepc_cl.targets</LastGenOutput> + </T4Template> + <Content Include="QtMSBuild\repc\qtrepc_cl.targets"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>qtrepc_cl.targets_TT</DependentUpon> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild uic property pages and targets + // --> + <Content Include="QtMSBuild\uic\qt_import.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\uic\qtuic.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\uic\qtuic.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\uic\qtuic.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\uic\qtuic_v3.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild deploy properties and targets + // --> + <Content Include="QtMSBuild\deploy\qt_import.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <Content Include="QtMSBuild\deploy\qtdeploy.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\deploy\qtdeploy.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\deploy\qtdeploy.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <SubType>Designer</SubType> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild translation tools property pages and targets + // --> + <Content Include="QtMSBuild\translation\qttranslation.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\translation\qttranslation.targets"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\translation\qttranslation.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="QtMSBuild\translation\qt_import.props"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Inline tasks + // --> + <Compile Include="Tasks\CriticalSection.cs" /> + <Compile Include="Tasks\GetVarsFromMSBuild.cs" /> + <Compile Include="Tasks\HostExec_LinuxWSL_Error.cs" /> + <Compile Include="Tasks\HostTranslatePaths_LinuxWSL_Error.cs" /> + <Compile Include="Tasks\ListQrc.cs" /> + <Compile Include="Tasks\ParseVarDefs.cs" /> + <Compile Include="Tasks\GetVarsFromMakefile.cs" /> + <Compile Include="Tasks\QtRunWork.cs" /> + <Compile Include="Tasks\GetItemHash.cs" /> + <Compile Include="Tasks\Flatten.cs" /> + <Compile Include="Tasks\Expand.cs" /> + <Compile Include="Tasks\Join.cs" /> + <Compile Include="Tasks\DumpItems.cs" /> + <Compile Include="Tasks\HostTranslatePaths_Windows.cs" /> + <Compile Include="Tasks\HostExec_Windows.cs" /> + <Compile Include="Tasks\HostTranslatePaths_LinuxSSL.cs" /> + <Compile Include="Tasks\HostExec_LinuxSSL.cs" /> + <Compile Include="Tasks\HostTranslatePaths_LinuxWSL.cs" /> + <Compile Include="Tasks\HostExec_LinuxWSL.cs" /> + <T4Template Include="QtMSBuild\qt_tasks.targets_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>qt_tasks.targets</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <SubType>Designer</SubType> + <LastGenOutput>qt_tasks.targets</LastGenOutput> + </T4Template> + <Content Include="QtMSBuild\qt_tasks.targets"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>qt_tasks.targets_TT</DependentUpon> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <T4Template Include="$(SolutionDir)\vstools.pri_TT"> + <Link>vstools.pri_TT</Link> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>$(SolutionDir)\vstools.pri</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>vstools.pri</LastGenOutput> + <SubType>Designer</SubType> + </T4Template> + <None Include="$(SolutionDir)\vstools.pri"> + <Link>vstools.pri</Link> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>vstools.pri_TT</DependentUpon> + <SubType>Designer</SubType> + </None> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> \ No newline at end of file diff --git a/QtMSBuild/QtMsBuild/Qt.props b/QtMSBuild/QtMsBuild/Qt.props new file mode 100644 index 0000000..dade92b --- /dev/null +++ b/QtMSBuild/QtMsBuild/Qt.props @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Item type definition and default values +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import private Qt property definitions + // --> + <ImportGroup Label="Qt"> + <Import Project="$(MSBuildThisFileDirectory)\qt_private.props"/> + </ImportGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // User-defined settings + // --> + <PropertyGroup Label="UserMacros"> + <!-- Placeholder for user macros written by VS Property Manager --> + </PropertyGroup> + <PropertyGroup> + <!-- Placeholder for properties written by VS Property Manager --> + </PropertyGroup> + <ItemDefinitionGroup> + <!-- Placeholder for default metadata written by VS Property Manager --> + </ItemDefinitionGroup> + <ItemGroup> + <!-- Placeholder for items written by VS Property Manager --> + </ItemGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/deploy/qt_import.props b/QtMSBuild/QtMsBuild/deploy/qt_import.props new file mode 100644 index 0000000..a38b3fc --- /dev/null +++ b/QtMSBuild/QtMsBuild/deploy/qt_import.props @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild import + // --> + <PropertyGroup> + <QtMsBuildProps> + $(QtMsBuildProps); + $(MSBuildThisFileDirectory)qtdeploy.props + </QtMsBuildProps> + <QtMsBuildTargets> + $(QtMsBuildTargets); + $(MSBuildThisFileDirectory)qtdeploy.targets + </QtMsBuildTargets> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/deploy/qtdeploy.props b/QtMSBuild/QtMsBuild/deploy/qtdeploy.props new file mode 100644 index 0000000..261f6ae --- /dev/null +++ b/QtMSBuild/QtMsBuild/deploy/qtdeploy.props @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Deployment of Qt dependencies +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <QtDeploy Condition="'$(QtDeploy)' == ''">false</QtDeploy> + </PropertyGroup> + <ItemDefinitionGroup> + <QtDeploy> + <QtDeployFiles>$(TargetPath)</QtDeployFiles> + <QtDeployDir Condition="'$(QtDeployToProjectDir)' == 'true'">$(ProjectDir)</QtDeployDir> + <QtDeployDir Condition="'$(QtDeployToProjectDir)' != 'true'">$(OutDir)</QtDeployDir> + <QtDeployLibDir/> + <QtDeployPluginDir/> + <QtDeployDebugRelease>false</QtDeployDebugRelease> + <QtDeployPdb>false</QtDeployPdb> + <QtDeployForce>false</QtDeployForce> + <QtDeployNoPatchQt>false</QtDeployNoPatchQt> + <QtDeployNoPlugins>false</QtDeployNoPlugins> + <QtDeployNoLibraries>false</QtDeployNoLibraries> + <QtDeployQmlDir/> + <QtDeployQmlImport/> + <QtDeployNoQuickImport>false</QtDeployNoQuickImport> + <QtDeployNoTranslations>false</QtDeployNoTranslations> + <QtDeployNoSystemD3dCompiler>false</QtDeployNoSystemD3dCompiler> + <QtDeployNoVirtualKeyboard>false</QtDeployNoVirtualKeyboard> + <QtDeployCompilerRuntime>false</QtDeployCompilerRuntime> + <QtDeployWebkit2>false</QtDeployWebkit2> + <QtDeployAngle>false</QtDeployAngle> + <QtDeployNoOpenglSw>false</QtDeployNoOpenglSw> + <QtDeployIncludeModules/> + <QtDeployExcludeModules/> + <QtDeployVerbose>false</QtDeployVerbose> + <QtDeployVsContent Condition="'$(QtDeployVsContent)' == 'true'">true</QtDeployVsContent> + <QtDeployVsContent Condition="'$(QtDeployVsContent)' != 'true'">false</QtDeployVsContent> + </QtDeploy> + </ItemDefinitionGroup> + <ItemGroup Condition="'$(QtDeploy)' == 'true'"> + <ProjectTools Include="QtDeploy"/> + </ItemGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/deploy/qtdeploy.targets b/QtMSBuild/QtMsBuild/deploy/qtdeploy.targets new file mode 100644 index 0000000..9f9be36 --- /dev/null +++ b/QtMSBuild/QtMsBuild/deploy/qtdeploy.targets @@ -0,0 +1,342 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Deployment of Qt dependencies +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Setup property page + // --> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtdeploy.xml" /> + </ItemGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Default deploy log location + // --> + <PropertyGroup> + <QtDeployLog Condition="'$(QtDeployLog)' == ''">$(IntDir)windeployqt.log</QtDeployLog> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Schedule QtDeploy target to run after Link + // --> + <PropertyGroup Condition="'$(QtDeploy)' == 'true' AND '$(ApplicationType)' != 'Linux'"> + <BuildLinkTargets>$(BuildLinkTargets);QtDeploy</BuildLinkTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Deploy Qt files + // --> + <Target Name="QtDeploy" + AfterTargets="Link" + Inputs="$(TargetPath)" Outputs="$(QtDeployLog)" + Condition="'$(QtDeploy)' == 'true' AND '$(ApplicationType)' != 'Linux'"> + + <ItemGroup> + <QtDeploy Include="$(TargetPath)"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Convert string lists in source item properties to lists of items + // --> + <Flatten Items="@(QtDeploy)" + Metadata="QtDeployFiles; + QtDeployDir; + QtDeployLibDir; + QtDeployPluginDir; + QtDeployDebugRelease; + QtDeployPdb; + QtDeployForce; + QtDeployNoPatchQt; + QtDeployNoPlugins; + QtDeployNoLibraries; + QtDeployQmlDir; + QtDeployQmlImport; + QtDeployNoQuickImport; + QtDeployNoTranslations; + QtDeployNoSystemD3dCompiler; + QtDeployNoVirtualKeyboard; + QtDeployCompilerRuntime; + QtDeployWebkit2; + QtDeployAngle; + QtDeployNoOpenglSw; + QtDeployIncludeModules; + QtDeployExcludeModules; + QtDeployVerbose"> + <Output + TaskParameter="Result" ItemName="Options" /> + </Flatten> + + <ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Remove quotes from all paths + // --> + <Options> + <Value Condition="( '%(Name)' == 'QtDeployFiles' + OR '%(Name)' == 'QtDeployDir' + OR '%(Name)' == 'QtDeployLibDir' + OR '%(Name)' == 'QtDeployPluginDir' + OR '%(Name)' == 'QtDeployQmlDir' + OR '%(Name)' == 'QtDeployQmlImport' )" + >$([System.String]::Copy('%(Value)').Replace('"', ''))</Value> + </Options> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////// + // Escape trailing back-slash in paths + // --> + <Options> + <Value Condition="( '%(Name)' == 'QtDeployFiles' + OR '%(Name)' == 'QtDeployDir' + OR '%(Name)' == 'QtDeployLibDir' + OR '%(Name)' == 'QtDeployPluginDir' + OR '%(Name)' == 'QtDeployQmlDir' + OR '%(Name)' == 'QtDeployQmlImport' ) + AND $([System.String]::Copy('%(Value)').Contains(' ')) + AND $([System.String]::Copy('%(Value)').EndsWith('\'))" + >%(Value)\</Value> + </Options> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////// + // Add quotes to paths containing spaces + // --> + <Options> + <Value Condition="( '%(Name)' == 'QtDeployFiles' + OR '%(Name)' == 'QtDeployDir' + OR '%(Name)' == 'QtDeployLibDir' + OR '%(Name)' == 'QtDeployPluginDir' + OR '%(Name)' == 'QtDeployQmlDir' + OR '%(Name)' == 'QtDeployQmlImport' ) + AND $([System.String]::Copy('%(Value)').Contains(' '))" + >"%(Value)"</Value> + </Options> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Generate tool command line arguments + // --> + <Options> + <!-- [files] Binaries or directory containing the binary. --> + <PosArg Condition="'%(Name)' == 'QtDeployFiles'">%(Value)</PosArg> + </Options> + <Options> + <!-- -dir <directory> Use directory instead of binary directory. --> + <Arg Condition="'%(Name)' == 'QtDeployDir'">--dir %(Value)</Arg> + </Options> + <Options> + <!-- -libdir <path> Copy libraries to path. --> + <Arg Condition="'%(Name)' == 'QtDeployLibDir'">--libdir %(Value)</Arg> + </Options> + <Options> + <!-- -plugindir <path> Copy plugins to path. --> + <Arg Condition="'%(Name)' == 'QtDeployPluginDir'">--plugindir %(Value)</Arg> + </Options> + <Options> + <!-- -debug Assume debug binaries. + -release Assume release binaries.--> + <Arg Condition="'%(Name)' == 'QtDeployDebugRelease' AND '%(Value)' == 'debug'" + >--debug</Arg> + <Arg Condition="'%(Name)' == 'QtDeployDebugRelease' AND '%(Value)' == 'release'" + >--release</Arg> + </Options> + <Options> + <!-- -pdb Deploy .pdb files (MSVC). --> + <Arg Condition="'%(Name)' == 'QtDeployPdb' AND '%(Value)' == 'true'" + >--pdb</Arg> + </Options> + <Options> + <!-- -force Force updating files. --> + <Arg Condition="'%(Name)' == 'QtDeployForce' AND '%(Value)' == 'true'" + >--force</Arg> + </Options> + <Options> + <!-- -no-patchqt Do not patch the Qt5Core library. --> + <Arg Condition="'%(Name)' == 'QtDeployNoPatchQt' AND '%(Value)' == 'true'" + >--no-patchqt</Arg> + </Options> + <Options> + <!-- -no-plugins Skip plugin deployment. --> + <Arg Condition="'%(Name)' == 'QtDeployNoPlugins' AND '%(Value)' == 'true'" + >--no-plugins</Arg> + </Options> + <Options> + <!-- -no-libraries Skip library deployment. --> + <Arg Condition="'%(Name)' == 'QtDeployNoLibraries' AND '%(Value)' == 'true'" + >--no-libraries</Arg> + </Options> + <Options> + <!-- -qmldir <directory> Scan for QML-imports starting from directory. --> + <Arg Condition="'%(Name)' == 'QtDeployQmlDir'">--qmldir %(Value)</Arg> + </Options> + <Options> + <!-- -qmlimport <directory> Add the given path to the QML module search + locations. --> + <Arg Condition="'%(Name)' == 'QtDeployQmlImport'">--qmlimport %(Value)</Arg> + </Options> + <Options> + <!-- -no-quick-import Skip deployment of Qt Quick imports. --> + <Arg Condition="'%(Name)' == 'QtDeployNoQuickImport' AND '%(Value)' == 'true'" + >--no-quick-import</Arg> + </Options> + <Options> + <!-- -no-translations Skip deployment of translations. --> + <Arg Condition="'%(Name)' == 'QtDeployNoTranslations' AND '%(Value)' == 'true'" + >--no-translations</Arg> + </Options> + <Options> + <!-- -no-system-d3d-compiler Skip deployment of the system D3D compiler. --> + <Arg Condition="'%(Name)' == 'QtDeployNoSystemD3dCompiler' AND '%(Value)' == 'true'" + >--no-system-d3d-compiler</Arg> + </Options> + <Options> + <!-- -no-virtualkeyboard Disable deployment of the Virtual Keyboard. --> + <Arg Condition="'%(Name)' == 'QtDeployNoVirtualKeyboard' AND '%(Value)' == 'true'" + >--no-virtualkeyboard</Arg> + </Options> + <Options> + <!-- -compiler-runtime Deploy compiler runtime (Desktop only). + -no-compiler-runtime Do not deploy compiler runtime (Desktop only). --> + <Arg Condition="'%(Name)' == 'QtDeployCompilerRuntime' AND '%(Value)' == 'deploy'" + >--compiler-runtime</Arg> + <Arg Condition="'%(Name)' == 'QtDeployCompilerRuntime' AND '%(Value)' == 'skip'" + >--no-compiler-runtime</Arg> + </Options> + <Options> + <!-- -webkit2 Deployment of WebKit2 (web process). + -no-webkit2 Skip deployment of WebKit2. --> + <Arg Condition="'%(Name)' == 'QtDeployWebkit2' AND '%(Value)' == 'deploy'" + >--webkit2</Arg> + <Arg Condition="'%(Name)' == 'QtDeployWebkit2' AND '%(Value)' == 'skip'" + >--no-webkit2</Arg> + </Options> + <Options> + <!-- -angle Force deployment of ANGLE. + -no-angle Disable deployment of ANGLE. --> + <Arg Condition="'%(Name)' == 'QtDeployAngle' AND '%(Value)' == 'deploy'" + >--angle</Arg> + <Arg Condition="'%(Name)' == 'QtDeployAngle' AND '%(Value)' == 'skip'" + >--no-angle</Arg> + </Options> + <Options> + <!-- -no-opengl-sw Do not deploy the software rasterizer library. --> + <Arg Condition="'%(Name)' == 'QtDeployNoOpenglSw' AND '%(Value)' == 'true'" + >--no-opengl-sw</Arg> + </Options> + <Options> + <!-- Qt libraries can be added by passing their name (-xml) ... --> + <Arg Condition="'%(Name)' == 'QtDeployIncludeModules'">-%(Value)</Arg> + </Options> + <Options> + <!-- ... or removed by passing the name prepended by -no- (-no-xml). --> + <Arg Condition="'%(Name)' == 'QtDeployExcludeModules'">--no-%(Value)</Arg> + </Options> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Run windeployqt + // --> + <PropertyGroup> + <Cmd><![CDATA["$(QtToolsPath)\windeployqt.exe"]]></Cmd> + <Cmd>$(Cmd) --list target</Cmd> + <Cmd Condition="'%(Options.Arg)' != ''">$(Cmd) %(Options.Arg)</Cmd> + <Cmd Condition="'%(Options.PosArg)' != ''">$(Cmd) %(Options.PosArg)</Cmd> + </PropertyGroup> + <Message Importance="high" Text="windeployqt: $(Cmd)" /> + <Exec Command="$(Cmd) > "$(QtDeployLog)"" /> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Read deploy log into QtDeployed list + // --> + <ReadLinesFromFile File="$(QtDeployLog)"> + <Output TaskParameter="Lines" ItemName="QtDeployed" /> + </ReadLinesFromFile> + <ItemGroup> + <QtDeployed Remove="@(QtDeployed)" Condition="!Exists('%(Fullpath)')"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Mark deployed files as source content for VS deployment project + // --> + <ItemGroup> + <None Include="@(QtDeployed)" Condition="'%(QtDeploy.QtDeployVsContent)' == 'true'"> + <DeploymentContent>true</DeploymentContent> + <RootFolder>$(ProjectDir)</RootFolder> + </None> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Log output files; used by VS on clean and up-to-date check + // --> + <ItemGroup> + <QtDeployLog Include="$(QtDeployLog)"/> + <QtDeployed Include="$(QtDeployLog)"/> + </ItemGroup> + <WriteLinesToFile + File="$(TLogLocation)windeployqt.read.1u.tlog" + Lines="^$(ProjectPath)" + Overwrite="true" Encoding="Unicode"/> + <WriteLinesToFile + File="$(TLogLocation)windeployqt.write.1u.tlog" + Lines="^$(ProjectPath);@(QtDeployLog->'%(Fullpath)')" + Overwrite="true" Encoding="Unicode"/> + <WriteLinesToFile + File="$(TLogLocation)$(ProjectName).write.1u.tlog" + Lines="^$(ProjectFileName);@(QtDeployed->'%(Fullpath)')" + Overwrite="false" Encoding="Unicode"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <Cmd/> + </PropertyGroup> + <ItemGroup> + <Options Remove="@(Options)"/> + <QtDeploy Remove="@(QtDeploy)"/> + <QtDeployLog Remove="@(QtDeployLog)"/> + <QtDeployed Remove="@(QtDeployed)"/> + </ItemGroup> + </Target> +</Project> diff --git a/QtMSBuild/QtMsBuild/deploy/qtdeploy.xml b/QtMSBuild/QtMsBuild/deploy/qtdeploy.xml new file mode 100644 index 0000000..0244483 --- /dev/null +++ b/QtMSBuild/QtMsBuild/deploy/qtdeploy.xml @@ -0,0 +1,366 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// windeployqt Property Page +// +// --> +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule70_Deploy" + PageTemplate="tool" + DisplayName="Qt Deploy Tool" + Order="999"> + <Rule.DataSource> + <DataSource + Persistence="UserFile" + ItemType="QtDeploy" + HasConfigurationCondition="true"/> + </Rule.DataSource> + <Rule.Categories> + <Category Name="General" DisplayName="windeployqt"/> + </Rule.Categories> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Application Binary + // --> + <StringListProperty + Name="QtDeployFiles" DisplayName="Application Binary"> + <StringListProperty.Description> + <sys:String> + <![CDATA[Binaries or directory containing the application binary ( [files] ).]]> + </sys:String> + </StringListProperty.Description> + </StringListProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Deployment Directory + // --> + <StringProperty + Name="QtDeployDir" DisplayName="Deployment Directory"> + <StringProperty.Description> + <sys:String> + <![CDATA[Use directory instead of binary directory ( --dir <directory> ).]]> + </sys:String> + </StringProperty.Description> + </StringProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Library Directory + // --> + <StringProperty + Name="QtDeployLibDir" DisplayName="Library Directory"> + <StringProperty.Description> + <sys:String> + <![CDATA[Copy libraries to path ( --libdir <path> ).]]> + </sys:String> + </StringProperty.Description> + </StringProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Plugin Directory + // --> + <StringProperty + Name="QtDeployPluginDir" DisplayName="Plugin Directory"> + <StringProperty.Description> + <sys:String> + <![CDATA[Copy plugins to path ( --plugindir <path> ).]]> + </sys:String> + </StringProperty.Description> + </StringProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Debug/Release Binaries + // --> + <EnumProperty + Name="QtDeployDebugRelease" DisplayName="Debug/Release Binaries"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Assume debug or release binaries ( --debug | --release ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Default"/> + <EnumValue Name="debug" DisplayName="Debug ( --debug )"/> + <EnumValue Name="release" DisplayName="Release ( --release )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Deploy PDB Files + // --> + <EnumProperty + Name="QtDeployPdb" DisplayName="Deploy PDB Files"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Deploy .pdb files (MSVC) ( --pdb ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --pdb )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // + // --> + <EnumProperty + Name="QtDeployForce" DisplayName="Force Update"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Force updating files ( --force ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --force )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Qt5Core Patch + // --> + <EnumProperty + Name="QtDeployNoPatchQt" DisplayName="Skip Qt5Core Patch"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Do not patch the Qt5Core library ( --no-patchqt ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-patchqt )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Plugins + // --> + <EnumProperty + Name="QtDeployNoPlugins" DisplayName="Skip Plugins"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Skip plugin deployment ( --no-plugins ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-plugins )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Libraries + // --> + <EnumProperty + Name="QtDeployNoLibraries" DisplayName="Skip Libraries"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Skip library deployment ( --no-libraries ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-libraries )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // QML Directories Root + // --> + <StringProperty + Name="QtDeployQmlDir" DisplayName="QML Directories Root"> + <StringProperty.Description> + <sys:String> + <![CDATA[Scan for QML-imports starting from directory ( --qmldir <directory> ).]]> + </sys:String> + </StringProperty.Description> + </StringProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // QML Modules Path + // --> + <StringListProperty + Name="QtDeployQmlImport" DisplayName="QML Modules Path"> + <StringListProperty.Description> + <sys:String> + <![CDATA[Add the given path to the QML module search locations ( --qmlimport <directory> ).]]> + </sys:String> + </StringListProperty.Description> + </StringListProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Qt Quick Imports + // --> + <EnumProperty + Name="QtDeployNoQuickImport" DisplayName="Skip Qt Quick Imports"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Skip deployment of Qt Quick imports ( --no-quick-import ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-quick-import )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Translations + // --> + <EnumProperty + Name="QtDeployNoTranslations" DisplayName="Skip Translations"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Skip deployment of translations ( --no-translations ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-translations )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Direct3D Compiler + // --> + <EnumProperty + Name="QtDeployNoSystemD3dCompiler" DisplayName="Skip Direct3D Compiler"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Skip deployment of the system D3D compiler ( --no-system-d3d-compiler ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-system-d3d-compiler )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Virtual Keyboard + // --> + <EnumProperty + Name="QtDeployNoVirtualKeyboard" DisplayName="Skip Virtual Keyboard"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Disable deployment of the Virtual Keyboard ( --no-virtualkeyboard ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-virtualkeyboard )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Compiler Runtime + // --> + <EnumProperty + Name="QtDeployCompilerRuntime" DisplayName="Compiler Runtime"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Deploy compiler runtime (Desktop only).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Default"/> + <EnumValue Name="deploy" DisplayName="Deploy ( --compiler-runtime )"/> + <EnumValue Name="skip" DisplayName="Do Not Deploy ( --no-compiler-runtime )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // WebKit2 Deployment + // --> + <EnumProperty + Name="QtDeployWebkit2" DisplayName="WebKit2 Deployment"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Deployment of WebKit2 (web process).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Default"/> + <EnumValue Name="deploy" DisplayName="Deploy ( --webkit2 )"/> + <EnumValue Name="skip" DisplayName="Do Not Deploy ( --no-webkit2 )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // ANGLE Deployment + // --> + <EnumProperty + Name="QtDeployAngle" DisplayName="ANGLE Deployment"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Deployment of ANGLE.]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Default"/> + <EnumValue Name="deploy" DisplayName="Deploy ( --angle )"/> + <EnumValue Name="skip" DisplayName="Do Not Deploy ( --no-angle )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip Rasterizer Library + // --> + <EnumProperty + Name="QtDeployNoOpenglSw" DisplayName="Skip Rasterizer Library"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Do not deploy the software rasterizer library ( --no-opengl-sw ).]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enabled ( --no-opengl-sw )"/> + </EnumProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Include Qt Libraries + // --> + <StringListProperty + Name="QtDeployIncludeModules" DisplayName="Include Qt Libraries"> + <StringListProperty.Description> + <sys:String> + <![CDATA[Qt libraries can be added by passing their name ( -<name> ).]]> + </sys:String> + </StringListProperty.Description> + </StringListProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Exclude Qt Libraries + // --> + <StringListProperty + Name="QtDeployExcludeModules" DisplayName="Exclude Qt Libraries"> + <StringListProperty.Description> + <sys:String> + <![CDATA[Qt libraries can be removed by passing their prepended by --no- ( --no-<name> ).]]> + </sys:String> + </StringListProperty.Description> + </StringListProperty> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Set As Solution Deployment Content + // --> + <EnumProperty + Name="QtDeployVsContent" DisplayName="Set As Solution Deployment Content"> + <EnumProperty.Description> + <sys:String> + <![CDATA[Mark deployed files as solution deployment content.]]> + </sys:String> + </EnumProperty.Description> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="true" DisplayName="Enable"/> + </EnumProperty> + </Rule> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/moc/qt_import.props b/QtMSBuild/QtMsBuild/moc/qt_import.props new file mode 100644 index 0000000..e84bf5b --- /dev/null +++ b/QtMSBuild/QtMsBuild/moc/qt_import.props @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild import + // --> + <PropertyGroup> + <QtMsBuildProps> + $(QtMsBuildProps); + $(MSBuildThisFileDirectory)qtmoc.props + </QtMsBuildProps> + <QtMsBuildTargets> + $(QtMsBuildTargets); + $(MSBuildThisFileDirectory)qtmoc.targets + </QtMsBuildTargets> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/moc/qtmoc.props b/QtMSBuild/QtMsBuild/moc/qtmoc.props new file mode 100644 index 0000000..720a8b3 --- /dev/null +++ b/QtMSBuild/QtMsBuild/moc/qtmoc.props @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild moc property definitions +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeMoc)' != ''" + Project="$(QtMsBuildProps_BeforeMoc)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QtMoc item template + // --> + <ItemDefinitionGroup> + <QtMoc> + <ExecutionDescription>moc %(Identity)</ExecutionDescription> + + <QTDIR Condition="'$(QtVsProjectSettings)' != 'true'" + >$(QTDIR)</QTDIR> + <QTDIR Condition="'$(QtVsProjectSettings)' == 'true'" + >$(QtInstallDir)</QTDIR> + + <InputFile>%(FullPath)</InputFile> + + <OutputFile Condition="'$(QtVsProjectSettings)' != 'true'" + >$(ProjectDir)GeneratedFiles\$(Configuration)\moc_%(Filename).cpp</OutputFile> + + <QtMocDir Condition="'$(QtVsProjectSettings)' == 'true'" + >$(IntDir)moc\</QtMocDir> + <QtMocFileName Condition="'$(QtVsProjectSettings)' == 'true'" + >moc_%(Filename).cpp</QtMocFileName> + + <DynamicSource>output</DynamicSource> + <ParallelProcess>true</ParallelProcess> + <CommandLineTemplate>[AllOptions] [AdditionalOptions]</CommandLineTemplate> + <Outputs>%(OutputFile)</Outputs> + <OverrideClCompile>false</OverrideClCompile> + </QtMoc> + </ItemDefinitionGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterMoc)' != ''" + Project="$(QtMsBuildProps_AfterMoc)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/moc/qtmoc.targets b/QtMSBuild/QtMsBuild/moc/qtmoc.targets new file mode 100644 index 0000000..fff7af8 --- /dev/null +++ b/QtMSBuild/QtMsBuild/moc/qtmoc.targets @@ -0,0 +1,510 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Definitions specific to moc +/////////////////////////////////////////////////////////////////////////////////////////////////// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeMoc)' != ''" + Project="$(QtMsBuildTargets_BeforeMoc)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // --> + <PropertyGroup> + <QtBuildTargets>QtMoc;$(QtBuildTargets)</QtBuildTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Setup item type and property page + // --> + <Choose> + <When Condition="'$(QtVsProjectSettings)' == 'true' AND '$(QtVsProjectClProperties)' == 'true'"> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtmoc_v3.xml" /> + </ItemGroup> + </When> + <Otherwise> + <ItemGroup Condition="'$(QtVsProjectSettings)' == 'false'"> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtmoc.xml" /> + </ItemGroup> + </Otherwise> + </Choose> + <ItemGroup> + <AvailableItemName Include="QtMoc"> + <Targets>Qt;_ClCompile</Targets> + </AvailableItemName> + </ItemGroup> + <PropertyGroup> + <QtMocRuleName>QtRule30_Moc</QtMocRuleName> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtMocInit + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Initialize default metadata + // --> + <Target Name="QtMocInit"> + <!--// Initialize %(OutputFile) --> + <ItemGroup Condition="'$(QtVsProjectSettings)' == 'true' AND '@(QtMoc)' != ''"> + <QtMocAux Include="@(QtMoc)"> + <OutputFile + >$([System.IO.Path]::Combine('%(QtMoc.QtMocDir)','%(QtMoc.QtMocFileName)'))</OutputFile> + </QtMocAux> + <QtMoc Remove="@(QtMoc)"/> + <QtMoc Include="@(QtMocAux)"/> + <QtMocAux Remove="@(QtMocAux)"/> + </ItemGroup> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Add moc output dir to C++ include path + // --> + <ItemGroup Condition="'@(QtMoc)' != ''"> + <QtIncludePath Include="$([System.IO.Path]::GetDirectoryName('%(QtMoc.OutputFile)'))"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtMocPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Prepare to process sources + // --> + <Target Name="QtMocPrepare" DependsOnTargets="QtMocInit" + Inputs="%(QtMoc.Identity)" Outputs="@(QtMoc->'####### Don't skip this target #######')"> + <ItemGroup> + <selected_files Include="$(SelectedFiles)"/> + </ItemGroup> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtMocPrepare @(QtMoc)"/> + + <PropertyGroup> + <selected_files>[@(selected_files->'%(Identity)','][')]</selected_files> + <file>[@(QtMoc->'%(Identity)')]</file> + <output_file>@(QtMoc->'%(OutputFile)')</output_file> + <is_selected Condition="$(selected_files.Contains('$(file)'))">true</is_selected> + <is_selected Condition="!$(selected_files.Contains('$(file)'))">false</is_selected> + </PropertyGroup> + + <!--// Delete output file to force build of source if it was manually selected to build + // (e.g. by the 'Compile' option in the context menu for the file) --> + <Delete Files="$(output_file)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' == 'true'" /> + + <!--// If a source was manually selected to build, remove all other sources --> + <ItemGroup Condition="'@(selected_files)' != ''"> + <QtMoc Remove="@(QtMoc)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' != 'true'" /> + </ItemGroup> + + <!--// Remove sources excluded from build --> + <ItemGroup> + <QtMoc Remove="@(QtMoc)" + Condition="'$(SelectedFiles)' == '' AND '%(QtMoc.ExcludedFromBuild)' == 'true'"/> + </ItemGroup> + + <PropertyGroup> + <selected_files/> + <file/> + <output_file/> + <is_selected/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtMocSetModified + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Set InputChanged flag if source file or dependencies have been modified + // --> + <Target Name="QtMocSetModified" DependsOnTargets="QtMocPrepare" + Condition="'@(QtMoc)' != ''" + Inputs="%(QtMoc.FullPath);%(QtMoc.AdditionalDependencies)" Outputs="@(QtMoc->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtMocSetModified @(QtMoc)" /> + + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="input_changed" /> + </CreateProperty> + <ItemGroup> + <QtMoc> + <InputChanged>$(input_changed)</InputChanged> + </QtMoc> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtMocOverrideCpp + ///////////////////////////////////////////////////////////////////////////////////////////////// + // + // --> + <Import Project="qtmoc_cl.targets"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtMoc + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Process each source file and produce the corresponding QtWork items + // --> + <PropertyGroup> + <QtMocDependsOn> + QtPrepare; + QtMocPrepare; + QtMocSetModified; + QtMocOverrideCpp; + $(QtMocDependsOn) + </QtMocDependsOn> + </PropertyGroup> + <Target Name="QtMoc" + DependsOnTargets="$(QtMocDependsOn)" + BeforeTargets="$(QtMocBeforeTargets)" AfterTargets="$(QtMocAfterTargets)" + Condition="'@(QtMoc)' != ''" + Inputs="%(QtMoc.FullPath);%(QtMoc.AdditionalDependencies);$(MSBuildProjectFile)" + Outputs="@(QtMoc->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="## QtMoc @(QtMoc)" /> + + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="dependencies_changed" /> + </CreateProperty> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Convert string lists in source item properties to lists of items + // --> + <Flatten Items="@(QtMoc)" + Metadata="InputFile; + OutputFile; + IncludePath; + MacFramework; + PreprocessOnly; + Define; + Undefine; + Metadata; + CompilerFlavor; + NoInclude; + PathPrefix; + ForceInclude; + PrependInclude; + Include; + NoNotesWarnings; + NoNotes; + NoWarnings; + IgnoreConflicts; + OptionsFile"> + <Output + TaskParameter="Result" ItemName="LocalOptions" /> + </Flatten> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Translate local paths to build host paths + // --> + <HostTranslatePaths + Items="@(LocalOptions)" + Names="InputFile; + OutputFile; + IncludePath; + MacFramework; + PathPrefix; + ForceInclude; + PrependInclude; + Include; + OptionsFile"> + <Output + TaskParameter="Result" ItemName="options" /> + </HostTranslatePaths> + + <ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Escape double-quotes in macro definitions + // --> + <options> + <Value Condition="('%(Name)' == 'Define' OR '%(Name)' == 'Undefine') + AND ($([System.String]::Copy('%(Value)').Contains(' ')) + OR $([System.String]::Copy('%(Value)').Contains('"')))" + > "$([System.String]::Copy('%(Value)').Replace('"', '\"'))"</Value> + </options> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Remove quotes from all paths + // Escape trailing back-slash in paths + // Add quotes to paths containing spaces + // --> + <options> + <Value Condition="'%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'IncludePath' OR '%(Name)' == 'MacFramework' OR '%(Name)' == 'PathPrefix' + OR '%(Name)' == 'ForceInclude' OR '%(Name)' == 'PrependInclude' OR '%(Name)' == 'Include' + OR '%(Name)' == 'OptionsFile'" + >$([System.String]::Copy('%(Value)').Replace('"', ''))</Value> + </options> + + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'IncludePath' OR '%(Name)' == 'MacFramework' OR '%(Name)' == 'PathPrefix' + OR '%(Name)' == 'ForceInclude' OR '%(Name)' == 'PrependInclude' OR '%(Name)' == 'Include' + OR '%(Name)' == 'OptionsFile') + AND $([System.String]::Copy('%(Value)').Contains(' ')) + AND $([System.String]::Copy('%(Value)').EndsWith('\'))" + >%(Value)\</Value> + </options> + + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'IncludePath' OR '%(Name)' == 'MacFramework' OR '%(Name)' == 'PathPrefix' + OR '%(Name)' == 'ForceInclude' OR '%(Name)' == 'PrependInclude' OR '%(Name)' == 'Include' + OR '%(Name)' == 'OptionsFile') + AND $([System.String]::Copy('%(Value)').Contains(' '))" + >"%(Value)"</Value> + </options> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate tool command line arguments + // --> + <!--// [header-file] Header file to read from --> + <options> + <Value Condition="'%(Name)' == 'InputFile'">%(Value)</Value> + </options> + + <!--// -o <file> Write output to file --> + <options> + <Value Condition="'%(Name)' == 'OutputFile'">-o %(Value)</Value> + </options> + + <!--// -I <dir> Add dir to the include path for header files --> + <options> + <Value Condition="'%(Name)' == 'IncludePath'">-I%(Value)</Value> + </options> + + <!--// -F <framework> Add macOS framework to the include path for headers --> + <options> + <Value Condition="'%(Name)' == 'MacFramework'">-F %(Value)</Value> + </options> + + <!--// -E Preprocess only; do not generate meta object code --> + <options> + <Value Condition="'%(Name)' == 'PreprocessOnly' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'PreprocessOnly' AND '%(Value)' == 'true'">-E</Value> + </options> + + <!--// -D <macro[=def]> Define macro, with optional definition --> + <options> + <Value Condition="'%(Name)' == 'Define'">-D%(Value)</Value> + </options> + + <!--// -U <macro> Undefine macro--> + <options> + <Value Condition="'%(Name)' == 'Undefine'">-U%(Value)</Value> + </options> + + <!--// -M <key=value> Add key/value pair to plugin meta data --> + <options> + <Value Condition="'%(Name)' == 'Metadata'">-M%(Value)</Value> + </options> + + <!--// -compiler-flavor <flavor> Set the compiler flavor: either "msvc" or "unix" --> + <options> + <Value Condition="'%(Name)' == 'CompilerFlavor'">--compiler-flavor %(Value)</Value> + </options> + + <!--// -i Do not generate an #include statement --> + <options> + <Value Condition="'%(Name)' == 'NoInclude' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'NoInclude' AND '%(Value)' == 'true'">-i</Value> + </options> + + <!--// -p <path> Path prefix for included file --> + <options> + <Value Condition="'%(Name)' == 'PathPrefix'">-p%(Value)</Value> + </options> + + <!--// -f <file> Force #include <file> (overwrite default) --> + <options> + <Value Condition="'%(Name)' == 'ForceInclude'">-f %(Value)</Value> + </options> + + <!--// -b <file> Prepend #include <file> (preserve default include) --> + <options> + <Value Condition="'%(Name)' == 'PrependInclude'">-b %(Value)</Value> + </options> + + <!--// -include <file> Parse <file> as an #include before the main source(s) --> + <options> + <Value Condition="'%(Name)' == 'Include'">--include %(Value)</Value> + </options> + + <!--// -n <which> Do not display notes (-nn) or warnings (-nw) --> + <options> + <Value Condition="'%(Name)' == 'NoNotesWarnings'">-n%(Value)</Value> + </options> + + <!--// -no-notes Do not display notes --> + <options> + <Value Condition="'%(Name)' == 'NoNotes' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'NoNotes' AND '%(Value)' == 'true'">--no-notes</Value> + </options> + + <!--// -no-warnings Do not display warnings (implies -no-notes) --> + <options> + <Value Condition="'%(Name)' == 'NoWarnings' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'NoWarnings' AND '%(Value)' == 'true'">--no-warnings</Value> + </options> + + <!--// -ignore-option-clashes Ignore all options that conflict with compilers --> + <options> + <Value Condition="'%(Name)' == 'IgnoreConflicts' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'IgnoreConflicts' AND '%(Value)' == 'true'" + >--ignore-option-clashes</Value> + </options> + + <!--// [@option-file] Read additional options from option-file--> + <options> + <Value Condition="'%(Name)' == 'OptionsFile'">@%(Value)</Value> + </options> + </ItemGroup> + <PropertyGroup> + <options>@(options->'%(Value)', ' ')</options> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Aux properties + // --> + <PropertyGroup> + <!--// Force modified flag if source was manually selected to build --> + <input_changed Condition="'$(SelectedFiles)' != ''" + >true</input_changed> + <input_changed Condition="'$(SelectedFiles)' == ''" + >%(QtMoc.InputChanged)</input_changed> + + <!--// Run work in parallel processes --> + <run_parallel Condition="'@(QtMoc)' != '' + AND '%(QtMoc.ParallelProcess)' == 'true' + AND '$(SelectedFiles)' == ''" + >true</run_parallel> + + <!--// Run work in single process --> + <run_single Condition="'@(QtMoc)' != '' + AND ('%(QtMoc.ParallelProcess)' != 'true' + OR '$(SelectedFiles)' != '')" + >true</run_single> + + <!--// Get relative path to output --> + <output_relative +>$([MSBuild]::MakeRelative($(ProjectDir), %(QtMoc.OutputFile)).TrimStart('\'))</output_relative> + + <!--// Get relative path to input --> + <input_relative +>$([MSBuild]::MakeRelative($(ProjectDir), %(QtMoc.InputFile)).TrimStart('\'))</input_relative> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Create work item + // --> + <ItemGroup Condition="'$(run_parallel)' == 'true' OR '$(run_single)' == 'true'"> + <QtWork Include="@(QtMoc)"> + <WorkType>moc</WorkType> + <ToolPath Condition="'$(QtVsProjectSettings)' == 'true'" + >$(QtToolsPath)/moc</ToolPath> + <ToolPath Condition="'$(QtVsProjectSettings)' != 'true'" + >%(QtMoc.QTDIR)\bin\moc.exe</ToolPath> + <Options>$(options)</Options> + <Message>%(QtMoc.ExecutionDescription)</Message> + <DependenciesChanged>$(dependencies_changed)</DependenciesChanged> + <InputChanged>$(input_changed)</InputChanged> + <ParallelBuild Condition="'$(run_parallel)' == 'true'">true</ParallelBuild> + <ParallelBuild Condition="'$(run_single)' == 'true'">false</ParallelBuild> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////// + // C++ dynamic source --> + <ClCompile Condition="'%(QtMoc.DynamicSource)' == 'output'">$(output_relative)</ClCompile> + <ClCompile Condition="'%(QtMoc.DynamicSource)' == 'input'">$(input_relative)</ClCompile> + </QtWork> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <options/> + <dependencies_changed/> + <input_changed/> + <run_parallel/> + <run_single/> + <output_relative/> + <input_relative/> + </PropertyGroup> + <ItemGroup> + <LocalOptions Remove="@(LocalOptions)"/> + <options Remove="@(options)"/> + <selected_files Remove="@(selected_files)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterMoc)' != ''" + Project="$(QtMsBuildTargets_AfterMoc)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/moc/qtmoc.xml b/QtMSBuild/QtMsBuild/moc/qtmoc.xml new file mode 100644 index 0000000..4a43f2d --- /dev/null +++ b/QtMSBuild/QtMsBuild/moc/qtmoc.xml @@ -0,0 +1,316 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Defines the fields included in the moc property page, as well as any +// other metadata associated to QtMoc items +// --> +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule30_Moc" + PageTemplate="tool" + DisplayName="Qt Meta-Object Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtMoc" /> + </Rule.DataSource> + <Rule.Categories> + <Category + Name="General"> + <Category.DisplayName> + <sys:String>moc.exe</sys:String> + </Category.DisplayName> + </Category> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtMoc" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False"/> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Description="Path to installation of Qt."/> + <StringProperty + Name="InputFile" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Input File" + Description="Specifies the full path of the input file. (<file>)" + Switch=""[value]"" /> + <StringProperty + Name="OutputFile" + Category="General" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Output File" + Description="Specifies the full path of the output file. (-o <file>)" + Switch="-o "[value]"" /> + <StringListProperty + Name="IncludePath" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Include Path" + Description= +"Add <dir> to the include path for header files; separate with semicolons if more than one. +(-I<dir>)" + Switch=""-I[value]"" /> + <StringProperty + Name="MacFramework" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="macOS Framework" + Description= +"Add macOS <framework> to the include path for header files. (-F <framework>)" + Switch="-F "[value]"" + Visible="False"/> + <BoolProperty + Name="PreprocessOnly" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Preprocess Only" + Description="Preprocess only; do not generate meta object code. (-E)" + Switch="-E" + Visible="False"/> + <StringListProperty + Name="Define" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Macro Definitions" + Description= +"Define macro, with optional definition; separate with semicolons if more than one. +(-D<macro>[=<def>])" + Switch="-D " /> + <StringListProperty + Name="Undefine" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Undefine Macro Definitions" + Description="Undefine macro; separate with semicolons if more than one. (-U<macro>)" + Switch="-U[value]" /> + <StringListProperty + Name="Metadata" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Plugin Meta Data" + Description= +"Add key/value pair to plugin meta data; separate with semicolons if more than one. +(-M<key=value>)" + Switch="-M[value]" /> + <StringProperty + Name="CompilerFlavor" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Compiler Flavor" + Description= +"Set the compiler <flavor>: either "msvc" or "unix". +(--compiler-flavor <flavor>)" + Switch="--compiler-flavor [value]"/> + <BoolProperty + Name="NoInclude" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Include" + Description="Do not generate an #include statement. (-i)" + Switch="-i"/> + <StringProperty + Name="PathPrefix" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Path Prefix" + Description="<path> prefix for included file. (-p <path>)" + Switch="-p [value]"/> + <StringListProperty + Name="ForceInclude" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Force Include" + Description= +"Force #include <file> (overwrite default); separate with semicolons if more than one. +(-f <file>)" + Switch="-f "[value]""/> + <StringListProperty + Name="PrependInclude" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Prepend Include" + Description= +"Prepend #include <file> (preserve default); separate with semicolons if more than one. +(-b <file>)" + Switch="-b "[value]""/> + <StringListProperty + Name="Include" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Include" + Description= +"Parse <file> as an #include before the main source(s); separate with semicolons if more +than one. (--include <file>)" + Switch="--include "[value]""/> + <StringListProperty + Name="NoNotesWarnings" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Notes Or Warnings" + Description= +"Do not display notes (-nn) or warnings (-nw); Compatibility option; separate with semicolons if +more than one. (-n<n|w>)" + Switch="-n[value]" + Visible="False"/> + <BoolProperty + Name="NoNotes" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Notes" + Description="Do not display notes. (--no-notes)" + Switch="--no-notes"/> + <BoolProperty + Name="NoWarnings" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Warnings" + Description="Do not display warnings (implies --no-notes). (--no-warnings)" + Switch="--no-warnings"/> + <BoolProperty + Name="IgnoreConflicts" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Ignore Conflicts" + Description= +"Ignore all options that conflict with compilers, like -pthread conflicting with moc's -p option. +(--ignore-option-clashes)" + Switch="--ignore-option-clashes"/> + <StringProperty + Name="OptionsFile" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Options File" + Description="Read additional command-line options from <file>. (@<file>)" + Switch=""@[value]"" /> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtMocBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtMocBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtMocAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtMocAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <EnumProperty + Name="DynamicSource" + DisplayName="Dynamic C++ Source" + Description="Add file to list of C++ sources during build."> + <EnumValue + Name="output" + DisplayName="Output File" + Description="Use output as dynamic C++ source."/> + <EnumValue + Name="input" + DisplayName="Input File" + Description="Use input as dynamic C++ source."/> + <EnumValue + Name="false" + DisplayName="Disable" + Description="Disable dynamic source."/> + </EnumProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + </Rule> + <ItemType + Name="QtMoc" + DisplayName="Qt Meta-Object Compiler (moc)" /> + <ContentType + Name="QtMoc" + DisplayName="Qt Meta-Object Compiler (moc)" + ItemType="QtMoc" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/moc/qtmoc_cl.targets_TT b/QtMSBuild/QtMsBuild/moc/qtmoc_cl.targets_TT new file mode 100644 index 0000000..e72c5c3 --- /dev/null +++ b/QtMSBuild/QtMsBuild/moc/qtmoc_cl.targets_TT @@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="targets" #> +<#@include file="$(SolutionDir)\common.tt" #> +<#@assembly Name="System.Xml" #> +<#@assembly Name="System.Xml.Linq" #> +<#@import namespace="System.Collections.Generic" #> +<#@import namespace="System.Linq" #> +<#@import namespace="System.Text" #> +<#@import namespace="System.Xml" #> +<#@import namespace="System.Xml.Linq" #> +<# + var pathRuleCl = Path.Combine(VC_TARGETS_PATH, "1033", "cl.xml"); + var xmlRule = XmlLoad(File.ReadAllText(pathRuleCl, Encoding.UTF8)); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Target Name="QtMocOverrideCpp" DependsOnTargets="QtMocPrepare;QtGetDefaultClCompile" + Inputs="%(QtMoc.Identity)" Outputs="@(QtMoc->'####### Don't skip this target #######')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtMocOverrideCpp @(QtMoc)" /> + + <PropertyGroup Condition="'$(QtVsProjectClProperties)' == 'true'"> + <override_cl_compile>%(QtMoc.OverrideClCompile)</override_cl_compile> + </PropertyGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'extend'"> + <QtMoc> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().Equals("StringListProperty")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .Select(x => new XElement((string)x.Attribute("Name"), + string.Format( + "@(ClCompile->WithMetadataValue('Identity','DefaultClCompile')->'%({0})');%(QtMoc.{0})", + (string)x.Attribute("Name"))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <AdditionalCppOptions + Condition="'%(QtMoc.AdditionalCppOptions)' != ''" + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalOptions)') %(QtMoc.AdditionalCppOptions)</AdditionalCppOptions> + <AdditionalCppOptions + Condition="'%(QtMoc.AdditionalCppOptions)' == ''" + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalOptions)')</AdditionalCppOptions> + </QtMoc> + <QtMoc> + <IncludePath>%(QtMoc.AdditionalIncludeDirectories)</IncludePath> + <Define>%(QtMoc.PreprocessorDefinitions)</Define> + <Undefine>%(QtMoc.UndefinePreprocessorDefinitions)</Undefine> + </QtMoc> + </ItemGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'replace'"> + <QtMoc> + <IncludePath Condition="'%(QtMoc.AdditionalIncludeDirectories)' != ''" + >%(QtMoc.AdditionalIncludeDirectories)</IncludePath> + <IncludePath Condition="'%(QtMoc.AdditionalIncludeDirectories)' == ''" + >@(ClCompile-> + WithMetadataValue('Identity', 'DefaultClCompile')-> + '%(AdditionalIncludeDirectories)')</IncludePath> + <Define Condition="'%(QtMoc.PreprocessorDefinitions)' != ''" + >%(QtMoc.PreprocessorDefinitions)</Define> + <Define Condition="'%(QtMoc.PreprocessorDefinitions)' == ''" + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(PreprocessorDefinitions)')</Define> + <Undefine Condition="'%(QtMoc.UndefinePreprocessorDefinitions)' == ''" + >%(QtMoc.UndefinePreprocessorDefinitions)</Undefine> + <Undefine Condition="'%(QtMoc.UndefinePreprocessorDefinitions)' != ''" + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(UndefinePreprocessorDefinitions)')</Undefine> + </QtMoc> + </ItemGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'false'"> + <QtMoc> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().EndsWith("Property")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .Select(x => new XElement((string)x.Attribute("Name"), + new XAttribute("Condition", + string.Format("'%(QtMoc.{0})' != ''", (string)x.Attribute("Name"))), + string.Format( + "@(ClCompile->WithMetadataValue('Identity','DefaultClCompile')->'%({0})')", + (string)x.Attribute("Name"))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <IncludePath + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalIncludeDirectories)')</IncludePath> + <Define + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(PreprocessorDefinitions)')</Define> + <Undefine + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(UndefinePreprocessorDefinitions)')</Undefine> + <AdditionalCppOptions + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalOptions)')</AdditionalCppOptions> + </QtMoc> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <override_cl_compile/> + </PropertyGroup> + </Target> +</Project> diff --git a/QtMSBuild/QtMsBuild/moc/qtmoc_v3.xml_TT b/QtMSBuild/QtMsBuild/moc/qtmoc_v3.xml_TT new file mode 100644 index 0000000..3bf7054 --- /dev/null +++ b/QtMSBuild/QtMsBuild/moc/qtmoc_v3.xml_TT @@ -0,0 +1,356 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="xml" #> +<#@include file="$(SolutionDir)\common.tt" #> +<#@assembly Name="System.Xml" #> +<#@assembly Name="System.Xml.Linq" #> +<#@import namespace="System.Collections.Generic" #> +<#@import namespace="System.Linq" #> +<#@import namespace="System.Text" #> +<#@import namespace="System.Xml" #> +<#@import namespace="System.Xml.Linq" #> +<# + var pathRuleCl = Path.Combine(VC_TARGETS_PATH, "1033", "cl.xml"); + var xmlRule = XmlLoad(File.ReadAllText(pathRuleCl, Encoding.UTF8)); + var categoryNames = xmlRule + .Elements("Rule.Categories") + .Elements("Category") + .Where(x => x.Attribute("Subtype") == null) + .ToDictionary( + x => (string)x.Attribute("Name"), + x => (string)x.Attribute("DisplayName")); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule30_Moc" + PageTemplate="tool" + DisplayName="Qt Meta-Object Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtMoc" /> + </Rule.DataSource> + <Rule.Categories> + <Category Name="General" DisplayName="moc"/> + <Category Name="MocCpp" DisplayName="Override C/C++"/> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtMoc" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False"/> + <StringProperty + Name="QtMocDir" + Category="General" + DisplayName="Output Directory" + Description="Specifies the path of the generated moc output directory."/> + <StringProperty + Name="QtMocFileName" + Category="General" + DisplayName="Output File Name" + Description="Specifies the name of the generated moc output file."/> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Visible="false"/> + <StringProperty + Name="InputFile" + DisplayName="Input File" + Visible="false"/> + <StringProperty + Name="OutputFile" + DisplayName="Output File" + Visible="false"/> + <StringProperty + Name="MacFramework" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="macOS Framework" + Description= +"Add macOS <framework> to the include path for header files. (-F <framework>)" + Switch="-F "[value]"" + Visible="False"/> + <BoolProperty + Name="PreprocessOnly" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Preprocess Only" + Description="Preprocess only; do not generate meta object code. (-E)" + Switch="-E" + Visible="False"/> + <StringListProperty + Name="Metadata" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Plugin Meta Data" + Description= +"Add key/value pair to plugin meta data; separate with semicolons if more than one. +(-M<key=value>)" + Switch="-M[value]" /> + <StringProperty + Name="CompilerFlavor" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Compiler Flavor" + Description= +"Set the compiler <flavor>: either "msvc" or "unix". +(--compiler-flavor <flavor>)" + Switch="--compiler-flavor [value]"/> + <BoolProperty + Name="NoInclude" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Include" + Description="Do not generate an #include statement. (-i)" + Switch="-i"/> + <StringProperty + Name="PathPrefix" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Path Prefix" + Description="<path> prefix for included file. (-p <path>)" + Switch="-p [value]"/> + <StringListProperty + Name="ForceInclude" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Force Include" + Description= +"Force #include <file> (overwrite default); separate with semicolons if more than one. +(-f <file>)" + Switch="-f "[value]""/> + <StringListProperty + Name="PrependInclude" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Prepend Include" + Description= +"Prepend #include <file> (preserve default); separate with semicolons if more than one. +(-b <file>)" + Switch="-b "[value]""/> + <StringListProperty + Name="Include" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Include" + Description= +"Parse <file> as an #include before the main source(s); separate with semicolons if more +than one. (--include <file>)" + Switch="--include "[value]""/> + <StringListProperty + Name="NoNotesWarnings" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Notes Or Warnings" + Description= +"Do not display notes (-nn) or warnings (-nw); Compatibility option; separate with semicolons if +more than one. (-n<n|w>)" + Switch="-n[value]" + Visible="False"/> + <BoolProperty + Name="NoNotes" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Notes" + Description="Do not display notes. (--no-notes)" + Switch="--no-notes"/> + <BoolProperty + Name="NoWarnings" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="No Warnings" + Description="Do not display warnings (implies --no-notes). (--no-warnings)" + Switch="--no-warnings"/> + <BoolProperty + Name="IgnoreConflicts" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Ignore Conflicts" + Description= +"Ignore all options that conflict with compilers, like -pthread conflicting with moc's -p option. +(--ignore-option-clashes)" + Switch="--ignore-option-clashes"/> + <StringProperty + Name="OptionsFile" + HelpUrl="http://doc.qt.io/qt-5/moc.html" + DisplayName="Options File" + Description="Read additional command-line options from <file>. (@<file>)" + Switch=""@[value]"" /> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtMocBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtMocBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtMocAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtMocAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <EnumProperty + Name="DynamicSource" + DisplayName="Dynamic C++ Source" + Description="Add file to list of C++ sources during build."> + <EnumValue + Name="output" + DisplayName="Output File" + Description="Use output as dynamic C++ source."/> + <EnumValue + Name="input" + DisplayName="Input File" + Description="Use input as dynamic C++ source."/> + <EnumValue + Name="false" + DisplayName="Disable" + Description="Disable dynamic source."/> + </EnumProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + <StringListProperty Name="IncludePath" Visible="false"/> + <StringListProperty Name="Define" Visible="false"/> + <StringListProperty Name="Undefine" Visible="false"/> + <EnumProperty + Name="OverrideClCompile" + Category="MocCpp" + DisplayName="Override C/C++ Properties" + Description="Define how C/C++ properties are redefined for moc"> + <EnumValue Name="extend" DisplayName= +"Extend (append override definitions to C/C++ list properties; replace other properties)"/> + <EnumValue Name="replace" DisplayName= +"Replace (use override definitions instead of C/C++ properties)"/> + <EnumValue Name="false" DisplayName= +"Disabled (use C/C++ properties; ignore override definitions)"/> + </EnumProperty> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().EndsWith("Property")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .GroupBy(x => (string)x.Attribute("Category")) + .SelectMany(y => y.Select(x => + { + x = new XElement(x); + x.SetAttributeValue("Category", "MocCpp"); + x.SetAttributeValue("Description", "(overrides C/C++ property) " + + (string)x.Attribute("Description")); + return x; + }) + .Prepend(new XElement("StringProperty", + new XAttribute("Name", "QtCppHeader_" + y.Key.Replace(' ', '_')), + new XAttribute("Category", "MocCpp"), + new XAttribute("ReadOnly", "true"), + new XAttribute("DisplayName", string.Format("{0} {1} {2}", + new string('#', 10), categoryNames[y.Key], new string('#', 30))))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <StringProperty + Name="AdditionalCppOptions" + DisplayName="Additional Options" + Description="Additional Options." + Category="MocCpp"/> + </Rule> + <ItemType + Name="QtMoc" + DisplayName="Qt Meta-Object Compiler (moc)" /> + <ContentType + Name="QtMoc" + DisplayName="Qt Meta-Object Compiler (moc)" + ItemType="QtMoc" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/qml/qt_import.props b/QtMSBuild/QtMsBuild/qml/qt_import.props new file mode 100644 index 0000000..e736247 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qml/qt_import.props @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild import + // --> + <PropertyGroup> + <QtMsBuildProps_AfterRcc> + $(QtMsBuildProps_AfterRcc); + $(MSBuildThisFileDirectory)qtqml.props; + $(MSBuildThisFileDirectory)qtqml_cache.props; + $(MSBuildThisFileDirectory)qtqml_static.props + </QtMsBuildProps_AfterRcc> + <QtMsBuildTargets_AfterRcc> + $(QtMsBuildTargets_AfterRcc); + $(MSBuildThisFileDirectory)qtqml.targets; + $(MSBuildThisFileDirectory)qtqml_cache.targets; + $(MSBuildThisFileDirectory)qtqml_static.targets + </QtMsBuildTargets_AfterRcc> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/qml/qtqml.props b/QtMSBuild/QtMsBuild/qml/qtqml.props new file mode 100644 index 0000000..0a10b50 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qml/qtqml.props @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild QML property definitions +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeQml)' != ''" + Project="$(QtMsBuildProps_BeforeQml)"/> + + <!-- Default QML work dir = $(IntDir)\qml + * can be overridden in property sheets --> + <PropertyGroup> + <QtQmlIntDir Condition="'$(QtQmlIntDir)' == ''" + >$([System.IO.Path]::Combine('$(ProjectDir)', '$(IntDir)', 'qml'))</QtQmlIntDir> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterQml)' != ''" + Project="$(QtMsBuildProps_AfterQml)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qml/qtqml.targets b/QtMSBuild/QtMsBuild/qml/qtqml.targets new file mode 100644 index 0000000..c93acbd --- /dev/null +++ b/QtMSBuild/QtMsBuild/qml/qtqml.targets @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// QML targets +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeQml)' != ''" + Project="$(QtMsBuildTargets_BeforeQml)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // * add QtQml to the list of targets to run during build + // --> + <PropertyGroup> + <QtBuildTargets>$(QtBuildTargets);QtQml</QtBuildTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QML build entry point + // --> + <Target Name="QtQml" DependsOnTargets="QtQmlCreateItems"> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Create QtQml items for project files with extension '.qml' or '.js' + // --> + <Target Name="QtQmlCreateItems"> + <ItemGroup> + <QtQml Include="@(None)" + Condition="'%(None.Extension)' == '.qml' OR '%(None.Extension)' == '.js'" /> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterQml)' != ''" + Project="$(QtMsBuildTargets_AfterQml)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qml/qtqml_cache.props b/QtMSBuild/QtMsBuild/qml/qtqml_cache.props new file mode 100644 index 0000000..55ca2d9 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qml/qtqml_cache.props @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild QML property definitions +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeQmlCache)' != ''" + Project="$(QtMsBuildProps_BeforeQmlCache)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QtRcc item template, QML cache generation properties + // --> + <ItemDefinitionGroup> + <QtRcc> + <QmlCacheGenerate>false</QmlCacheGenerate> + <!-- + // QML cache generation --> + <QmlCacheGenMessage + ><![CDATA[Qt Quick Compiler: compiling %<Identity> ahead of time]]></QmlCacheGenMessage> + <QmlCacheOutputFile + ><![CDATA[$(ProjectDir)GeneratedFiles\%<Filename>_%<Suffix>.cpp]]></QmlCacheOutputFile> + <!-- + // QRC filtering --> + <QmlCacheFilterMessage + >Qt Quick Compiler: filtering %(Identity)</QmlCacheFilterMessage> + <QmlCacheFilteredFile + >$(ProjectDir)GeneratedFiles\%(Filename)_qmlcache.qrc</QmlCacheFilteredFile> + <QmlCacheFilteredOutputFile + >$(ProjectDir)GeneratedFiles\qrc_%(Filename)_qmlcache.cpp</QmlCacheFilteredOutputFile> + <QmlCacheFilteredInitFuncName + >%(Filename)_qmlcache</QmlCacheFilteredInitFuncName> + <!-- + // QML cache loader generation --> + <QmlCacheLoaderMessage + >Qt Quick Compiler: generating cache loader</QmlCacheLoaderMessage> + <QmlCacheLoaderFile + >$(ProjectDir)GeneratedFiles\qmlcache_loader.cpp</QmlCacheLoaderFile> + </QtRcc> + </ItemDefinitionGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterQmlCache)' != ''" + Project="$(QtMsBuildProps_AfterQmlCache)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qml/qtqml_cache.targets b/QtMSBuild/QtMsBuild/qml/qtqml_cache.targets new file mode 100644 index 0000000..33e5d51 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qml/qtqml_cache.targets @@ -0,0 +1,625 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Targets required to build the QML cache (aka. qtquickcompiler) +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeQmlCache)' != ''" + Project="$(QtMsBuildTargets_BeforeQmlCache)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // --> + <PropertyGroup> + <QtBuildTargets>$(QtBuildTargets);QtQmlCache</QtBuildTargets> + <QtRccDependsOn>$(QtRccDependsOn);QtQmlCache</QtRccDependsOn> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // TARGET QtQmlCachePrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Pre-process rcc source files marked for QML cache generation + // --> + <Target Name="QtQmlCachePrepare" + DependsOnTargets="QtRccSetDependencies" + BeforeTargets="QtRccPrepare" Condition="'@(QtRcc)' != ''" + Inputs="%(QtRcc.Identity)" Outputs="@(QtRcc->'####### Don't skip this target #######')"> + <ItemGroup> + <selected_files Include="$(SelectedFiles)"/> + </ItemGroup> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtQmlCachePrepare %(QtRcc.Identity)" /> + + <!--// Remove sources excluded from build --> + <ItemGroup> + <QtRcc Remove="@(QtRcc)" + Condition="'$(SelectedFiles)' == '' AND '%(QtRcc.ExcludedFromBuild)' == 'true'"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // $(is_selected) ::= file is selected to build + // --> + <PropertyGroup> + <selected_files>[@(selected_files->'%(Identity)','][')]</selected_files> + <file>[@(QtRcc->'%(Identity)')]</file> + <output_file>@(QtRcc->'%(OutputFile)')</output_file> + <is_selected + Condition="'@(selected_files)' == '' OR $(selected_files.Contains('$(file)'))" + >true</is_selected> + <is_selected + Condition="'@(selected_files)' != '' AND !$(selected_files.Contains('$(file)'))" + >false</is_selected> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Parse QRC file + // @(res_file) ::= names of resource files + // @(res_qml_js) ::= names of QML and JS resource files + // @(res_other) ::= names of other resource files + // --> + <ItemGroup> + <res_file + Condition="'@(QtRcc)' != '' AND '%(QtRcc.QmlCacheGenerate)' == 'true'" + Include="%(QtRcc.ResourceFiles)"/> + </ItemGroup> + <ItemGroup Condition="'@(res_file)' != ''"> + <res_qml_js Include="%(res_file.Identity)" + Condition="'%(res_file.Extension)' == '.qml' OR '%(res_file.Extension)' == '.js'"/> + <res_other Include="%(res_file.Identity)" + Condition="'%(res_file.Extension)' != '.qml' AND '%(res_file.Extension)' != '.js'"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Expand QML and JS build items + // --> + <ItemGroup Condition="'@(res_qml_js)' != ''"> + <template Include="template"> + <InputFile><![CDATA[%<FullPath>]]></InputFile> + <OutputFile>@(QtRcc->'%(QmlCacheOutputFile)')</OutputFile> + <Message>@(QtRcc->'%(QmlCacheGenMessage)')</Message> + <Outputs>%(OutputFile)</Outputs> + <ResourceFile>@(QtRcc->'%(FullPath)')</ResourceFile> + <IsSelected>$(is_selected)</IsSelected> + <AdditionalDependencies>@(QtRcc)</AdditionalDependencies> + </template> + </ItemGroup> + <!-- + // @(QtQmlCacheItem) ::= QML and JS resource files to be cached --> + <Expand Condition="'@(res_qml_js)' != ''" + Items="@(res_qml_js)" BaseItem="@(QtRcc)" Template="@(template)"> + <Output TaskParameter="Result" ItemName="QtQmlCacheItem"/> + </Expand> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Convert rcc sources to QML cache generation filter sources + // --> + <ItemGroup Condition="'@(QtQmlCacheItem)' != ''"> + <!-- + // @(QtRccFilter) ::= QRC files to be filtered --> + <QtRccFilter Include="@(QtRcc)"> + <QtQmlCacheItems>@(QtQmlCacheItem)</QtQmlCacheItems> + <OtherResources>@(res_other->'%(Identity)')</OtherResources> + <InputChanged>false</InputChanged> + <OutputFile>%(QtRcc.QmlCacheFilteredFile)</OutputFile> + <Outputs>%(QtRcc.QmlCacheFilteredFile)</Outputs> + <IsSelected>$(is_selected)</IsSelected> + </QtRccFilter> + <QtRcc Remove="@(QtRcc)"/> + </ItemGroup> + <!-- + // Delete output file to force build of source if it was manually selected to build + // (e.g. by the 'Compile' option in the context menu for the file) --> + <Delete + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' == 'true'" + Files="%(QtRccFilter.OutputFile)"/> + <Delete + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' == 'true'" + Files="%(QtQmlCacheItem.OutputFile)"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <selected_files/> + <file/> + <output_file/> + <is_selected/> + </PropertyGroup> + <ItemGroup> + <res_file Remove="@(res_file)"/> + <res_qml_js Remove="@(res_qml_js)"/> + <res_other Remove="@(res_other)"/> + <template Remove="@(res_other)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // TARGET QtRccFilterSetModified + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Set InputModified flag for modified files (i.e. Inputs newer than Outputs) + // --> + <Target Name="QtRccFilterSetModified" DependsOnTargets="QtQmlCachePrepare" + Inputs="%(QtRccFilter.FullPath);%(QtRccFilter.AdditionalDependencies)" + Outputs="@(QtRccFilter->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true' AND '@(QtRccFilter)' != ''" + Text="## QtRccFilterSetModified %(QtRccFilter.Identity) => %(QtRccFilter.OutputFile)"/> + + <PropertyGroup> + <input_changed/> + </PropertyGroup> + <CreateProperty Condition="'@(QtRccFilter)' != ''" Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="input_changed" /> + </CreateProperty> + <ItemGroup> + <QtRccFilter> + <InputChanged>$(input_changed)</InputChanged> + <IsSelected Condition="'$(input_changed)' == 'true'">true</IsSelected> + </QtRccFilter> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <input_changed/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // TARGET QtRccFilter + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Filter QRC file: + // - Filter QML and JS resources from QRC + // - If other resources are present, generate new QRC with filtered contents + // - Collect data to use in cache loader generation + // --> + <Target Name="QtRccFilter" DependsOnTargets="QtRccFilterSetModified" + Inputs="%(QtRccFilter.FullPath);%(QtRccFilter.AdditionalDependencies);$(MSBuildProjectFile)" + Outputs="@(QtRccFilter->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtRccFilter %(QtRccFilter.Identity) => @(QtRccFilter->'%(OutputFile)')" /> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Set DependenciesChanged flag for modified files or dependencies + // --> + <PropertyGroup> + <dependencies_changed/> + </PropertyGroup> + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="dependencies_changed" /> + </CreateProperty> + <ItemGroup> + <QtRccFilter> + <DependenciesChanged>$(dependencies_changed)</DependenciesChanged> + </QtRccFilter> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // @(input_file) ::= QRC file to filter + // @(filtered_file) ::= filtered QRC file + // --> + <ItemGroup> + <input_file Include="%(QtRccFilter.InputFile)" + Condition="'%(QtRccFilter.QtQmlCacheItems)' != '' AND '%(QtRccFilter.InputFile)' != ''"/> + <filtered_file Include="%(QtRccFilter.QmlCacheFilteredFile)" + Condition="'%(QtRccFilter.QmlCacheFilteredFile)' != ''"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Prepare qmlcachegen command for QRC filtering + // --> + <PropertyGroup> + <!-- + // $(options) ::= qmlcachegen command line options --> + <options Condition="'%(QtRccFilter.QtQmlCacheItems)' != '' AND '@(input_file)' != ''" + >$(options) @(input_file->'--filter-resource-file "%(Identity)"')</options> + <options Condition="'%(QtRccFilter.QtQmlCacheItems)' != '' AND '@(filtered_file)' != ''" + >$(options) @(filtered_file->'-o "%(Identity)"')</options> + <!-- + // $(full_path) ::= full path of filtered file --> + <full_path Condition="'%(QtRccFilter.QtQmlCacheItems)' != ''" +>$([System.IO.Path]::Combine($(MSBuildProjectDirectory),%(QmlCacheFilteredFile)))</full_path> + <!-- + // $(relative_path) ::= path of filtered file, relative to project directory --> + <relative_path Condition="'%(QtRccFilter.QtQmlCacheItems)' != ''" +>$([MSBuild]::MakeRelative($(MSBuildProjectDirectory),$(full_path)).TrimStart('\'))</relative_path> + <!-- + // $(run_parallel) ::= run qmlcachegen in a parallel process + // $(run_single) ::= run qmlcachegen in single process mode --> + <run_parallel Condition="'@(QtRccFilter)' != '' + AND '%(QtRccFilter.ParallelProcess)' == 'true' AND '$(SelectedFiles)' == ''" + >true</run_parallel> + <run_single Condition="'@(QtRccFilter)' != '' + AND ('%(QtRccFilter.ParallelProcess)' != 'true' OR '$(SelectedFiles)' != '')" + >true</run_single> + </PropertyGroup> + + <ItemGroup Condition="'@(QtRccFilter)' != ''"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Create work item for QRC filtering + // --> + <QtWork + Include="@(QtRccFilter)" + Condition="'%(QtRccFilter.QtQmlCacheItems)' != '' + AND '%(QtRccFilter.OtherResources)' != '' + AND '%(QtRccFilter.IsSelected)' == 'true'"> + <WorkType>qmlcachegen_filter</WorkType> + <ToolPath Condition="'$(QtVsProjectSettings)' == 'true'" + >$(QtToolsPath)\qmlcachegen.exe</ToolPath> + <ToolPath Condition="'$(QtVsProjectSettings)' != 'true'" + >%(QtRccFilter.QTDIR)\bin\qmlcachegen.exe</ToolPath> + <Options>$(options)</Options> + <OutputFile>$(full_path)</OutputFile> + <Message>%(QtRccFilter.QmlCacheFilterMessage)</Message> + <ParallelBuild Condition="'$(run_parallel)' == 'true'">true</ParallelBuild> + <ParallelBuild Condition="'$(run_single)' == 'true'">false</ParallelBuild> + </QtWork> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Create build item for generated QRC file + // --> + <QtRcc Condition="'%(QtRccFilter.QtQmlCacheItems)' != '' + AND '%(QtRccFilter.OtherResources)' != ''" + Include="@(QtRccFilter->'$(relative_path)')"> + <InputFile>@(QtRccFilter->'%(QmlCacheFilteredFile)')</InputFile> + <OutputFile>@(QtRccFilter->'%(QmlCacheFilteredOutputFile)')</OutputFile> + <InitFuncName>@(QtRccFilter->'%(QmlCacheFilteredInitFuncName)')</InitFuncName> + <DependsOn>@(QtRccFilter)</DependsOn> + </QtRcc> + </ItemGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // If sources were manually selected (e.g. by the 'Compile' option in the context menu for + // project items), add generated QRC to the list of selected files + // --> + <PropertyGroup> + <selected_rcc + Condition="'$(SelectedFiles)' != '' + AND '%(QtRccFilter.IsSelected)' == 'true' + AND '%(QtRccFilter.QtQmlCacheItems)' != '' + AND '%(QtRccFilter.OtherResources)' != ''" + >$(relative_path)</selected_rcc> + </PropertyGroup> + <PropertyGroup Condition="'$(selected_rcc)' != ''"> + <SelectedFiles>$(SelectedFiles);$(selected_rcc)</SelectedFiles> + </PropertyGroup> + <ItemGroup Condition="'$(selected_rcc)' != ''"> + <selected_files Include="$(selected_rcc)"/> + <SelectedFiles Include="$(selected_rcc)"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Update timestamp of generated QRC file to force rcc target to run + // --> + <MakeDir Directories="$([System.IO.Path]::GetDirectoryName(%(QtRccFilter.OutputFile)))"/> + <Touch Condition="'%(QtRccFilter.InputChanged)' == 'true'" + AlwaysCreate="true" Files="%(QtRccFilter.OutputFile)"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Create loader input item, used in cache loader generation + // --> + <ItemGroup Condition="'@(QtRccFilter)' != ''"> + <loader_input Include="@(QtRccFilter->'QtQmlCacheLoader')"> + <Inputs>%(QtRccFilter.Identity)</Inputs> + <InputFile>%(QtRccFilter.InputFile)</InputFile> + <Mapping Condition="'%(QtRccFilter.QtQmlCacheItems)' != '' AND '%(QtRccFilter.OtherResources)' != ''" + >%(QtRccFilter.InputFile)=$(full_path)</Mapping> + </loader_input> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <ItemGroup> + <input_file Remove="@(input_file)"/> + <filtered_file Remove="@(filtered_file)"/> + </ItemGroup> + <PropertyGroup> + <dependencies_changed/> + <options/> + <full_path/> + <relative_path/> + <run_parallel/> + <run_single/> + <selected_rcc/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // TARGET QtQmlCacheLoader + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Generate QML cache loader + // --> + <Target Name="QtQmlCacheLoader" DependsOnTargets="QtRccFilter"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtQmlCacheLoader @(QtQmlCacheLoader->'%(Inputs)')" /> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Get QML cache loader properties from project rcc property page + // --> + <ItemGroup> + <QtRcc Include="qmlcachengen_loader"/> + <loader Include="@(QtRcc)" Condition="'%(QtRcc.Identity)' == 'qmlcachengen_loader'"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate QML cache loader + // --> + <PropertyGroup> + <!-- + // $(options) ::= qmlcachegen command line options --> + <options Condition="'@(loader_input)' != ''" + >$(options) @(loader_input->'--resource-file-mapping="%(Mapping)"',' ')</options> + <options Condition="'@(loader_input)' != ''" + >$(options) @(loader_input->'"%(InputFile)"',' ')</options> + <options Condition="'@(loader)' != ''" + >$(options) @(loader->'-o "%(QmlCacheLoaderFile)"')</options> + <!-- + // $(run_parallel) ::= run qmlcachegen in a parallel process + // $(run_single) ::= run qmlcachegen in single process mode --> + <run_parallel Condition="'@(QtQmlCacheLoader)' != '' + AND '%(loader.ParallelProcess)' == 'true' AND '$(SelectedFiles)' == ''">true</run_parallel> + <run_single Condition="'@(QtQmlCacheLoader)' != '' + AND ('%(loader.ParallelProcess)' != 'true' OR '$(SelectedFiles)' != '')">true</run_single> + <!-- + // $(dependencies_changed) ::= source QRC files changed status + // $(input_changed) ::= source QRC files input changed status --> + <dependencies_changed>@(loader_input->'%(DependenciesChanged)')</dependencies_changed> + <input_changed>@(loader_input->'%(InputChanged)')</input_changed> + <input_changed Condition="!Exists('@(loader->'%(QmlCacheLoaderFile)')')">true</input_changed> + <is_selected>@(loader_input->'%(IsSelected)')</is_selected> + + <!--// Get relative path to output --> + <cache_loader_path>@(loader->'%(QmlCacheLoaderFile)')</cache_loader_path> + <output_relative +>$([MSBuild]::MakeRelative($(ProjectDir), $(cache_loader_path)).TrimStart('\'))</output_relative> + </PropertyGroup> + <ItemGroup> + <!-- + // Create work item to generate QML cache loader --> + <QtWork + Include="qmlcachengen_loader" + Condition="'@(loader_input)' != '' + AND $(is_selected.Contains('true'))"> + <WorkType>qmlcachengen_loader</WorkType> + <ToolPath Condition="'$(QtVsProjectSettings)' == 'true'" + >$(QtToolsPath)\qmlcachegen.exe</ToolPath> + <ToolPath Condition="'$(QtVsProjectSettings)' != 'true'" + >@(loader->'%(QTDIR)')\bin\qmlcachegen.exe</ToolPath> + <Options>$(options)</Options> + <OutputFile>@(loader->'%(QmlCacheLoaderFile)')</OutputFile> + <Message>@(loader->'%(QmlCacheLoaderMessage)')</Message> + <ParallelBuild Condition="'$(run_parallel)' == 'true'">true</ParallelBuild> + <ParallelBuild Condition="'$(run_single)' == 'true'">false</ParallelBuild> + <DisableLog>true</DisableLog> + <DependenciesChanged>$(dependencies_changed.Contains('true'))</DependenciesChanged> + <InputChanged>$(input_changed.Contains('true'))</InputChanged> + <ClCompile Condition="'%(loader.DynamicSource)' != 'false'">$(output_relative)</ClCompile> + <DependsOn>@(loader_input->'%(Inputs)')</DependsOn> + </QtWork> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <ItemGroup> + <loader_mapping_option Remove="@(mapping_option)"/> + <loader_input_option Remove="@(loader_input_option)"/> + <loader Remove="@(loader)"/> + <QtRcc Remove="qmlcachengen_loader"/> + </ItemGroup> + <PropertyGroup> + <options/> + <run_parallel/> + <run_single/> + <dependencies_changed/> + <input_changed/> + <cache_loader_path/> + <output_relative/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtQmlSetModified + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Set InputModified flag for modified files (i.e. Inputs newer than Outputs) + // --> + <Target Name="QtQmlSetModified" DependsOnTargets="QtQmlCachePrepare" + Inputs="%(QtQmlCacheItem.FullPath);%(QtQmlCacheItem.AdditionalDependencies)" + Outputs="@(QtQmlCacheItem->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtQmlSetModified %(QtQmlCacheItem.Identity) => @(QtQmlCacheItem->'%(OutputFile)')"/> + + <PropertyGroup> + <input_changed/> + </PropertyGroup> + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="input_changed" /> + </CreateProperty> + <ItemGroup> + <QtQmlCacheItem> + <InputChanged>$(input_changed)</InputChanged> + </QtQmlCacheItem> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <input_changed/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // TARGET QtQmlCacheItem + ///////////////////////////////////////////////////////////////////////////////////////////////// + // --> + <Target Name="QtQmlCacheItem" DependsOnTargets="QtQmlSetModified" + Inputs="%(QtQmlCacheItem.FullPath);%(QtQmlCacheItem.AdditionalDependencies);$(MSBuildProjectFile)" + Outputs="@(QtQmlCacheItem->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtQmlCacheItem %(QtQmlCacheItem.Identity) => @(QtQmlCacheItem->'%(OutputFile)')" /> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Set DependenciesChanged flag for modified files or dependencies + // --> + <PropertyGroup> + <dependencies_changed/> + </PropertyGroup> + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="dependencies_changed" /> + </CreateProperty> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate QML cache + // --> + <PropertyGroup> + <!-- + // $(options) ::= qmlcachegen command line options --> + <options Condition="'@(QtQmlCacheItem)' != ''" + >$(options) --resource="%(ResourceFile)"</options> + <options Condition="'@(QtQmlCacheItem)' != ''" + >$(options) "%(FullPath)"</options> + <options Condition="'@(QtQmlCacheItem)' != ''" + >$(options) -o "%(OutputFile)"</options> + <!-- + // $(run_parallel) ::= run qmlcachegen in a parallel process + // $(run_single) ::= run qmlcachegen in single process mode --> + <run_parallel Condition="'@(QtQmlCacheItem)' != '' + AND '%(QtQmlCacheItem.ParallelProcess)' == 'true' AND '$(SelectedFiles)' == ''" + >true</run_parallel> + <run_single Condition="'@(QtQmlCacheItem)' != '' + AND ('%(QtQmlCacheItem.ParallelProcess)' != 'true' OR '$(SelectedFiles)' != '')" + >true</run_single> + + <!--// Get relative path to output --> + <output_relative +>$([MSBuild]::MakeRelative($(ProjectDir), %(QtQmlCacheItem.OutputFile)).TrimStart('\'))</output_relative> + </PropertyGroup> + <ItemGroup> + <!-- + // Create work item to generate QML cache --> + <QtWork + Include="@(QtQmlCacheItem)" + Condition="'@(QtQmlCacheItem)' != '' + AND '%(QtQmlCacheItem.IsSelected)' == 'true'"> + <WorkType>qmlcachegen</WorkType> + <ToolPath Condition="'$(QtVsProjectSettings)' == 'true'" + >$(QtToolsPath)\qmlcachegen.exe</ToolPath> + <ToolPath Condition="'$(QtVsProjectSettings)' != 'true'" + >%(QtQmlCacheItem.QTDIR)\bin\qmlcachegen.exe</ToolPath> + <Options>$(options)</Options> + <Message>%(QtQmlCacheItem.Message)</Message> + <DependenciesChanged>$(dependencies_changed)</DependenciesChanged> + <ParallelBuild Condition="'$(run_parallel)' == 'true'">true</ParallelBuild> + <ParallelBuild Condition="'$(run_single)' == 'true'">false</ParallelBuild> + <ClCompile Condition="'%(QtQmlCacheItem.DynamicSource)' != 'false'">$(output_relative)</ClCompile> + </QtWork> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <dependencies_changed/> + <options/> + <run_parallel/> + <run_single/> + <output_relative/> + </PropertyGroup> + <ItemGroup> + <selected_files Remove="@(selected_files)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // TARGET QtQmlCache + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Root target of QML cache generation + // --> + <Target Name="QtQmlCache" + DependsOnTargets="QtQmlCacheLoader;QtQmlCacheItem" + BeforeTargets="QtRcc"> + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="## QtQmlCache" /> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterQmlCache)' != ''" + Project="$(QtMsBuildTargets_AfterQmlCache)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qml/qtqml_static.props b/QtMSBuild/QtMsBuild/qml/qtqml_static.props new file mode 100644 index 0000000..459fa50 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qml/qtqml_static.props @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild property definitions for QML static imports +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeQmlStatic)' != ''" + Project="$(QtMsBuildProps_BeforeQmlStatic)"/> + + <PropertyGroup> + <QtQmlPluginImportCpp>$(QtQmlIntDir)\qml_plugin_import.cpp</QtQmlPluginImportCpp> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterQmlStatic)' != ''" + Project="$(QtMsBuildProps_AfterQmlStatic)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qml/qtqml_static.targets b/QtMSBuild/QtMsBuild/qml/qtqml_static.targets new file mode 100644 index 0000000..13ccc62 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qml/qtqml_static.targets @@ -0,0 +1,342 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Generate imports of QML static plugins +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeQmlStatic)' != ''" + Project="$(QtMsBuildTargets_BeforeQmlStatic)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // * add QtQmlStatic to the list of targets to run during build + // * add QtQmlStaticLink to the list of targets to run when calculating linker input + // --> + <PropertyGroup> + <QtBuildTargets>$(QtBuildTargets);QtQmlStatic</QtBuildTargets> + <ComputeLinkInputsTargets>$(ComputeLinkInputsTargets);QtQmlStaticLink</ComputeLinkInputsTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtQmlStaticLink + /// => runs only once + /// => always runs if building against a static build of Qt + /// => targets that must run first: QtQmlStatic, ComputeCLGeneratedLinkInputs + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Add QML static plugin libs to Link input. + // --> + <Target + Name="QtQmlStaticLink" + Condition="'$(QtStaticPlugins)' == 'true' AND '@(QtQml)' != ''" + DependsOnTargets="QtQmlStatic;ComputeCLGeneratedLinkInputs"> + <ItemGroup> + <Link> + <AdditionalDependencies Condition="'@(QtQmlImportLib)' != ''" + >%(Link.AdditionalDependencies);@(QtQmlImportLib)</AdditionalDependencies> + </Link> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtQmlStatic + /// => runs only once + /// => always runs if building against a static build of Qt + /// => targets that must run first: QtQmlImportsParseJson, QtQmlGetImportCpp + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Entry point for generating imports of QML static plugins. + // --> + <Target + Name="QtQmlStatic" + Condition="'$(QtStaticPlugins)' == 'true' AND '@(QtQml)' != ''" + DependsOnTargets="QtQmlImportsParseJson;QtQmlGetImportCpp"> + <ItemGroup> + <!-- for each required import, create a reference to the corresponding import lib --> + <QtQmlImportLib + Condition="'$(QtBuildConfig)' == 'debug'" + Include="@(QtQmlImport->'%(Path)\%(Plugin)d.lib')"/> + <QtQmlImportLib + Condition="'$(QtBuildConfig)' != 'debug'" + Include="@(QtQmlImport->'%(Path)\%(Plugin).lib')"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtQmlGetImportCpp + /// => runs only once + /// => skipped if generated C++ is up-to-date, relative to all JSON output files + /// => targets that must run first: QtQmlImportsParseJson + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Generate C++ source file with imports of QML static plugins. + // --> + <Target + Name="QtQmlGetImportCpp" + Condition="'$(QtStaticPlugins)' == 'true' AND '@(QtQml)' != ''" + DependsOnTargets="QtQmlImportsParseJson" + Inputs="@(QtQml->'%(ImportJson)')" + Outputs="$(QtQmlPluginImportCpp)"> + + <!-- file header --> + <PropertyGroup> + <QmlPluginImportCppHeader> + <![CDATA[ +// This file is autogenerated by Qt/MSBuild. It imports static plugin classes for +// static plugins used by QML imports. +#include <QtPlugin>]]> + </QmlPluginImportCppHeader> + </PropertyGroup> + + <!-- file contents: header + imports --> + <ItemGroup> + <QmlPluginImportCpp Include="$(QmlPluginImportCppHeader)" /> + <QmlPluginImportCpp Include="@(QtQmlImport->'Q_IMPORT_PLUGIN(%(ClassName))')" /> + </ItemGroup> + + <!-- write .cpp file --> + <WriteLinesToFile + File="$(QtQmlPluginImportCpp)" + Lines="@(QmlPluginImportCpp)" /> + + <!-- clean-up --> + <PropertyGroup> + <QmlPluginImportCppHeader/> + </PropertyGroup> + <ItemGroup> + <QtQmlPluginImportCpp Remove="@(QtQmlPluginImportCpp)" /> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtQmlImportsParseJson + /// => runs only once + /// => always runs if building agains a static build of Qt + /// => targets that must run first: QtQmlScanImports + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Parse JSON output of qmlimportscanner. + // * ATTN: "Naïve" parser: assumes JSON is formatted with each value in a single line + // --> + <Target + Name="QtQmlImportsParseJson" + Condition="'$(QtStaticPlugins)' == 'true' AND '@(QtQml)' != ''" + DependsOnTargets="QtQmlScanImports"> + + <!-- constants --> + <PropertyGroup> + <StrClassName>"classname": </StrClassName> + <StrPath>"path": </StrPath> + <StrPlugin>"plugin": </StrPlugin> + </PropertyGroup> + + <!-- load text of all JSON output files --> + <ReadLinesFromFile File="%(QtQml.ImportJson)"> + <Output TaskParameter="Lines" ItemName="JsonText"/> + </ReadLinesFromFile> + + <ItemGroup> + <!-- list of all lines starting with '"classname": ' --> + <JsonClassName + Include="@(JsonText)" + Condition="$([System.String]::Copy('%(JsonText.Identity)').StartsWith('$(StrClassName)'))" /> + <!-- extract value --> + <JsonClassName> + <ClassName>$([System.String]::Copy('%(JsonClassName.Identity)').Substring($(StrClassName.Length)).Trim(',').Trim('"'))</ClassName> + </JsonClassName> + + <!-- list of all lines starting with '"path": ' --> + <JsonPath + Include="@(JsonText)" + Condition="$([System.String]::Copy('%(JsonText.Identity)').StartsWith('$(StrPath)'))" /> + <!-- extract value --> + <JsonPath> + <Path>$([System.String]::Copy('%(JsonPath.Identity)').Substring($(StrPath.Length)).Trim(',').Trim('"'))</Path> + </JsonPath> + + <!-- list of all lines starting with '"plugin": ' --> + <JsonPlugin + Include="@(JsonText)" + Condition="$([System.String]::Copy('%(JsonText.Identity)').StartsWith('$(StrPlugin)'))" /> + <!-- extract value --> + <JsonPlugin> + <Plugin>$([System.String]::Copy('%(JsonPlugin.Identity)').Substring($(StrPlugin.Length)).Trim(',').Trim('"'))</Plugin> + </JsonPlugin> + </ItemGroup> + + <!-- join lists of extracted values into single list of imports --> + <Join LeftItems="@(JsonClassName)" RightItems="@(JsonPath)" On="ROW_NUMBER"> + <Output TaskParameter="Result" ItemName="JsonAux"/> + </Join> + <Join LeftItems="@(JsonAux)" RightItems="@(JsonPlugin)" On="ROW_NUMBER"> + <Output TaskParameter="Result" ItemName="Json"/> + </Join> + <!-- remove duplicate import items --> + <RemoveDuplicates Inputs="@(Json->'%(ClassName)')"> + <Output TaskParameter="Filtered" ItemName="QtQmlImport"/> + </RemoveDuplicates> + + <!-- @(QtQmlImport) => list of required imports --> + + <!-- clean-up --> + <PropertyGroup> + <StrClassName/> + <StrPath/> + <StrPlugin/> + </PropertyGroup> + <ItemGroup> + <JsonText Remove="@(JsonText)" /> + <JsonClassName Remove="@(JsonClassName)"/> + <JsonPath Remove="@(JsonPath)"/> + <JsonPlugin Remove="@(JsonPlugin)"/> + <JsonAux Remove="@(JsonAux)" /> + <Json Remove="@(Json)" /> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtQmlScanImports + /// => runs once for each QML file + /// => skipped if corresponding JSON output file is up-to-date + /// => targets that must run first: QtQmlStaticPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Run qmlimportscanner tool. + // --> + <Target + Name="QtQmlScanImports" + Condition="'$(QtStaticPlugins)' == 'true' AND '@(QtQml)' != ''" + DependsOnTargets="QtQmlStaticPrepare" + Inputs="%(QtQml.Identity)" + Outputs="%(QtQml.ImportJson)"> + + <!-- create QML work dir --> + <MakeDir Directories="$(QtQmlIntDir)" /> + + <!-- map local paths to build host paths --> + <ItemGroup> + <LocalPath Include="QtQmlFile"> + <Name>Path</Name> + <Item>%(QtQml.Identity)</Item> + <Value>%(QtQml.Identity)</Value> + </LocalPath> + <LocalPath Include="QtQmlImportJson"> + <Name>Path</Name> + <Item>%(QtQml.ImportJson)</Item> + <Value>%(QtQml.ImportJson)</Value> + </LocalPath> + <LocalPath Include="QtInstallQml"> + <Name>Path</Name> + <Item>$(QMake_QT_INSTALL_QML_)</Item> + <Value>$(QMake_QT_INSTALL_QML_)</Value> + </LocalPath> + </ItemGroup> + <HostTranslatePaths Items="@(LocalPath)" Names="Path"> + <Output TaskParameter="Result" ItemName="HostPath"/> + </HostTranslatePaths> + <ItemGroup> + <QtQmlFile Include="@(HostPath->WithMetadataValue('Identity', 'QtQmlFile'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </QtQmlFile> + <QtQmlImportJson Include="@(HostPath->WithMetadataValue('Identity', 'QtQmlImportJson'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </QtQmlImportJson> + <QtInstallQml Include="@(HostPath->WithMetadataValue('Identity', 'QtInstallQml'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </QtInstallQml> + </ItemGroup> + + <!-- qmlimportscanner command line --> + <PropertyGroup> + <Cmd><![CDATA["$(QtToolsPath)/qmlimportscanner.exe"]]></Cmd> + <Cmd><![CDATA[$(Cmd) -qmlFiles @(QtQmlFile->'%(HostPath)')]]></Cmd> + <Cmd><![CDATA[$(Cmd) -importPath @(QtInstallQml->'%(HostPath)')]]></Cmd> + </PropertyGroup> + + <!-- run qmlimportscanner --> + <HostExec + Command="$(Cmd)" RedirectStdOut="@(QtQmlImportJson->'%(HostPath)')" + Inputs="@(QtQmlFile->'%(HostPath)')" + Outputs="@(QtQmlImportJson->'%(HostPath)')" + RemoteTarget="$(ResolvedRemoteTarget)" + RemoteProjectDir="$(_ResolvedRemoteProjectDir)" /> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtQmlStaticPrepare + /// => runs once for each QML file + /// => skipped if $(QtStaticPlugins) is false + /// => skipped if there are no QML files in the project + /// => targets that must run first: QtQmlCreateItems + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate path of .json file with the output of qmlimportscanner for each QML file. + // * Format: <QML work dir> '\' SHA1(<path to QML file>) '_' <QML file name> '.json' + // --> + <Target + Name="QtQmlStaticPrepare" + Condition="'$(QtStaticPlugins)' == 'true' AND '@(QtQml)' != ''" + DependsOnTargets="QtQmlCreateItems" + Inputs="%(QtQml.Identity)" + Outputs="@(QtQml->'####### Do not skip this target #######')"> + <!-- calculate SHA1 of QML file path, relative to project dir --> + <Hash ItemsToHash="@(QtQml)"> + <Output TaskParameter="HashResult" PropertyName="QtQmlIdentityHash"/> + </Hash> + <!-- set path to corresponding JSON file --> + <ItemGroup> + <QtQml> + <ImportJson>$([MSBuild]::MakeRelative( + '$(ProjectDir)','$(QtQmlIntDir)\$(QtQmlIdentityHash)_%(Filename).json'))</ImportJson> + </QtQml> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterQmlStatic)' != ''" + Project="$(QtMsBuildTargets_AfterQmlStatic)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qt.targets b/QtMSBuild/QtMsBuild/qt.targets new file mode 100644 index 0000000..ec7d93d --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt.targets @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Fail-safe import of private definitions + // --> + <Import + Condition="'$(QtPrivateLoaded)' != 'true'" + Project="$(MSBuildThisFileDirectory)\qt_private.props"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Defaults for project version constants + // --> + <PropertyGroup> + <QtVsProjectVersion + Condition="'$(QtVsProjectVersion)' == ''" + >0</QtVsProjectVersion> + <QtVsProjectMinVersion_Settings + Condition="'$(QtVsProjectMinVersion_Settings)' == ''" + >0</QtVsProjectMinVersion_Settings> + <QtVsProjectMinVersion_ClProperties + Condition="'$(QtVsProjectMinVersion_ClProperties)' == ''" + >0</QtVsProjectMinVersion_ClProperties> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt Common Targets + // --> + <Import Project="qt_globals.targets"/> + <Import Project="qt_settings.targets"/> + <Import Project="qt_tasks.targets"/> + <Import Project="qt_vars.targets"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import subfolder targets + // --> + <Import + Project="$(QtMsBuildTargets)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qt_defaults.props b/QtMSBuild/QtMsBuild/qt_defaults.props new file mode 100644 index 0000000..d351b61 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt_defaults.props @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Default values of Qt settings +// +// Preceding evaluation chain: +// * Project global properties, incl. $(Keyword), $(WindowsTargetPlatformVersion), $(QtMsBuild) +// * Microsoft.Cpp.Default.props +// * Configuration properties, incl. $(ConfigurationType), $(PlatformToolset) +// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <QtDefaultsLoaded>true</QtDefaultsLoaded> + </PropertyGroup> + + <!-- // Qt VS Project Format Version --> + <PropertyGroup> + <QtVsProjectVersion>0</QtVsProjectVersion> + <QtVsProjectMinVersion_Settings>300</QtVsProjectMinVersion_Settings> + <QtVsProjectMinVersion_ClProperties>300</QtVsProjectMinVersion_ClProperties> + </PropertyGroup> + <PropertyGroup Condition="'$(Keyword)' != ''"> + <QtVsProjectVersion Condition="'$(Keyword)' == 'Qt4VSv1.0'">200</QtVsProjectVersion> + <QtVsProjectVersion Condition="$(Keyword.StartsWith('QtVS_v'))" + >$([System.Convert]::ToInt32($(Keyword.Substring(6))))</QtVsProjectVersion> + <QtVsProjectSettings Condition="$(QtVsProjectVersion) >= $(QtVsProjectMinVersion_Settings)" + >true</QtVsProjectSettings> + <QtVsProjectSettings Condition="$(QtVsProjectVersion) < $(QtVsProjectMinVersion_Settings)" + >false</QtVsProjectSettings> + <QtVsProjectClProperties + Condition="$(QtVsProjectVersion) >= $(QtVsProjectMinVersion_ClProperties)" + >true</QtVsProjectClProperties> + <QtVsProjectClProperties + Condition="$(QtVsProjectVersion) < $(QtVsProjectMinVersion_ClProperties)" + >false</QtVsProjectClProperties> + </PropertyGroup> + + <PropertyGroup> + <!--// Path of Qt binary files --> + <QtPathBinaries>bin</QtPathBinaries> + <QtPathLibraryExecutables>bin</QtPathLibraryExecutables> + + <!--// qmake template --> + <QtQMakeTemplate>vcapp</QtQMakeTemplate> + + <!--// Extract Qt variables from qmake-generated Makefile + // Syntax: < var_name > [ = [ makefile_name ] / < pattern > / < replace > / ] --> + <QMake_Makefile> + DEFINES=/-D([^\s=]+(=(\x22(\\\\|\\\x22|[^\x22])*\x22|\S+))?)/$1/; + INCLUDEPATH=INCPATH/-(?:iquote|isystem|idirafter|I)\s*(\x22[^\x22]+\x22|[^\s]+)/$1/; + LIBS=/(?:(?:\/LIBPATH:|-L)(?:\x22[^\x22]+\x22|[^\s]+))|(\x22[^\x22]+\x22|[^\s]+)/$1/; + LIBPATH=LIBS/(?:\/LIBPATH:|-L)(\x22[^\x22]+\x22|[^\s]+)/$1/; + </QMake_Makefile> + + <!--// Extract Qt variables from qmake-generated .vcxproj file + // Syntax: < var_name > = < xpath_to_value > --> + <QMake_MSBuild> + DEFINES=/Project/ItemDefinitionGroup/ClCompile/PreprocessorDefinitions; + INCLUDEPATH=/Project/ItemDefinitionGroup/ClCompile/AdditionalIncludeDirectories; + STDCPP=/Project/ItemDefinitionGroup/ClCompile/LanguageStandard; + CL_OPTIONS=/Project/ItemDefinitionGroup/ClCompile/AdditionalOptions; + LIBS=/Project/ItemDefinitionGroup/Link/AdditionalDependencies; + LINK_OPTIONS=/Project/ItemDefinitionGroup/Link/AdditionalOptions; + </QMake_MSBuild> + + <!--// Default Qt version --> + <QtVersionsRegKey Condition="'$(QtVersionsRegKey)' == ''" + >HKEY_CURRENT_USER\Software\Digia\Versions</QtVersionsRegKey> + <DefaultQtVersion Condition="'$(DefaultQtVersion)' == ''" + >$([MSBuild]::GetRegistryValue('$(QtVersionsRegKey)','DefaultQtVersion'))</DefaultQtVersion> + + <!--// Qt build config --> + <QtBuildConfig Condition="'$(Configuration)' == 'Debug'">debug</QtBuildConfig> + <QtBuildConfig Condition="'$(Configuration)' != 'Debug'">release</QtBuildConfig> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Read subfolder dependencies (qt_import.props) + // --> + <PropertyGroup> + <SubFolder>$([System.IO.Directory]::GetDirectories('$(MSBuildThisFileDirectory)'))</SubFolder> + <SubFolderImports Condition="'$(SubFolder)' != ''" + > + $([System.String]::Join( + '\qt_import.props;', + $(SubFolder.Split(';'))))\qt_import.props + </SubFolderImports> + <QtImports>$(SubFolderImports.Split(';'))</QtImports> + <SubFolder/> + <SubFolderImports/> + </PropertyGroup> + <Import + Project="$(QtImports)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qt_globals.targets b/QtMSBuild/QtMsBuild/qt_globals.targets new file mode 100644 index 0000000..a8cb31c --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt_globals.targets @@ -0,0 +1,636 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Qt/MSBuild global definitions +/////////////////////////////////////////////////////////////////////////////////////////////////// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Build dependencies + // --> + <PropertyGroup> + <BuildDependsOn> + QtVersion; + $(BuildDependsOn); + Qt + </BuildDependsOn> + <CleanDependsOn> + $(CleanDependsOn); + QtClean + </CleanDependsOn> + <DesignTimeBuildInitTargets> + $(DesignTimeBuildInitTargets); + Qt + </DesignTimeBuildInitTargets> + <ComputeCompileInputsTargets> + $(ComputeCompileInputsTargets); + Qt + </ComputeCompileInputsTargets> + <BeforeClCompileTargets> + $(BeforeClCompileTargets); + Qt + </BeforeClCompileTargets> + <ComputeLinkInputsTargets> + $(ComputeLinkInputsTargets); + Qt + </ComputeLinkInputsTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // --> + <Import Project="..\version.targets" Condition="Exists('..\version.targets')"/> + <PropertyGroup> + <QtMsBuildVersion>$(QtVSToolsVersion)</QtMsBuildVersion> + <QtDebug Condition="'$(QtDebug)' == ''">false</QtDebug> + <QtLogFilePath Condition="'$(QtLogFilePath)' == ''" + >$([System.IO.Path]::Combine($(ProjectDir),$(IntDir)qt_work.log))</QtLogFilePath> + <QtMaxProcs Condition="'$(QtMaxProcs)' == ''" + >$([System.Environment]::ProcessorCount)</QtMaxProcs> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtGetDefaultClCompile + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Get default C++ properties + // --> + <Target Name="QtGetDefaultClCompile"> + <ItemGroup> + <ClCompile Include="DefaultClCompile"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtClean + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up from previous build + // --> + <Target Name="QtClean"> + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="## Qt Clean"/> + <Delete Files="$(QtLogFilePath)"/> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtVersion + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Print debug message with Qt/MSBuild version + // --> + <Target Name="QtVersion"> + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="Qt/MSBuild v$(QtMsBuildVersion) ($(MSBuildThisFileDirectory))"/> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Prepare Qt build: read and parse work log file + // --> + <Target Name="QtPrepare" + Condition="'$(QtSkipWork)' != 'true'" + DependsOnTargets="$(QtDependsOn)" + BeforeTargets="QtWorkPrepare"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="#### QtPrepare"/> + <CriticalSection Lock="true" Name="$(ProjectGuid)" /> + + <ReadLinesFromFile File="$(QtLogFilePath)"> + <Output TaskParameter="Lines" ItemName="QtLogData"/> + </ReadLinesFromFile> + <ItemGroup Condition="'@(QtLogData)' != ''"> + <QtWorkLog + Include="@(QtLogData->'$([System.String]::Copy('%(QtLogData.Identity)').Split('|')[0])')"> + <Hash>$([System.String]::Copy('%(QtLogData.Identity)').Split('|')[1])</Hash> + </QtWorkLog> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtWorkPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Analyze work request and decide if the Qt tool needs to be called or if the output from the + // previous call is still valid. + // --> + <Target Name="QtWorkPrepare" DependsOnTargets="$(QtDependsOn);$(QtBuildTargets)" + Condition="'$(QtSkipWork)' != 'true'" + Inputs="%(QtWork.WorkType)(%(QtWork.Identity))" + Outputs="@(QtWork->'####### Don't skip this target #######')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true' AND '@(QtWork)' != ''" + Text="## QtWorkPrepare %(QtWork.Identity)" /> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate hash for the requested work item, based on its associated tool and options + // --> + <GetItemHash Condition="'@(QtWork)' != ''" + Item="@(QtWork)" Keys="Identity;WorkType;ToolPath;Options"> + <Output TaskParameter="Hash" PropertyName="work_hash" /> + </GetItemHash> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Try to find entry in Qt work log for the requested work item; get logged hash + // --> + <PropertyGroup Condition="'@(QtWork)' != ''"> + <work_key>@(QtWork->'%(WorkType)(%(Identity))')</work_key> + <dependencies_changed>@(QtWork->'%(DependenciesChanged)')</dependencies_changed> + <input_changed>@(QtWork->'%(InputChanged)')</input_changed> + <project_changed + Condition="'$(dependencies_changed)' == 'true' AND '$(input_changed)' != 'true'" + >true</project_changed> + </PropertyGroup> + + <FindInList Condition="'@(QtWork)' != '' AND '$(input_changed)' != 'true'" + CaseSensitive="false" List="@(QtWorkLog)" ItemSpecToFind="$(work_key)"> + <Output TaskParameter="ItemFound" ItemName="log_entry"/> + </FindInList> + + <PropertyGroup Condition="'@(QtWork)' != ''"> + <log_hash Condition="'@(log_entry)' != ''">@(log_entry->'%(Hash)')</log_hash> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip work item if: + // * work is not needed: + // - input was not modified + // - AND project was not modified OR command line did not change (i.e. hashes are the same) + // * OR we're in a design-time build + // --> + <PropertyGroup> + <do_work + Condition="'$(input_changed)' == 'true' + OR ('$(project_changed)' == 'true' AND '$(log_hash)' != '$(work_hash)')" + >true</do_work> + <skip_work + Condition="'$(do_work)' != 'true' OR '$(DesignTimeBuild)' == 'true'" + >true</skip_work> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Skip work item + // --> + <ItemGroup Condition="'@(QtWork)' != '' AND '$(skip_work)' == 'true'"> + <QtWorkResult Include="@(QtWork)"> + <ExitCode>0</ExitCode> + <Skipped>true</Skipped> + </QtWorkResult> + <QtWork Remove="@(QtWork)" /> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate new work log entry and ensure path to output exists + // --> + <ItemGroup Condition="'@(QtWork)' != '' AND '$(skip_work)' != 'true'"> + <QtWorkLog Remove="$(work_key)"/> + <QtWorkLog Include="$(work_key)"> + <Hash>$(work_hash)</Hash> + </QtWorkLog> + </ItemGroup> + + <MakeDir Condition="'@(QtWork)' != '' AND '$(skip_work)' != 'true'" + Directories="$([System.IO.Path]::GetDirectoryName(%(QtWork.OutputFile)))"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <work_key/> + <log_hash/> + <dependencies_changed/> + <input_changed/> + <project_changed/> + <do_work/> + <skip_work/> + </PropertyGroup> + <ItemGroup> + <log_entry Remove="@(log_entry)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtWork + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Run Qt tools and add dynamic C++ sources to build + // --> + <Target Name="QtWork" + Condition="'$(QtSkipWork)' != 'true'" + DependsOnTargets="QtWorkPrepare;QtGetDefaultClCompile"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## Qt Build $(QtBuildTargets.Replace(';',' ').Trim())" /> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Run work locally in parallel processes + // --> + <QtRunWork + Condition="'$(ApplicationType)' != 'Linux' AND '@(QtWork)' != '' + AND '%(QtWork.ParallelBuild)' == 'true' + AND '$(DesignTimeBuild)' != 'true'" + QtWork="@(QtWork)" QtMaxProcs="$(QtMaxProcs)" QtDebug="$(QtDebug)"> + <Output TaskParameter="Result" ItemName="QtWorkResult" /> + </QtRunWork> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Run work locally in a single process + // --> + <QtRunWork + Condition="'$(ApplicationType)' != 'Linux' AND '@(QtWork)' != '' + AND '%(QtWork.ParallelBuild)' != 'true' + AND '$(DesignTimeBuild)' != 'true'" + QtWork="@(QtWork)" QtMaxProcs="1" QtDebug="$(QtDebug)"> + <Output TaskParameter="Result" ItemName="QtWorkResult" /> + </QtRunWork> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Run work in build host + // --> + <!-- // Translate local paths to host paths --> + <Flatten + Condition="'$(ApplicationType)' == 'Linux' + AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'" + Items="@(QtWork)" Metadata="ResourceFiles"> + <Output TaskParameter="Result" ItemName="ResourceFiles"/> + </Flatten> + <ItemGroup + Condition="'$(ApplicationType)' == 'Linux' + AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'"> + <LocalPath Include="%(QtWork.Identity)"> + <Name>InputPath</Name> + <Item>%(QtWork.Identity)</Item> + <Value>%(QtWork.Identity)</Value> + </LocalPath> + <LocalPath + Condition="'%(ResourceFiles.Identity)' != ''" + Include="@(ResourceFiles->'%(Item)')"> + <Name>InputPath</Name> + <Item>@(ResourceFiles->'%(Value)')</Item> + <Value>@(ResourceFiles->'%(Value)')</Value> + </LocalPath> + <LocalPath Include="%(QtWork.Identity)"> + <Name>OutputPath</Name> + <Item>%(QtWork.OutputFile)</Item> + <Value>%(QtWork.OutputFile)</Value> + </LocalPath> + </ItemGroup> + <HostTranslatePaths + Condition="'$(ApplicationType)' == 'Linux' + AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'" + Items="@(LocalPath)" Names="InputPath;OutputPath"> + <Output TaskParameter="Result" ItemName="HostPath"/> + </HostTranslatePaths> + <ItemGroup> + <InputPath Include="@(HostPath->WithMetadataValue('Name', 'InputPath'))" /> + <OutputPath Include="@(HostPath->WithMetadataValue('Name', 'OutputPath'))" /> + </ItemGroup> + + <!-- // Run command --> + <HostExec + Condition="'$(ApplicationType)' == 'Linux' + AND '%(Identity)' != '' AND '$(DesignTimeBuild)' != 'true'" + Message="@(QtWork->'%(WorkType) %(Identity)')" + Command="@(QtWork->'%(ToolPath) %(Options)')" + Inputs="@(InputPath)" + Outputs="@(OutputPath)" + RemoteTarget="$(ResolvedRemoteTarget)" + RemoteProjectDir="$(_ResolvedRemoteProjectDir)"> + </HostExec> + + <!-- // Generate result item --> + <ItemGroup + Condition="'$(ApplicationType)' == 'Linux' + AND '@(QtWork)' != '' AND '$(DesignTimeBuild)' != 'true'"> + <QtWorkResult Include="@(QtWork)"> + <ExitCode>0</ExitCode> + </QtWorkResult> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Save tracking log of files read during build; used by VS to check the up-to-date status + // --> + <ItemGroup Condition="'$(DesignTimeBuild)' != 'true' AND '$(QtVSToolsBuild)' != 'true'"> + <read_log Include="^%(QtWorkResult.FullPath);%(QtWorkResult.AdditionalDependencies)" + Condition="'%(QtWorkResult.ExitCode)' == '0' AND '%(QtWorkResult.DisableLog)' != 'true'"> + <WorkType>%(QtWorkResult.WorkType)</WorkType> + </read_log> + <read_log> + <Path Condition="$([System.String]::Copy('%(Identity)').StartsWith('^'))">%(Identity)</Path> + <Path Condition="!$([System.String]::Copy('%(Identity)').StartsWith('^'))" + >$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)','%(Identity)'))</Path> + </read_log> + </ItemGroup> + + <WriteLinesToFile + Condition="'@(read_log)' != ''" + File="$(TLogLocation)%(read_log.WorkType).read.1u.tlog" + Lines="@(read_log->MetaData('Path')->ToUpperInvariant());" + Overwrite="true" + Encoding="Unicode"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Save tracking log of files written during build; used by VS to check the up-to-date status + // --> + <ItemGroup Condition="'$(DesignTimeBuild)' != 'true' AND '$(QtVSToolsBuild)' != 'true'"> + <write_log Include="^%(QtWorkResult.FullPath);%(QtWorkResult.OutputFile)" + Condition="'%(QtWorkResult.ExitCode)' == '0' AND '%(QtWorkResult.DisableLog)' != 'true'"> + <WorkType>%(QtWorkResult.WorkType)</WorkType> + </write_log> + <write_log> + <Path Condition="$([System.String]::Copy('%(Identity)').StartsWith('^'))">%(Identity)</Path> + <Path Condition="!$([System.String]::Copy('%(Identity)').StartsWith('^'))" + >$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)','%(Identity)'))</Path> + </write_log> + </ItemGroup> + + <WriteLinesToFile Condition="'@(write_log)' != ''" + File="$(TLogLocation)%(write_log.WorkType).write.1u.tlog" + Lines="@(write_log->MetaData('Path')->ToUpperInvariant());" + Overwrite="true" Encoding="Unicode"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Log output files; this is used by VS to determine what files to delete on "Clean" + // --> + <ItemGroup Condition="'$(DesignTimeBuild)' != 'true' AND '$(QtVSToolsBuild)' != 'true'"> + <clean_log Include="%(QtWorkResult.OutputFile)" + Condition="'%(QtWorkResult.ExitCode)' == '0'"> + <Source>@(QtWorkResult, '|')</Source> + </clean_log> + </ItemGroup> + + <WriteLinesToFile Condition="'@(clean_log)' != ''" + File="$(TLogLocation)$(ProjectName).write.1u.tlog" + Lines="^%(clean_log.Source);@(clean_log->'%(Fullpath)')" + Encoding="Unicode"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Log calls to Qt tools; used in QtWorkPrepare to detect changes to the options of Qt tools + // --> + <WriteLinesToFile Condition="'@(QtWorkLog)' != '' AND '$(DesignTimeBuild)' != 'true'" + File="$(QtLogFilePath)" + Lines="@(QtWorkLog->'%(Identity)|%(Hash)')" + Overwrite="true" Encoding="Unicode"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate build error if a Qt tool did not terminate correctly + // --> + <Error + Condition="'%(QtWorkResult.ExitCode)' != '' + AND '%(QtWorkResult.ExitCode)' != '0' + AND '$(DesignTimeBuild)' != 'true'" + File="%(QtWorkResult.Identity)" Code="%(QtWorkResult.ExitCode)" + Text="%(QtWorkResult.WorkType) (%(QtWorkResult.ToolPath))"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Add dynamic C++ sources to build + // --> + <ItemGroup> + <QtWork_ClCompile + Condition="'%(QtWorkResult.ExitCode)' == '0' AND '%(QtWorkResult.ClCompile)' != ''" + Include="@(QtWorkResult->'%(ClCompile)')"/> + <QtWork_ClCompile + Condition="Exists('$(QtVarsOutputDir)\qtvars_plugin_import.cpp')" + Include="$(QtVarsOutputDir)\qtvars_plugin_import.cpp"/> + <!-- Add QML static plugins --> + <QtWork_ClCompile + Condition="Exists('$(QtQmlPluginImportCpp)')" + Include="$(QtQmlPluginImportCpp)"/> + </ItemGroup> + <ItemGroup Condition="'$(ApplicationType)' == 'Linux'"> + <QtWork_ClCompile Condition="'%(QtWork_ClCompile.ObjectFileName)' == ''"> + <ObjectFileName>$(IntDir)%(Filename).o</ObjectFileName> + </QtWork_ClCompile> + </ItemGroup> + + <!-- // Copy default C++ compiler properties --> + <Expand Condition="'@(QtWork_ClCompile)' != ''" + Items="@(QtWork_ClCompile)" + BaseItem="@(ClCompile->WithMetadataValue('Identity', 'DefaultClCompile'))"> + <Output TaskParameter="Result" ItemName="QtWork_ClCompile_Expanded"/> + </Expand> + + <!-- // Force pre-compiled header include --> + <ItemGroup Condition="'$(ApplicationType)' != 'Linux'"> + <QtWork_ClCompile_Expanded> + <AdditionalIncludeDirectories + >$(ProjectDir);%(QtWork_ClCompile_Expanded.AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <ForcedIncludeFiles Condition="'%(PrecompiledHeader)' == 'Use'" + >%(PrecompiledHeaderFile)</ForcedIncludeFiles> + <AdditionalOptions>$([System.String]::Copy( +'%(QtWork_ClCompile_Expanded.AdditionalOptions) %(QtWork_ClCompile_Expanded.AdditionalCppOptions)') + .Trim())</AdditionalOptions> + </QtWork_ClCompile_Expanded> + </ItemGroup> + + <!-- // Add C++ source items and clean-up temp items --> + <ItemGroup> + <ClCompile Include="@(QtWork_ClCompile_Expanded)"/> + <QtWork_ClCompile_Expanded Remove="@(QtWork_ClCompile_Expanded)"/> + <QtWork_ClCompile Remove="@(QtWork_ClCompile)"/> + </ItemGroup> + + <!--// If sources were manually selected (e.g. by the 'Compile' option in the context menu for + // project items), add generated C++ sources to the list of selected files --> + <PropertyGroup Condition="'$(SelectedFiles)' != ''"> + <SelectedClCompile>@(QtWorkResult->'%(ClCompile)')</SelectedClCompile> + </PropertyGroup> + <PropertyGroup Condition="'$(SelectedClCompile)' != ''"> + <SelectedFiles>$(SelectedFiles);$(SelectedClCompile)</SelectedFiles> + </PropertyGroup> + <ItemGroup Condition="'$(SelectedClCompile)' != ''"> + <SelectedFiles Include="$(SelectedClCompile)"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Update C++ sources with generated information + // --> + <PropertyGroup> + <QtIncludePath>@(QtIncludePath->Distinct())</QtIncludePath> + </PropertyGroup> + <ItemGroup> + <ClCompile_Updated Include="@(ClCompile)"> + <AdditionalIncludeDirectories +>$(QtIncludePath);%(ClCompile.AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile_Updated> + <ClCompile Remove="@(ClCompile)"/> + <ClCompile Include="@(ClCompile_Updated)"/> + <ClCompile_Updated Remove="@(ClCompile_Updated)"/> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <ItemGroup> + <QtWork Remove="@(QtWork)"/> + <QtWorkResult Remove="@(QtWorkResult)"/> + <QtWorkLog Remove="@(QtWorkLog)"/> + <read_log Remove="@(read_log)"/> + <write_log Remove="@(write_log)"/> + <clean_log Remove="@(clean_log)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET Qt + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Root Qt target + // --> + <Target Name="Qt" DependsOnTargets="QtPrepare;QtWork" BeforeTargets="FixupCLCompileOptions"> + <ItemGroup> + <ClCompile Remove="DefaultClCompile" /> + </ItemGroup> + <CriticalSection Lock="false" Name="$(ProjectGuid)" /> + <OnError ExecuteTargets="QtLeaveCriticalSection_OnError"/> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtOuterBuild + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Run targets in $(QtOuterBuildDependsOn) and then recursively invoke build + // --> + <Target Name="QtOuterBuild" DependsOnTargets="$(QtOuterBuildDependsOn)"> + <!--// Invoke inner build: recursive build in second MSBuild instance --> + <MSBuild + Projects="$(MSBuildProjectFullPath)" + Targets="Build" + Properties="QtInnerBuild=$(MSBuildProjectFullPath);RandomFileName=$(RandomFileName);BuildProjectReferences=false"> + </MSBuild> + <OnError ExecuteTargets="QtLeaveCriticalSection_OnError"/> + </Target> + + <PropertyGroup + Condition="'$(QtInnerBuild)' == '' AND '$(DesignTimeBuild)' != 'true'"> + <!--// Outer build: invoke inner build --> + <BuildDependsOn> + $(QtOuterBuildPrepare); + QtOuterBuild; + $(QtOuterBuildFinalize) + </BuildDependsOn> + <QtInnerBuild>$(MSBuildProjectFullPath)</QtInnerBuild> + <RandomFileName>$([System.IO.Path]::GetRandomFileName())</RandomFileName> + </PropertyGroup> + + <PropertyGroup + Condition="'$(QtInnerBuild)' != '$(MSBuildProjectFullPath)' AND '$(DesignTimeBuild)' != 'true'"> + <!--// Dependent project inner build: skip build --> + <BuildDependsOn>$(QtOuterBuildPrepare);$(QtOuterBuildFinalize)</BuildDependsOn> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtEnterCriticalSection_... / QtLeaveCriticalSection_InitializeBuildStatus + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Synchronize calls to InitializeBuildStatus + // --> + <Target Name="QtEnterCriticalSection_InitializeBuildStatus" BeforeTargets="InitializeBuildStatus"> + <CriticalSection Lock="true" Name="$(ProjectGuid)" /> + </Target> + <Target Name="QtLeaveCriticalSection_InitializeBuildStatus" AfterTargets="InitializeBuildStatus"> + <CriticalSection Lock="false" Name="$(ProjectGuid)" /> + </Target> + <PropertyGroup> + <!--// Schedule critical section enter/leave targets around InitializeBuildStatus --> + <QtSync_InitializeBuildStatus> + QtEnterCriticalSection_InitializeBuildStatus; + InitializeBuildStatus; + QtLeaveCriticalSection_InitializeBuildStatus + </QtSync_InitializeBuildStatus> + <!--// Replace 'InitializeBuildStatus' with '$(QtSync_InitializeBuildStatus)' --> + <BuildDependsOn> + $([MSBuild]::Unescape( + $([System.Text.RegularExpressions.Regex]::Replace(' $(BuildDependsOn) ', + '(?<!\w)InitializeBuildStatus(?!\w)', $(QtSync_InitializeBuildStatus))))) + </BuildDependsOn> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtEnterCriticalSection_... / QtLeaveCriticalSection_FinalizeBuildStatus + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Synchronize calls to FinalizeBuildStatus + // --> + <Target Name="QtEnterCriticalSection_FinalizeBuildStatus" BeforeTargets="FinalizeBuildStatus"> + <CriticalSection Lock="true" Name="$(ProjectGuid)" /> + </Target> + <Target Name="QtLeaveCriticalSection_FinalizeBuildStatus" AfterTargets="FinalizeBuildStatus"> + <CriticalSection Lock="false" Name="$(ProjectGuid)" /> + </Target> + <PropertyGroup> + <!--// Schedule critical section enter/leave targets around FinalizeBuildStatus --> + <QtSync_FinalizeBuildStatus> + QtEnterCriticalSection_FinalizeBuildStatus; + FinalizeBuildStatus; + QtLeaveCriticalSection_FinalizeBuildStatus + </QtSync_FinalizeBuildStatus> + <!--// Replace 'FinalizeBuildStatus' with '$(QtSync_FinalizeBuildStatus)' --> + <BuildDependsOn> + $([MSBuild]::Unescape( + $([System.Text.RegularExpressions.Regex]::Replace(' $(BuildDependsOn) ', + '(?<!\w)FinalizeBuildStatus(?!\w)', $(QtSync_FinalizeBuildStatus))))) + </BuildDependsOn> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtLeaveCriticalSection_OnError + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Fail-safe release of critical section lock, to be used in OnError tasks + // --> + <Target Name="QtLeaveCriticalSection_OnError"> + <CriticalSection Lock="false" Name="$(ProjectGuid)" /> + </Target> +</Project> diff --git a/QtMSBuild/QtMsBuild/qt_private.props b/QtMSBuild/QtMsBuild/qt_private.props new file mode 100644 index 0000000..c1303e0 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt_private.props @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Evaluation of Qt properties +// +// Preceding evaluation chain: +// * Project global properties, incl. $(Keyword), $(WindowsTargetPlatformVersion), $(QtMsBuild) +// * Microsoft.Cpp.Default.props +// * Configuration properties, incl. $(ConfigurationType), $(PlatformToolset) +// * qt_defaults.props +// * Qt build settings, incl. $(QtInstall), $(QtModules) +// * Property sheets preceding Qt.props +// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <QtPrivateLoaded>true</QtPrivateLoaded> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Fail-safe import of default values + // --> + <Import + Condition="'$(QtDefaultsLoaded)' != 'true'" + Project="$(MSBuildThisFileDirectory)\qt_defaults.props"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Setup Qt installation path + // --> + <PropertyGroup Condition="'$(QtVsProjectSettings)' == 'true'"> + <QtInstallDir Condition="Exists('$(QtInstall)')">$(QtInstall)</QtInstallDir> + <QtInstallRegKey Condition="'$(QtInstall)' != ''" + >$(QtVersionsRegKey)\$(QtInstall)</QtInstallRegKey> + <QtInstallRegDir Condition="'$(QtInstallDir)' == '' AND '$(QtInstallRegKey)' != ''" + >$([MSBuild]::GetRegistryValue('$(QtInstallRegKey)','InstallDir'))</QtInstallRegDir> + <QtInstallDir + Condition="'$(ApplicationType)' != 'Linux' AND '$(QtInstallDir)' == '' + AND Exists('$(QtInstallRegDir)')" + >$(QtInstallRegDir)</QtInstallDir> + <QtInstallDir + Condition="'$(ApplicationType)' == 'Linux' AND '$(QtInstallDir)' == '' + AND ( $(QtInstallRegDir.StartsWith('SSH:')) OR $(QtInstallRegDir.StartsWith('WSL:')) )" + >$(QtInstallRegDir.Split(':')[1])</QtInstallDir> + <QtInstallDir + Condition="'$(ApplicationType)' == 'Linux' AND '$(QtInstallDir)' == ''" + >$(QtInstall)</QtInstallDir> + </PropertyGroup> + <PropertyGroup Condition="'$(QtInstallDir)' != ''"> + <QtToolsPath Condition="'$(QtToolsPath)' == ''" + >$([System.IO.Path]::Combine('$(QtInstallDir)','$(QtPathBinaries)').Replace('\', '/'))</QtToolsPath> + <QtDllPath Condition="'$(QtDllPath)' == ''" + >$([System.IO.Path]::Combine('$(QtInstallDir)','$(QtPathLibraryExecutables)').Replace('\', '/'))</QtDllPath> + <QTDIR>$(QtInstallDir)</QTDIR> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt Variables Expansion + // --> + <PropertyGroup Condition="'$(QtVsProjectSettings)' == 'true'"> + + <!--// Path to Qt variables property file --> + <QtVarsOutputDir Condition="'$(QtVarsOutputDir)' == ''" + >$([System.IO.Path]::Combine('$(ProjectDir)', '$(IntDir)', 'qmake'))</QtVarsOutputDir> + <QtVarsFileName Condition="'$(QtVarsFileName)' == ''" + >qtvars_$(Platform.Replace(' ','_'))_$(Configuration.Replace(' ','_')).props</QtVarsFileName> + <QtVarsFilePath Condition="'$(QtVarsFilePath)' == ''" + >$(QtVarsOutputDir)\$(QtVarsFileName)</QtVarsFilePath> + + <!--// Path to temp work folder --> + <QtVarsWorkDirName + >temp</QtVarsWorkDirName> + <QtVarsWorkDir + >$([System.IO.Path]::Combine('$(QtVarsOutputDir)','$(QtVarsWorkDirName)'))</QtVarsWorkDir> + <QtVarsWorkPath + >$(QtVarsWorkDir)\$(QtVarsFileName)</QtVarsWorkPath> + <QtVarsIndexPathDesignTime + >$(QtVarsWorkDir)\$(ProjectGuid.Replace('{','').Replace('}','')).$(ProjectName).designtime.idx</QtVarsIndexPathDesignTime> + <QtVarsDesignTime Condition="Exists('$(QtVarsIndexPathDesignTime)')" + >$([System.String]::Copy($([System.IO.File]::ReadAllText('$(QtVarsIndexPathDesignTime)'))).Replace('
',''))</QtVarsDesignTime> + </PropertyGroup> + + <!--// Import Qt variables (full build) --> + <Import + Condition="'$(DesignTimeBuild)' != 'true' AND Exists('$(QtVarsFilePath)')" + Project="$(QtVarsFilePath)"/> + + <!--// Import Qt variables (design-time build) --> + <Import + Condition="'$(DesignTimeBuild)' == 'true' AND Exists('$(QtVarsDesignTime)')" + Project="$(QtVarsDesignTime)"/> + + <!--// Import Qt variables (fall-back) --> + <Import + Condition= +"'$(DesignTimeBuild)' == 'true' AND !Exists('$(QtVarsDesignTime)') AND Exists('$(QtVarsFilePath)')" + Project="$(QtVarsFilePath)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Add Qt DLL path to debugger definitions + // --> + <PropertyGroup> + <LocalDebuggerEnvironment Condition="'$(QtDllPath)' != ''" + >PATH=%PATH%;$(QtDllPath)
$(LocalDebuggerEnvironment) + </LocalDebuggerEnvironment> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QML debugging + // --> + <PropertyGroup> + <QmlDebug Condition="'$(QtQMLDebugEnable)' == 'true'" + >-qmljsdebugger=file:$(ProjectGuid),block</QmlDebug> + <LocalDebuggerCommandArguments + >$(QmlDebug)</LocalDebuggerCommandArguments> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Outer build + // --> + <PropertyGroup> + <QtOuterBuildPrepare Condition="'$(QtOuterBuildPrepare)' == ''" + >ResolveReferences;PrepareForBuild;InitializeBuildStatus</QtOuterBuildPrepare> + <QtOuterBuildFinalize Condition="'$(QtOuterBuildFinalize)' == ''" + >FinalizeBuildStatus</QtOuterBuildFinalize> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Extract Qt build settings from qmake-generated project files + // --> + <PropertyGroup> + <QtVars + Condition="'$(QtQMakeTemplate)' == 'app'" + >$(QMake_Makefile)</QtVars> + <QtVars + Condition="'$(QtQMakeTemplate)' == 'vcapp'" + >$(QMake_MSBuild);</QtVars> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Default item metadata + // --> + <ItemDefinitionGroup> + + <!--// C++ --> + <ClCompile> + <PreprocessorDefinitions Condition="'$(Qt_DEFINES_)' != ''" + >$(Qt_DEFINES_);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions Condition="'$(QtQMLDebugEnable)' == 'true'" + >QT_QML_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories Condition="'$(Qt_INCLUDEPATH_)' != ''" + >$(Qt_INCLUDEPATH_);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <LanguageStandard Condition="'$(Qt_STDCPP_)' != ''" + >$(Qt_STDCPP_)</LanguageStandard> + <AdditionalOptions Condition="'$(Qt_CL_OPTIONS_)' != ''" + >$(Qt_CL_OPTIONS_) %(AdditionalOptions)</AdditionalOptions> + </ClCompile> + + <!--// Linker (.obj files) --> + <Link> + <AdditionalDependencies Condition="'$(Qt_LIBS_)' != ''" + >$(Qt_LIBS_);%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalLibraryDirectories Condition="'$(Qt_LIBPATH_)' != ''" + >$(Qt_LIBPATH_);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <SharedLibrarySearchPath Condition="'$(Qt_LIBPATH_)' != ''" + >$(Qt_LIBPATH_);%(SharedLibrarySearchPath)</SharedLibrarySearchPath> + <AdditionalOptions Condition="'$(Qt_LINK_OPTIONS_)' != ''" + >$(Qt_LINK_OPTIONS_) %(AdditionalOptions)</AdditionalOptions> + </Link> + </ItemDefinitionGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Check static plugin import: + // * If qmake generated a platform plugin import, assume QML plugins are also required. + // --> + <PropertyGroup Condition="Exists('$(QtVarsWorkDir)\qtvars_plugin_import.cpp')"> + <QtStaticPlugins>true</QtStaticPlugins> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Exclude Qt headers from code analysis + // --> + <PropertyGroup> + <CAExcludePath>$(CAExcludePath);$(Qt_INCLUDEPATH_)</CAExcludePath> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Evaluate subfolder properties + // --> + <Import + Project="$(QtMsBuildProps)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/qt_settings.targets b/QtMSBuild/QtMsBuild/qt_settings.targets new file mode 100644 index 0000000..1014ad1 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt_settings.targets @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Add property page for Qt settings +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Condition="'$(QtVsProjectSettings)' == 'true'"> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qt_settings.xml"> + <Context>Project;PropertySheet</Context> + </PropertyPageSchema> + </ItemGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/qt_settings.xml b/QtMSBuild/QtMsBuild/qt_settings.xml new file mode 100644 index 0000000..d078dc6 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt_settings.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Rule + Name="QtRule10_Settings" + DisplayName="Qt Project Settings" + PageTemplate="generic" + Description="Qt Project Settings" + Order="8" + xmlns="http://schemas.microsoft.com/build/2009/properties"> + <Rule.DataSource> + <DataSource Persistence="ProjectFile" Label="QtSettings" HasConfigurationCondition="true"/> + </Rule.DataSource> + <Rule.Categories> + <Category Name="QtSettings_01_General" DisplayName="General"/> + <Category Name="QtSettings_02_Paths" DisplayName="Paths"/> + <Category Name="QtSettings_03_QMake" DisplayName="qmake"/> + <Category Name="QtSettings_04_QML" DisplayName="QML"/> + </Rule.Categories> + <EnumProperty + Name="Keyword" + DisplayName="Qt VS Project Format Version" + Category="QtSettings_01_General" + ReadOnly="true" + Description="Version of the format of this Qt Visual Studio project."> + <EnumProperty.DataSource> + <DataSource Persistence="ProjectFile" Label="Globals" /> + </EnumProperty.DataSource> + <EnumValue Name="Qt4VSv1.0" DisplayName="Version 2.0"/> + <EnumValue Name="QtVS_v300" DisplayName="Version 3.0"/> + <EnumValue Name="QtVS_v301" DisplayName="Version 3.1"/> + <EnumValue Name="QtVS_v302" DisplayName="Version 3.2"/> + <EnumValue Name="QtVS_v303" DisplayName="Version 3.3"/> + <EnumValue Name="QtVS_v304" DisplayName="Version 3.4"/> + </EnumProperty> + <StringProperty + Name="QtLastBackgroundBuild" + DisplayName="Last Background Build" + Category="QtSettings_01_General" + Visible="False"> + <StringProperty.DataSource> + <DataSource Persistence="UserFile" Label="QtSettings" HasConfigurationCondition="true"/> + </StringProperty.DataSource> + </StringProperty> + <DynamicEnumProperty + Name="QtInstall" + DisplayName="Qt Installation" + Category="QtSettings_01_General" + EnumProvider="QtVersionProvider" + Description= +"Select Qt installation; can be either a path to an installation directory or a version name registered through the Qt Visual Studio Tools (v2.4 or above)."> + </DynamicEnumProperty> + <StringListProperty + Name="QtModules" + Category="QtSettings_01_General" + DisplayName="Qt Modules"> + <StringListProperty.ValueEditors> + <ValueEditor EditorType="QtModulesEditor" DisplayName="<Select Modules...>" /> + </StringListProperty.ValueEditors> + </StringListProperty> + <EnumProperty + Name="QtBuildConfig" + Category="QtSettings_01_General" + DisplayName="Build Config" + Description="Select whether to use debug or release."> + <EnumValue Name="debug" DisplayName="Debug"/> + <EnumValue Name="release" DisplayName="Release"/> + </EnumProperty> + <BoolProperty + Name="QtDeploy" + Category="QtSettings_01_General" + DisplayName="Run Deployment Tool" + Description="Select whether Qt for Windows Deployment Tool (windeployqt) should be called after build."/> + <StringProperty + Name="QtPathBinaries" + Category="QtSettings_02_Paths" + DisplayName="Qt Binaries" + Description= +"Path to folder containing executables of Qt tools (qmake, moc, etc.) When a non-rooted path is specified, it will be considered relative to the Qt installation directory."/> + <StringProperty + Name="QtPathLibraryExecutables" + Category="QtSettings_02_Paths" + DisplayName="Qt Library Executables" + Description= +"Path to folder containing executable files of Qt modules. When a non-rooted path is specified, it will be considered relative to the Qt installation directory."/> + <StringListProperty + Name="QtHeaderSearchPath" + Category="QtSettings_02_Paths" + DisplayName="Additional Qt header search paths" /> + <StringListProperty + Name="QtLibrarySearchPath" + Category="QtSettings_02_Paths" + DisplayName="Additional Qt library search paths" /> + <EnumProperty + Name="QtQMakeTemplate" + Category="QtSettings_03_QMake" + DisplayName="Template" + Description="Template of generated project files for extraction of Qt build settings."> + <EnumValue Name="vcapp" DisplayName="MSBuild" /> + <EnumValue Name="app" DisplayName="Makefile" /> + </EnumProperty> + <StringListProperty + Name="QtVars" + Category="QtSettings_03_QMake" + DisplayName="Extract Variables" + Visible="False" /> + <StringListProperty + Name="QMakeExtraArgs" + Category="QtSettings_03_QMake" + DisplayName="Additional Command Arguments" /> + <StringListProperty + Name="QMakeCodeLines" + Category="QtSettings_03_QMake" + DisplayName="Additional Project Settings" /> + <BoolProperty + Name="QtQMLDebugEnable" + Category="QtSettings_04_QML" + DisplayName="Enable QML Debugging" + Description="Select whether to launch a QML session when debugging."> + </BoolProperty> +</Rule> diff --git a/QtMSBuild/QtMsBuild/qt_tasks.targets_TT b/QtMSBuild/QtMsBuild/qt_tasks.targets_TT new file mode 100644 index 0000000..fe1d2e4 --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt_tasks.targets_TT @@ -0,0 +1,212 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +***************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +***************************************************************************** +<#@output extension="targets" #> +<#@include file="$(SolutionDir)\common.tt" #> +** <#=WARNING_GENERATED_FILE#> +***************************************************************************** +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Helper inline tasks used by the Qt/MSBuild targets +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + + <!-- BEGIN Generated code <#=XML_COMMENT_END#><# + + // Parse .cs files and generate inline MSBuild tasks using C# code + // + var files = Directory.GetFiles(Path.Combine(SolutionDir, "QtMsBuild", "Tasks"), "*.cs"); + foreach(var file in files) { + var text = File.ReadAllText(file); + var result = TaskParser.Parse(text); + var commentLines = (result.GetValues<string>("COMMENT") + .FirstOrDefault() ?? "") + .TrimEnd('\r', '\n').Split(new[] {"\r\n"}, StringSplitOptions.None); + var taskAttribs = result.GetValues<string>("ATTRIBS") + .FirstOrDefault() ?? ""; + var namespaces = result.GetValues<List<string>>("USING") + .FirstOrDefault() ?? new List<string>(); + var parameters = (result.GetValues<List<ParamDecl>>("PARAMS") + .FirstOrDefault() ?? new List<ParamDecl>()) + .Where((ParamDecl p) => !string.IsNullOrEmpty(p.name)) + .OrderBy((ParamDecl p) => p.isOut) + .ThenBy((ParamDecl p) => p.isOptional); + var taskCode = result.GetValues<string>("CODE") + .FirstOrDefault()?.TrimEnd('\r', '\n') ?? ""; + var assemblyRefs = (result.GetValues<List<string>>("REFERENCE") + .FirstOrDefault() ?? new List<string>()) + .Select(assemblyRef => assemblyRef.Trim()); + + WriteLine(string.Format( +//////////////////////////////////////////////////////////////////////////////////////////////////// +@" + {0} + {1} + {2} + <UsingTask {3} + TaskFactory=""CodeTaskFactory"" + AssemblyFile=""$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll""> + <ParameterGroup>{4} + </ParameterGroup> + <Task>{5}{6} + <Code Type=""Fragment"" Language=""cs""> + <![CDATA[ +{7} + ]]> + </Code> + </Task> + </UsingTask>", +//////////////////////////////////////////////////////////////////////////////////////////////////// + /*{0}*/ XML_COMMENT_BEGIN, + /*{1}*/ string.Join("\r\n ", commentLines), + /*{2}*/ XML_COMMENT_END, + /*{3}*/ taskAttribs, + /*{4}*/ string.Join("", parameters.Select(param => string.Format( +@" + <{0} ParameterType=""{1}""{2}{3}/>", + /*{0}*/ param.name, + /*{1}*/ param.type, + /*{2}*/ param.isOut ? @" Output=""true""" : "", + /*{3}*/ !param.isOut && !param.isOptional ? @" Required=""true""" : ""))), + /*{5}*/ string.Join("", assemblyRefs.Select(assemblyRef => string.Format( +@" + <Reference Include=""{0}""/>", + /*{0}*/ assemblyRef))), + /*{6}*/ string.Join("", namespaces.Select(nameSpc => string.Format( +@" + <Using Namespace=""{0}""/>", + /*{0}*/ nameSpc))), + /*{7}*/ taskCode)); + } + #> + <#=XML_COMMENT_BEGIN#> END Generated code --> + +</Project> +<!--<#=XML_COMMENT_END#> +<#@assembly Name="$(SolutionDir)\QtVsTools.RegExpr\bin\$(Configuration)\QtVsTools.RegExpr.dll" #> +<#@import namespace="static QtVsTools.SyntaxAnalysis.RegExpr" #> +<#+ + class ParamDecl { + public string name; + public string type; + public bool isOptional; + public bool isOut; + } + + Parser InitTaskParser() + { + var name + = CharSet[CharWord + Chars['.']].Repeat(); + + var assemblyRef + = StartOfLine & HorizSpace & "//" & new Token("REF", HorizSpace, Line) & LineBreak; + + var usingStmt + = new Token("using") + & new Token("NAMESPACE", new Token("static").Optional() & name) + & new Token(";"); + + var paramDecl = new Token("PARAM", + new Token("PARAM_OUT", "out").Optional() + & new Token("PARAM_TYPE", name & new Token("[]").Optional()) + & new Token("PARAM_NAME", Word) + & new Token("PARAM_OPTIONAL", "=" + & (new Token("null") | new Token("0") | new Token("false"))).Optional() + & new Token(CharSet[',', ')'])) + { + new Rule<ParamDecl> + { + Update("PARAM_NAME", (ParamDecl p, string value) => p.name = value), + Update("PARAM_TYPE", (ParamDecl p, string value) => p.type = value), + Update("PARAM_OUT", (ParamDecl p, string value) => p.isOut = true), + Update("PARAM_OPTIONAL", (ParamDecl p, string value) => p.isOptional = true), + } + }; + + var regionReference + = StartOfLine & HorizSpace & "#region Reference" & Line & LineBreak + & new Token("REFERENCE", SkipWs_Disable, assemblyRef.Repeat()) + { + new Rule<List<string>> + { + Update("REF", (List<string> list, string refName) => list.Add(refName)) + } + }; + + var regionUsing + = StartOfLine & HorizSpace & "#region Using" & Line & LineBreak + & new Token("USING", usingStmt.Repeat()) + { + new Rule<List<string>> + { + Update("NAMESPACE", (List<string> list, string nameSpc) => list.Add(nameSpc)) + } + }; + + var regionComment + = StartOfLine & HorizSpace & "#region Comment" & Line & LineBreak + & new Token("COMMENT", SkipWs_Disable, + (!LookAhead[StartOfLine & HorizSpace & "#endregion"] + & StartOfLine & HorizSpace & Line & LineBreak).Repeat()); + + var regionParameters + = StartOfLine & HorizSpace & "#region Parameters" & Line & LineBreak + & new Token("PARAMS", paramDecl.Repeat()) + { + new Rule<List<ParamDecl>> + { + Update("PARAM", (List<ParamDecl> list, ParamDecl param) => list.Add(param)) + } + }; + + var regionCode + = StartOfLine & HorizSpace & "#region Code" & Line & LineBreak + & new Token("CODE", SkipWs_Disable, + (!LookAhead[StartOfLine & HorizSpace & "#endregion"] + & StartOfLine & HorizSpace & Line & LineBreak).Repeat()); + + var task + = StartOfLine & HorizSpace & "#region Task" & new Token("ATTRIBS", Line) & LineBreak + & ( regionReference + | regionUsing + | regionComment + | regionParameters + | regionCode + | (Line & LineBreak) + ).Repeat(); + + return task.Render(Space); + } + + Parser _TaskParser; + Parser TaskParser => _TaskParser ?? ( + _TaskParser = InitTaskParser()); +//-->#> diff --git a/QtMSBuild/QtMsBuild/qt_vars.targets b/QtMSBuild/QtMsBuild/qt_vars.targets new file mode 100644 index 0000000..80c141c --- /dev/null +++ b/QtMSBuild/QtMsBuild/qt_vars.targets @@ -0,0 +1,594 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Read Qt Build Variables +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + + <PropertyGroup Condition="'$(QtVsProjectSettings)' == 'true'"> + <QtOuterBuildDependsOn>QtVarsPrepare;QtVars;$(QtDependsOn)</QtOuterBuildDependsOn> + <CleanDependsOn>$(CleanDependsOn);QtVarsClean</CleanDependsOn> + </PropertyGroup> + + <Target Name="ResolveHost" DependsOnTargets="ResolveRemoteDir" + Condition="'$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' != 'WSL_1_0'"> + <ResolveRemoteDir + Condition="'$(_ResolvedRemoteProjectDir)' == ''" + RemoteProjectDir="$(RemoteProjectDir)" + RemoteTarget="$(ResolvedRemoteTarget)" + ProjectDir="$(ProjectDir)" + IntermediateDir="$(IntDir)" + RemoteProjectDirFile="$(RemoteProjectDirFile)"> + <Output TaskParameter="ResolvedRemoteProjectDir" PropertyName="_ResolvedRemoteProjectDir"/> + </ResolveRemoteDir> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtVars + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Call qmake with generated .pro file to export Qt variables into MSBuild .props file + // --> + <Target + Name="QtVars" + Condition="'$(QtVsProjectSettings)' == 'true'" + DependsOnTargets="QtMocInit;QtUicInit;ResolveHost" + Inputs="$(MSBuildProjectFile)" Outputs="$(QtVarsFilePath)"> + + <!--// Check if qmake is in the tools path --> + <Error Condition="'$(QtToolsPath)' == ''" Text="There's no Qt version assigned to project $(MSBuildProjectFile) for configuration $(Configuration)/$(Platform).
Please set a Qt installation in 'Project|Properties|Configuration Properties|Qt Project Settings|Qt Installation'."/> + <Error + Condition="'$(ApplicationType)' != 'Linux' AND !Exists('$(QtToolsPath)\qmake.exe')" + Text="qmake not found in $(QtToolsPath) when building project $(MSBuildProjectFile)"/> + + <!--// Ensure C++ compiler in PATH --> + <Exec + Condition="'$(ApplicationType)' != 'Linux'" + Command="WHERE /Q cl.exe" IgnoreExitCode="true"> + <Output TaskParameter="ExitCode" PropertyName="ErrorLevel"/> + </Exec> + <SetEnv + Condition="'$(ApplicationType)' != 'Linux' AND '$(ErrorLevel)' != '0'" + Name="PATH" Prefix="true" Value="$(ExecutablePath)"/> + + <!--// Set-up temp directory --> + <RemoveDir + Directories="$(QtVarsWorkDir)"/> + <MakeDir + Directories="$(QtVarsWorkDir)"/> + + <!--// Translate local paths to build host paths --> + <ItemGroup> + <LocalPath Include="WorkDir"> + <Name>Path</Name> + <Item>$(QtVarsWorkDir)</Item> + <Value>$(QtVarsWorkDir)</Value> + </LocalPath> + <LocalPath Include="QMakeProps"> + <Name>Path</Name> + <Item>$(QtVarsWorkDir)\props.txt</Item> + <Value>$(QtVarsWorkDir)\props.txt</Value> + </LocalPath> + <LocalPath Include="QMakeLog"> + <Name>Path</Name> + <Item>$(QtVarsWorkDir)\qtvars.log</Item> + <Value>$(QtVarsWorkDir)\qtvars.log</Value> + </LocalPath> + <LocalPath Include="QMakeProj"> + <Name>Path</Name> + <Item>$(QtVarsWorkDir)\qtvars.pro</Item> + <Value>$(QtVarsWorkDir)\qtvars.pro</Value> + </LocalPath> + <LocalPath Include="QtMakefile"> + <Name>Path</Name> + <Item>$(QtVarsWorkDir)\Makefile</Item> + <Value>$(QtVarsWorkDir)\Makefile</Value> + </LocalPath> + </ItemGroup> + <HostTranslatePaths Items="@(LocalPath)" Names="Path"> + <Output TaskParameter="Result" ItemName="HostPath"/> + </HostTranslatePaths> + <ItemGroup> + <WorkDir Include="@(HostPath->WithMetadataValue('Identity', 'WorkDir'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </WorkDir> + <QMakeProps Include="@(HostPath->WithMetadataValue('Identity', 'QMakeProps'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </QMakeProps> + <QMakeLog Include="@(HostPath->WithMetadataValue('Identity', 'QMakeLog'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </QMakeLog> + <QMakeProj Include="@(HostPath->WithMetadataValue('Identity', 'QMakeProj'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </QMakeProj> + <QtMakefile Include="@(HostPath->WithMetadataValue('Identity', 'QtMakefile'))"> + <LocalPath>%(Item)</LocalPath> + <HostPath>%(Value)</HostPath> + </QtMakefile> + </ItemGroup> + + <!--// Run qmake: query properties --> + <PropertyGroup> + <Cmd><![CDATA["$(QtToolsPath)/qmake" -query]]></Cmd> + </PropertyGroup> + <HostExec + Command="$(Cmd)" RedirectStdOut="props.txt" + WorkingDirectory="@(WorkDir->'%(HostPath)')" + Outputs="@(QMakeProps)" + RemoteTarget="$(ResolvedRemoteTarget)" + RemoteProjectDir="$(_ResolvedRemoteProjectDir)" /> + <ReadLinesFromFile + File="@(QMakeProps->'%(LocalPath)')"> + <Output TaskParameter="Lines" ItemName="QMakeQueryResult"/> + </ReadLinesFromFile> + + <!--// Parse qmake query result --> + <ItemGroup> + <QMakeQueryResult> + <SepIdx>$([System.String]::Copy('%(Identity)').IndexOf(':'))</SepIdx> + </QMakeQueryResult> + <QMakeQueryResult> + <ValueIdx>$([MSBuild]::Add(%(SepIdx), 1))</ValueIdx> + </QMakeQueryResult> + <QMakeQueryResult> + <Name>$([System.String]::Copy('%(Identity)').Substring(0, %(SepIdx)).Replace('/', "SLASH"))</Name> + <Value>$([System.String]::Copy('%(Identity)').Substring(%(ValueIdx)))</Value> + </QMakeQueryResult> + <QMakeProperty + Include="@(QMakeQueryResult->'%(Name)')" + RemoveMetadata="Name;SepIdx;ValueIdx"/> + </ItemGroup> + + <!--// Parse Qt version --> + <PropertyGroup Condition="'@(QMakeProperty)' != ''"> + <QtVersionMajor>0</QtVersionMajor> + <QtVersionMinor>0</QtVersionMinor> + <QtVersionPatch>0</QtVersionPatch> + <QtVersionParts>0</QtVersionParts> + <QtVersion + >@(QMakeProperty->WithMetadataValue('Identity', 'QT_VERSION')->'%(Value)')</QtVersion> + <QtVersionParts + Condition="'$(QtVersion)' != ''">$(QtVersion.Split('.').Length)</QtVersionParts> + <QtVersionMajor Condition="$(QtVersionParts) >= 1">$(QtVersion.Split('.')[0])</QtVersionMajor> + <QtVersionMinor Condition="$(QtVersionParts) >= 2">$(QtVersion.Split('.')[1])</QtVersionMinor> + <QtVersionPatch Condition="$(QtVersionParts) >= 3">$(QtVersion.Split('.')[2])</QtVersionPatch> + </PropertyGroup> + + <!--// Workaround for 5.13.2 NDEBUG obsession --> + <PropertyGroup + Condition="$(QtVersionMajor) > 5 + OR ($(QtVersionMajor) == 5 AND ($(QtVersionMinor) > 13 + OR ($(QtVersionMinor) == 13 AND ($(QtVersionPatch) >= 2))))"> + <QMakeOptionEarly>true</QMakeOptionEarly> + </PropertyGroup> + + <!--// qmake command line arguments --> + <ItemGroup> + <QMakeArgsList Condition="'$(QMakeOptionEarly)' == 'true'" Include="-early"/> + <QMakeArgsList Include="CONFIG -= debug release debug_and_release"/> + <QMakeArgsList Include="CONFIG += $(QtBuildConfig)"/> + <QMakeArgsList Include="$(QMakeExtraArgs)"/> + </ItemGroup> + <ItemGroup> + <QMakeArgsList> + <Escaped>%(Identity)</Escaped> + <Escaped Condition="$([System.String]::Copy('%(Identity)').Contains(' ')) + OR $([System.String]::Copy('%(Identity)').Contains('"'))" + >"$([System.String]::Copy('%(Identity)').Replace('"','\"'))"</Escaped> + </QMakeArgsList> + </ItemGroup> + <PropertyGroup> + <QMakeArgs>@(QMakeArgsList->'%(Escaped)', ' ')</QMakeArgs> + <QMakeArgs Condition="'$(QtQMakeTemplate)' == 'vcapp'"> -tp vc $(QMakeArgs)</QMakeArgs> + </PropertyGroup> + + <!--// Modules and additional .pro file lines --> + <ItemGroup> + <QtModuleList Include="$(QtModules)"/> + <QMakeCodeLine Include="$(QMakeCodeLines)"/> + </ItemGroup> + + <!--// Generate INCLUDEPATH value --> + <ItemGroup> + <QtIncludesList Include="$(QtHeaderSearchPath)"/> + <QtIncludes Include= +"$([System.IO.Path]::Combine($(QtInstallDir),$([System.String]::Copy('%(QtIncludesList.Identity)'))))"/> + <QtIncludesList Remove="@(QtIncludesList)"/> + </ItemGroup> + <PropertyGroup> + <QtIncludes>@(QtIncludes->'"%(Identity)"', ' ')</QtIncludes> + </PropertyGroup> + + <!--// Generate LIBS value --> + <ItemGroup> + <QtLibsList Include="$(QtLibrarySearchPath)"/> + <QtLibs Include= +"$([System.IO.Path]::Combine($(QtInstallDir),$([System.String]::Copy('%(QtLibsList.Identity)'))))"/> + <QtLibsList Include="@(QtLibsList)"/> + </ItemGroup> + <PropertyGroup> + <QtLibs>@(QtLibs->'"-L%(Identity)"', ' ')</QtLibs> + </PropertyGroup> + + <Message + Importance="high" + Text="Reading Qt configuration ($(QtToolsPath)/qmake)"/> + + <PropertyGroup> + + <!--// .pro file configuration --> + <QtVarsProFileConfig> + CONFIG += no_fixpath + </QtVarsProFileConfig> + + <!--// .pro file input parameters --> + <QtVarsProFileInput> + <!-- +# Selected Qt modules --> + QT += @(QtModuleList->'%(Identity)', ' ') + <!-- +# Custom additional .pro file code (from property page) --> + @(QMakeCodeLine->'%(Identity)','%0D%0A') + </QtVarsProFileInput> + <!-- +# Custom additional header search paths (from property page) --> + <QtVarsProFileInput Condition="'$(QtHeaderSearchPath)' != ''"> + $(QtVarsProFileInput) + INCLUDEPATH += $(QtIncludes) + </QtVarsProFileInput> + <!-- +# Custom additional library search paths (from property page) --> + <QtVarsProFileInput Condition="'$(QtLibrarySearchPath)' != ''"> + $(QtVarsProFileInput) + LIBS += $(QtLibs) + </QtVarsProFileInput> + <!-- +# Undef UNICODE macro if project is configured with multi-byte char set --> + <QtVarsProFileInput Condition="'$(CharacterSet)' == 'MultiByte'"> + $(QtVarsProFileInput) + DEFINES -= UNICODE _UNICODE + </QtVarsProFileInput> + </PropertyGroup> + + <!--// Write .pro file to temp path --> + <WriteLinesToFile + File="$(QtVarsWorkDir)\qtvars.pro" + Lines="$(QtVarsProFileConfig) + ;$(QtVarsProFileInput)"/> + + <!--// Write start of Qt vars data file: + // * Open property group tag --> + <PropertyGroup> + <!-- +######## BEGIN generated XML (.props) ############################################################## +## --> + <QtVarsDataFileText> + <![CDATA[ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + ]]> + </QtVarsDataFileText> + <!-- +## +######## END generated XML (.props) ################################################################ + --> + </PropertyGroup> + <WriteLinesToFile File="$(QtVarsWorkPath)" Lines="$(QtVarsDataFileText)" Overwrite="true"/> + + <!--// Run qmake: convert Makefile vars to .props (XML) file --> + <PropertyGroup> + <Cmd><![CDATA["$(QtToolsPath)/qmake" $(QMakeArgs) qtvars.pro]]></Cmd> + </PropertyGroup> + <HostExec + Command="$(Cmd)" RedirectStdOut="qtvars.log" RedirectStdErr="STDOUT" + WorkingDirectory="@(WorkDir->'%(HostPath)')" + Inputs="@(QMakeProj)" + Outputs="@(QtMakefile);@(QMakeLog)" + RemoteTarget="$(ResolvedRemoteTarget)" + RemoteProjectDir="$(_ResolvedRemoteProjectDir)" + IgnoreExitCode="true"> + <Output TaskParameter="ExitCode" PropertyName="ErrorLevel"/> + </HostExec> + + <!--// Check qmake result --> + <PropertyGroup + Condition="'$(ErrorLevel)' != '0' + OR ( '$(QtQMakeTemplate)' == 'app' AND !Exists('$(QtVarsWorkDir)\Makefile') ) + OR ( '$(QtQMakeTemplate)' == 'vcapp' AND !Exists('$(QtVarsWorkDir)\qtvars.vcxproj') )"> + <QMakeError>true</QMakeError> + </PropertyGroup> + + <!--// Get qmake console output --> + <ItemGroup> + <QMakeError Include="($(QtToolsPath)/qmake)"/> + <QMakeError Include="%24PWD=$(QtVarsWorkDir)"/> + </ItemGroup> + <ReadLinesFromFile + File="$(QtVarsWorkDir)\qtvars.log"> + <Output TaskParameter="Lines" ItemName="QMakeError"/> + </ReadLinesFromFile> + <ItemGroup> + <QMakeError + Condition="$([System.String]::Copy('%(Identity)').StartsWith('Info: creating stash file'))" + Remove="@(QMakeError)"/> + <QMakeError + Condition="'$(QtQMakeTemplate)' == 'app' AND !Exists('$(QtVarsWorkDir)\Makefile')" + Include="Error creating Makefile"/> + <QMakeError + Condition="'$(QtQMakeTemplate)' == 'vcapp' AND !Exists('$(QtVarsWorkDir)\qtvars.vcxproj')" + Include="Error creating .vcxproj file"/> + </ItemGroup> + + <!--// If error, show qmake console output and stop build --> + <Error + Condition="'$(QMakeError)' == 'true'" + File="$(MSBuildProjectFile)" + Text="ERROR running qmake%0D%0Aqmake: @(QMakeError->'%(Identity)','%0D%0Aqmake: ')%0D%0A"/> + <Message + Importance="high" + Condition="'$(QMakeError)' != 'true' AND '$(QtVarsDebug)' == 'true'" + Text="qmake: @(QMakeError->'%(Identity)','%0D%0Aqmake: ')"/> + + + <!--//// Extract Qt vars from Makefile + // --> + <!--// Parse variable definition expressions for Makefile --> + <ParseVarDefs + Condition="'$(QtQMakeTemplate)' == 'app' AND '$(QtVars)' != ''" + QtVars="$(QtVars)"> + <Output TaskParameter="OutVarDefs" ItemName="QMake_Makefile_VarDef"/> + </ParseVarDefs> + + <!--// Read variables from Makefile --> + <GetVarsFromMakefile + Condition="'$(QtQMakeTemplate)' == 'app'" + Makefile="$(QtVarsWorkDir)\Makefile" VarDefs="@(QMake_Makefile_VarDef)" + ExcludeValues="$(QtVarsWorkDir)"> + <Output TaskParameter="OutVars" ItemName="QtVar"/> + </GetVarsFromMakefile> + + <!--//// Extract Qt vars from .vcxproj file + // --> + <!--// Parse variable definition expressions for .vcxproj file --> + <ItemGroup Condition="'$(QtQMakeTemplate)' == 'vcapp'"> + <QMake_MSBuild_VarDef Include="$(QMake_MSBuild)"/> + <QMake_MSBuild_VarDef> + <Name>$([System.String]::Copy('%(Identity)').Split('=')[0].Trim())</Name> + <XPath>$([System.String]::Copy('%(Identity)').Split('=')[1].Trim())</XPath> + </QMake_MSBuild_VarDef> + </ItemGroup> + + <!--// Read variables from .vcxproj file --> + <GetVarsFromMSBuild + Condition="'$(QtQMakeTemplate)' == 'vcapp'" + Project="$(QtVarsWorkDir)\qtvars.vcxproj" + VarDefs="@(QMake_MSBuild_VarDef)" + ExcludeValues="$(QtVarsWorkDir)"> + <Output TaskParameter="OutVars" ItemName="QtVar"/> + </GetVarsFromMSBuild> + + <!--// Write variables as MSBuild properties to .props file --> + <PropertyGroup> + <QtMakefileVars Condition="'@(QtVar)' != ''"> + <![CDATA[@(QtVar->'<Qt_%(Identity)_>%(Value)</Qt_%(Identity)_>','%0D%0A')]]> + </QtMakefileVars> + </PropertyGroup> + <WriteLinesToFile Condition="'$(QtMakefileVars)' != ''" + File="$(QtVarsWorkPath)" Lines="$([System.String]::Copy($(QtMakefileVars)))"/> + + <!--// Write qmake properties as MSBuild properties to .props file --> + <PropertyGroup> + <QMakeProps Condition="'@(QtVar)' != ''"> + <![CDATA[@(QMakeProperty->'<QMake_%(Identity)_>%(Value)</QMake_%(Identity)_>','%0D%0A')]]> + </QMakeProps> + </PropertyGroup> + <WriteLinesToFile Condition="'$(QMakeProps)' != ''" + File="$(QtVarsWorkPath)" Lines="$([System.String]::Copy($(QMakeProps)))"/> + + <!--// Qt settings list for backup --> + <ItemGroup> + <QtInstall Include="$(QtInstall)"/> + <QtModules Include="$(QtModules)"/> + <QtPathBinaries Include="$(QtPathBinaries)"/> + <QtPathLibraryExecutables Include="$(QtPathLibraryExecutables)"/> + <QtHeaderSearchPath Include="$(QtHeaderSearchPath)"/> + <QtLibrarySearchPath Include="$(QtLibrarySearchPath)"/> + <QtVariables Include="$(QtVars)"/> + <QMakeCodeLines Include="$(QMakeCodeLines)"/> + <QtBuildConfig Include="$(QtBuildConfig)"/> + </ItemGroup> + + <!--// Write end of .props (XML) file + // * Add Qt tools output directories to include path + // * Save settings backup + // * Close property group tag + // * Create QtVar items to export Qt variables to the calling instance of MSBuild + // * Close project tag --> + <PropertyGroup> + <!-- +######## BEGIN generated XML (.props) ############################################################## +## --> + <QtVarsDataFileText> + <![CDATA[ + <Qt_INCLUDEPATH_ + >%24(Qt_INCLUDEPATH_)%3B@(QtIncludePath->Distinct(),'%3B')</Qt_INCLUDEPATH_> + <QtBkup_QtInstall + >@(QtInstall,'%3B')</QtBkup_QtInstall> + <QtBkup_QtModules + >@(QtModules,'%3B')</QtBkup_QtModules> + <QtBkup_QtPathBinaries + >@(QtPathBinaries,'%3B')</QtBkup_QtPathBinaries> + <QtBkup_QtPathLibraryExecutables + >@(QtPathLibraryExecutables,'%3B')</QtBkup_QtPathLibraryExecutables> + <QtBkup_QtHeaderSearchPath + >@(QtHeaderSearchPath,'%3B')</QtBkup_QtHeaderSearchPath> + <QtBkup_QtLibrarySearchPath + >@(QtLibrarySearchPath,'%3B')</QtBkup_QtLibrarySearchPath> + <QtBkup_QtVars + >@(QtVariables,'%3B')</QtBkup_QtVars> + <QtBkup_QMakeCodeLines + >@(QMakeCodeLines,'%3B')</QtBkup_QMakeCodeLines> + <QtBkup_QtBuildConfig + >@(QtBuildConfig,'%3B')</QtBkup_QtBuildConfig> + <QtVersion>$(QtVersion)</QtVersion> + <QtVersionMajor>$(QtVersionMajor)</QtVersionMajor> + <QtVersionMinor>$(QtVersionMinor)</QtVersionMinor> + <QtVersionPatch>$(QtVersionPatch)</QtVersionPatch> + </PropertyGroup> +</Project> + ]]> + </QtVarsDataFileText> + <!-- +## +######## END generated XML (.props) ################################################################ + --> + </PropertyGroup> + <WriteLinesToFile File="$(QtVarsWorkPath)" Lines="$(QtVarsDataFileText)"/> + + <!--// Create $(QtVarsOutputDir) --> + <MakeDir Directories="$(QtVarsOutputDir)"/> + + <!--// Copy generated .props to $(QtVarsOutputDir) --> + <Delete + Condition="'$(ErrorLevel)' == '0'" + Files="$(QtVarsFilePath)"/> + <Copy + Condition="'$(ErrorLevel)' == '0'" + SourceFiles="$(QtVarsWorkPath)" DestinationFiles="$(QtVarsFilePath)"/> + + <!--// Copy generated qtvars_plugin_import.cpp to $(QtVarsOutputDir) --> + <Delete + Condition="'$(ErrorLevel)' == '0'" + Files="$(QtVarsOutputDir)\qtvars_plugin_import.cpp"/> + <Copy + Condition="'$(ErrorLevel)' == '0' AND Exists('$(QtVarsWorkDir)\qtvars_plugin_import.cpp')" + SourceFiles="$(QtVarsWorkDir)\qtvars_plugin_import.cpp" + DestinationFiles="$(QtVarsOutputDir)\qtvars_plugin_import.cpp"/> + + <!--// In design-time, copy generated .props to randomly named file --> + <PropertyGroup> + <QtVarsDesignTimeNew + Condition="'$(ErrorLevel)' == '0' AND '$(QtVSToolsBuild)' == 'true'" + >$([System.IO.Path]::Combine('$(TEMP)','$([System.IO.Path]::GetRandomFileName()).designtime.props')) + </QtVarsDesignTimeNew> + </PropertyGroup> + <Copy + Condition="'$(ErrorLevel)' == '0' AND '$(QtVSToolsBuild)' == 'true'" + SourceFiles="$(QtVarsFilePath)" DestinationFiles="$(QtVarsDesignTimeNew)"/> + <WriteLinesToFile + Condition="'$(ErrorLevel)' == '0' AND '$(QtVSToolsBuild)' == 'true'" + File="$(QtVarsIndexPathDesignTime)" Overwrite="true" Lines="$(QtVarsDesignTimeNew)"/> + + <!--// Clean-up --> + <ItemGroup> + <QtModuleList Remove="@(QtModuleList)"/> + <QMakeCodeLine Remove="@(QMakeCodeLine)"/> + <QtIncludes Remove="@(QtIncludes)"/> + <QtLibs Remove="@(QtLibs)"/> + <QMakeError Remove="@(QMakeError)"/> + <QtInstall Remove="@(QtInstall)"/> + <QtModules Remove="@(QtModules)"/> + <QtPathBinaries Remove="@(QtPathBinaries)"/> + <QtPathLibraryExecutables Remove="@(QtPathLibraryExecutables)"/> + <QtHeaderSearchPath Remove="@(QtHeaderSearchPath)"/> + <QtLibrarySearchPath Remove="@(QtLibrarySearchPath)"/> + <QtVariables Remove="@(QtVariables)"/> + <QMakeCodeLines Remove="@(QMakeCodeLines)"/> + <QtBuildConfig Remove="@(QtBuildConfig)"/> + <QMake_Makefile_VarDef Remove="@(QMake_Makefile_VarDef)"/> + <QMake_MSBuild_VarDef Remove="@(QMake_MSBuild_VarDef)"/> + <QtVar Remove="@(QtVar)"/> + </ItemGroup> + <PropertyGroup> + <QtIncludes/> + <QtLibs/> + <QtVarsProFileConfig/> + <QtVarsProFileInput/> + <QtVarsDataFileText/> + <Cmd/> + <QtMakefileVars/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtVarsPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Force QtVars target to run when Qt settings have changed + // --> + <Target Name="QtVarsPrepare" BeforeTargets="QtVars" Condition="Exists('$(QtVarsFilePath)')"> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtInstall)' != '$(QtInstall)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtModules)' != '$(QtModules)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtPathBinaries)' != '$(QtPathBinaries)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtPathLibraryExecutables)' != '$(QtPathLibraryExecutables)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtHeaderSearchPath)' != '$(QtHeaderSearchPath)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtLibrarySearchPath)' != '$(QtLibrarySearchPath)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtVars)' != '$(QtVars)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QMakeCodeLines)' != '$(QMakeCodeLines)'" /> + <Delete Files="$(QtVarsFilePath)" Condition="Exists('$(QtVarsFilePath)') + AND '$(QtBkup_QtBuildConfig)' != '$(QtBuildConfig)'" /> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtVarsClean + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Delete all files generated by the QtVars target + // --> + <Target Name="QtVarsClean"> + <RemoveDir Directories="$(QtVarsOutputDir)"/> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtVarsDesignTime + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Force QtVars target to run (called when project properties change) + // --> + <Target Name="QtVarsDesignTime"> + <CallTarget Targets="QtVarsClean"/> + <CallTarget Targets="QtVars"/> + </Target> + +</Project> diff --git a/QtMSBuild/QtMsBuild/rcc/qt_import.props b/QtMSBuild/QtMsBuild/rcc/qt_import.props new file mode 100644 index 0000000..1285383 --- /dev/null +++ b/QtMSBuild/QtMsBuild/rcc/qt_import.props @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild import + // --> + <PropertyGroup> + <QtMsBuildProps> + $(QtMsBuildProps); + $(MSBuildThisFileDirectory)qtrcc.props + </QtMsBuildProps> + <QtMsBuildTargets> + $(QtMsBuildTargets); + $(MSBuildThisFileDirectory)qtrcc.targets + </QtMsBuildTargets> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/rcc/qtrcc.props b/QtMSBuild/QtMsBuild/rcc/qtrcc.props new file mode 100644 index 0000000..cfdb160 --- /dev/null +++ b/QtMSBuild/QtMsBuild/rcc/qtrcc.props @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild rcc property definitions +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeRcc)' != ''" + Project="$(QtMsBuildProps_BeforeRcc)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QtRcc item template + // --> + <ItemDefinitionGroup> + <QtRcc> + <ExecutionDescription>rcc %(Identity)</ExecutionDescription> + + <QTDIR Condition="'$(QtVsProjectSettings)' != 'true'" + >$(QTDIR)</QTDIR> + <QTDIR Condition="'$(QtVsProjectSettings)' == 'true'" + >$(QtInstallDir)</QTDIR> + + <InputFile>%(FullPath)</InputFile> + + <OutputFile Condition="'$(QtVsProjectSettings)' != 'true'" + >$(ProjectDir)GeneratedFiles\$(Configuration)\qrc_%(Filename).cpp</OutputFile> + + <QtRccDir Condition="'$(QtVsProjectSettings)' == 'true'" + >$(IntDir)rcc\</QtRccDir> + <QtRccFileName Condition="'$(QtVsProjectSettings)' == 'true'" + >qrc_%(Filename).cpp</QtRccFileName> + + <InitFuncName>%(Filename)</InitFuncName> + <Compression>default</Compression> + <TwoPass>false</TwoPass> + <DynamicSource>output</DynamicSource> + <ParallelProcess>true</ParallelProcess> + <CommandLineTemplate>[AllOptions] [AdditionalOptions]</CommandLineTemplate> + <Outputs>%(OutputFile)</Outputs> + </QtRcc> + </ItemDefinitionGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterRcc)' != ''" + Project="$(QtMsBuildProps_AfterRcc)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/rcc/qtrcc.targets b/QtMSBuild/QtMsBuild/rcc/qtrcc.targets new file mode 100644 index 0000000..a4aa8c1 --- /dev/null +++ b/QtMSBuild/QtMsBuild/rcc/qtrcc.targets @@ -0,0 +1,595 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// Definitions specific to rcc +/////////////////////////////////////////////////////////////////////////////////////////////////// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeRcc)' != ''" + Project="$(QtMsBuildTargets_BeforeRcc)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // --> + <PropertyGroup> + <QtBuildTargets>QtRcc;$(QtBuildTargets)</QtBuildTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Setup item type and property page + // --> + <Choose> + <When Condition="'$(QtVsProjectSettings)' == 'true' AND '$(QtVsProjectClProperties)' == 'true'"> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtrcc_v3.xml" /> + </ItemGroup> + </When> + <Otherwise> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtrcc.xml" /> + </ItemGroup> + </Otherwise> + </Choose> + <ItemGroup> + <AvailableItemName Include="QtRcc"> + <Targets>Qt;_ClCompile</Targets> + </AvailableItemName> + </ItemGroup> + <PropertyGroup> + <QtRccRuleName>QtRule40_Rcc</QtRccRuleName> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRccInit + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Initialize default metadata + // --> + <Target Name="QtRccInit"> + <!--// Initialize %(OutputFile) --> + <ItemGroup Condition="'$(QtVsProjectSettings)' == 'true' AND '@(QtRcc)' != ''"> + <QtRccAux Include="@(QtRcc)"> + <OutputFile + >$([System.IO.Path]::Combine('%(QtRcc.QtRccDir)','%(QtRcc.QtRccFileName)'))</OutputFile> + </QtRccAux> + <QtRcc Remove="@(QtRcc)"/> + <QtRcc Include="@(QtRccAux)"/> + <QtRccAux Remove="@(QtRccAux)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRccSetDependencies + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Add resource files as dependencies of the QRC file + // --> + <Target Name="QtRccSetDependencies" DependsOnTargets="QtRccInit" + Inputs="%(QtRcc.Identity)" Outputs="@(QtRcc->'####### Don't skip this target #######')"> + <ItemGroup> + <selected_files Include="$(SelectedFiles)"/> + </ItemGroup> + + <!--// Parse QRC --> + <ListQrc QrcFilePath="%(QtRcc.FullPath)"> + <Output TaskParameter="Result" ItemName="res_file"/> + </ListQrc> + + <!--// Add dependencies --> + <ItemGroup Condition="'@(QtRcc)' != '' AND '@(res_file)' != ''"> + <QtRcc> + <ResourceFiles>@(res_file)</ResourceFiles> + <AdditionalDependencies + >%(QtRcc.AdditionalDependencies);@(res_file->'%(FullPath)')</AdditionalDependencies> + </QtRcc> + </ItemGroup> + + <!--// Clean-up --> + <PropertyGroup> + <QtRccExeQuoted/> + <QtRccQuoted/> + <RccListQuoted/> + </PropertyGroup> + <ItemGroup> + <res_file Remove="@(res_file)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRccPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Prepare items for processing + // --> + <Target Name="QtRccPrepare" DependsOnTargets="QtRccSetDependencies" + Inputs="%(QtRcc.Identity)" Outputs="@(QtRcc->'####### Don't skip this target #######')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtRccPrepare @(QtRcc)"/> + + <PropertyGroup> + <selected_files>[@(selected_files->'%(Identity)','][')]</selected_files> + <file>[@(QtRcc->'%(Identity)')]</file> + <output_file>@(QtRcc->'%(OutputFile)')</output_file> + <is_selected Condition="$(selected_files.Contains('$(file)'))">true</is_selected> + <is_selected Condition="!$(selected_files.Contains('$(file)'))">false</is_selected> + </PropertyGroup> + + <!--// Delete output file to force build of source if it was manually selected to build + // (e.g. by the 'Compile' option in the context menu for the file) --> + <Delete Files="$(output_file)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' == 'true'" /> + + <!--// If a source was manually selected to build, remove all other sources --> + <ItemGroup Condition="'@(selected_files)' != ''"> + <QtRcc Remove="@(QtRcc)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' != 'true'" /> + </ItemGroup> + + <!--// Remove sources excluded from build --> + <ItemGroup> + <QtRcc Remove="@(QtRcc)" + Condition="'$(SelectedFiles)' == '' AND '%(QtRcc.ExcludedFromBuild)' == 'true'"/> + </ItemGroup> + + <!--// Clean-up --> + <PropertyGroup> + <selected_files/> + <file/> + <output_file/> + <is_selected/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRccSetModified + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Set InputModified flag for modified files (i.e. Inputs newer than Outputs) + // --> + <Target Name="QtRccSetModified" DependsOnTargets="QtRccPrepare" + Condition="'@(QtRcc)' != ''" + Inputs="%(QtRcc.FullPath);%(QtRcc.AdditionalDependencies)" Outputs="@(QtRcc->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtRccSetModified @(QtRcc)" /> + + <CreateProperty Value="true"> + <!-- // Using ValueSetByTask ensures $(input_changed) is only set to 'true' when the target + // is actually executed and not when MSBuild is figuring out which targets to run --> + <Output TaskParameter="ValueSetByTask" PropertyName="input_changed" /> + </CreateProperty> + <ItemGroup> + <QtRcc> + <InputChanged>$(input_changed)</InputChanged> + </QtRcc> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRccOverrideCpp + ///////////////////////////////////////////////////////////////////////////////////////////////// + // + // --> + <Import Project="qtrcc_cl.targets"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRcc + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Process each QRC file and produce the corresponding QtWork items + // --> + <PropertyGroup> + <QtRccDependsOn> + QtPrepare; + QtRccPrepare; + QtRccSetModified; + QtRccOverrideCpp; + $(QtRccDependsOn) + </QtRccDependsOn> + </PropertyGroup> + <Target Name="QtRcc" + DependsOnTargets="$(QtRccDependsOn)" + BeforeTargets="$(QtRccBeforeTargets)" AfterTargets="$(QtRccAfterTargets)" + Condition="'@(QtRcc)' != ''" + Inputs="%(QtRcc.FullPath);%(QtRcc.AdditionalDependencies);$(MSBuildProjectFile)" + Outputs="@(QtRcc->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="## QtRcc @(QtRcc)" /> + + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="dependencies_changed" /> + </CreateProperty> + + <PropertyGroup> + <two_pass Condition="'%(QtRcc.TwoPass)' == 'true'">true</two_pass> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Convert string lists in source item properties to lists of items + // --> + <Flatten Items="@(QtRcc)" + Metadata="InputFile; + OutputFile; + TempFile; + InitFuncName; + Root; + Compression; + NoCompression; + CompressThreshold; + BinaryOutput; + PassNumber; + NoNamespace; + Verbose; + List; + Project; + FormatVersion"> + <Output + TaskParameter="Result" ItemName="LocalOptions" /> + </Flatten> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Translate local paths to build host paths + // --> + <HostTranslatePaths + Items="@(LocalOptions)" + Names="InputFile; + OutputFile; + TempFile; + Root"> + <Output + TaskParameter="Result" ItemName="options" /> + </HostTranslatePaths> + + <ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Remove quotes from all paths + // Escape trailing back-slash in paths + // Add quotes to paths containing spaces + // --> + <options> + <Value Condition="'%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'TempFile' OR '%(Name)' == 'Root'" + >$([System.String]::Copy('%(Value)').Replace('"', ''))</Value> + </options> + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'TempFile' OR '%(Name)' == 'Root') + AND $([System.String]::Copy('%(Value)').Contains(' ')) + AND $([System.String]::Copy('%(Value)').EndsWith('\'))" + >%(Value)\</Value> + </options> + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'TempFile' OR '%(Name)' == 'Root') + AND $([System.String]::Copy('%(Value)').Contains(' '))" + >"%(Value)"</Value> + </options> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate tool command line arguments + // --> + <!--// inputs Input files (*.qrc) --> + <options> + <Value Condition="'%(Name)' == 'InputFile'">%(Value)</Value> + </options> + + <!--// -o, -output <file> Write output to <file> --> + <options> + <Value Condition="'%(Name)' == 'OutputFile' AND '$(two_pass)' != 'true'">-o %(Value)</Value> + </options> + <options> + <Value Condition="'%(Name)' == 'OutputFile' AND '$(two_pass)' == 'true'"></Value> + </options> + + <!--// -t, -temp <file> Use temporary <file> for big resources --> + <options> + <Value Condition="'%(Name)' == 'TempFile'">--temp %(Value)</Value> + </options> + + <!--// -name <name> Create an external initialization function with <name> --> + <options> + <Value Condition="'%(Name)' == 'InitFuncName'">--name %(Value)</Value> + </options> + + <!--// -root <path> Prefix resource access path with root path --> + <options> + <Value Condition="'%(Name)' == 'Root'">--root %(Value)</Value> + </options> + + <!--// -compress <level> Compress input files by <level> --> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level1'">--compress 1</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level2'">--compress 2</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level3'">--compress 3</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level4'">--compress 4</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level5'">--compress 5</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level6'">--compress 6</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level7'">--compress 7</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level8'">--compress 8</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' AND '%(Value)' == 'level9'">--compress 9</Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'Compression' + AND !$([System.String]::Copy('%(Value)').StartsWith('--compress'))"></Value> + </options> + + <!--// -no-compress Disable all compression --> + <options> + <Value + Condition="'%(Name)' == 'NoCompression' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value + Condition="'%(Name)' == 'NoCompression' AND '%(Value)' == 'true'">--no-compress</Value> + </options> + + <!--// -threshold <level> Threshold to consider compressing files --> + <options> + <Value Condition="'%(Name)' == 'CompressThreshold'">--threshold %(Value)</Value> + </options> + + <!--// -binary Output a binary file for use as a dynamic source --> + <options> + <Value Condition="'%(Name)' == 'BinaryOutput' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'BinaryOutput' AND '%(Value)' == 'true'">--binary</Value> + </options> + + <!--// -pass <number> Pass number for big resources --> + <options> + <Value Condition="'%(Name)' == 'PassNumber'">--pass %(Value)</Value> + </options> + + <!--// -namespace Turn off namespace macros --> + <options> + <Value Condition="'%(Name)' == 'NoNamespace' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'NoNamespace' AND '%(Value)' == 'true'">--namespace</Value> + </options> + + <!--// -verbose Enable verbose mode --> + <options> + <Value Condition="'%(Name)' == 'Verbose' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'Verbose' AND '%(Value)' == 'true'">--verbose</Value> + </options> + + <!--// -list Only list .qrc file entries, do not generate code --> + <options> + <Value Condition="'%(Name)' == 'List' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'List' AND '%(Value)' == 'true'">--list</Value> + </options> + + <!--// -project Output a resource file containing all files from the + // current directory --> + <options> + <Value Condition="'%(Name)' == 'Project' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'Project' AND '%(Value)' == 'true'">--project</Value> + </options> + + <!--// -format-version <number> The RCC format version to write --> + <options> + <Value Condition="'%(Name)' == 'FormatVersion'">--format-version %(Value)</Value> + </options> + </ItemGroup> + <PropertyGroup> + <options>@(options->'%(Value)', ' ')</options> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Aux properties + // --> + <PropertyGroup> + <!--// Force modified flag if source was manually selected to build --> + <input_changed Condition="'$(SelectedFiles)' == ''" + >%(QtRcc.InputChanged)</input_changed> + <input_changed Condition="'$(SelectedFiles)' != ''" + >true</input_changed> + + <!--// Run work in parallel processes --> + <run_parallel Condition="'@(QtRcc)' != '' + AND '%(QtRcc.ParallelProcess)' == 'true' + AND '$(SelectedFiles)' == ''" + >true</run_parallel> + + <!--// Run work in single process --> + <run_single Condition="'@(QtRcc)' != '' + AND ('%(QtRcc.ParallelProcess)' != 'true' + OR '$(SelectedFiles)' != '')" + >true</run_single> + + <!--// Get relative path to output --> + <output_relative +>$([MSBuild]::MakeRelative($(ProjectDir), %(QtRcc.OutputFile)).TrimStart('\'))</output_relative> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Create work item + // --> + <ItemGroup Condition="'$(run_parallel)' == 'true' OR '$(run_single)' == 'true'"> + <QtWork Include="@(QtRcc)"> + <WorkType>rcc</WorkType> + <ToolPath Condition="'$(QtVsProjectSettings)' == 'true'">$(QtToolsPath)/rcc</ToolPath> + <ToolPath Condition="'$(QtVsProjectSettings)' != 'true'">%(QtRcc.QTDIR)\bin\rcc.exe</ToolPath> + <Options>$(options)</Options> + <Message>%(QtRcc.ExecutionDescription)</Message> + <DependenciesChanged>$(dependencies_changed)</DependenciesChanged> + <InputChanged>$(input_changed)</InputChanged> + <ParallelBuild Condition="'$(run_parallel)' == 'true'">true</ParallelBuild> + <ParallelBuild Condition="'$(run_single)' == 'true'">false</ParallelBuild> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////// + // Two-pass ("resources_big"): pass 1 --> + <Message Condition="'%(QtRcc.TwoPass)' == 'true'">%(QtRcc.ExecutionDescription) [pass 1]</Message> + <RccPass Condition="'%(QtRcc.TwoPass)' == 'true'">1</RccPass> + <RccOptions Condition="'%(QtRcc.TwoPass)' == 'true'">$(options)</RccOptions> + <Options Condition="'%(QtRcc.TwoPass)' == 'true'" + >$(options) -pass 1 -o "%(OutputFile)"</Options> + <Optimization Condition="'%(QtRcc.TwoPass)' == 'true'">Disabled</Optimization> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////// + // C++ dynamic source --> + <ClCompile Condition="'%(QtRcc.DynamicSource)' == 'output'">$(output_relative)</ClCompile> + </QtWork> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <options/> + <dependencies_changed/> + <input_changed/> + <run_parallel/> + <run_single/> + <output_relative/> + <two_pass/> + </PropertyGroup> + <ItemGroup> + <LocalOptions Remove="@(LocalOptions)"/> + <options Remove="@(options)"/> + <selected_files Remove="@(selected_files)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRccPass2 + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Two-pass ("resources_big"): pass 2 + // --> + <Target Name="QtRccPass2" + DependsOnTargets="QtRccPass2_GetObjs;QtRccPass2_Exec"/> + + <Target Name="QtRccPass2_GetObjs" + DependsOnTargets="ComputeCLOutputs"> + <ItemGroup> + <QtRccObj Include="@(Obj->WithMetadataValue('RccPass', '1'))"> + <RccPass>2</RccPass> + </QtRccObj> + </ItemGroup> + </Target> + + <Target Name="QtRccPass2_Exec" + DependsOnTargets="QtRccPass2_GetObjs" + Condition="'@(QtRccObj)' != ''"> + <Move + SourceFiles="%(QtRccObj.Identity)" + DestinationFiles="%(QtRccObj.Identity)_TMP"/> + <Exec + Command="@(QtRccObj->'ECHO %(ExecutionDescription) [pass 2] & %(ToolPath) %(RccOptions) -pass 2 -temp "%(Identity)_TMP" -o "%(Identity)"')" + IgnoreExitCode="true"/> + <ItemGroup> + <Obj Remove="@(QtRccObj->'%(Identity)')"/> + <Obj Include="@(QtRccObj)"/> + <QtRccObj Remove="@(QtRccObj)"/> + </ItemGroup> + </Target> + + <Target Name="QtRccPass2_Link" + DependsOnTargets="QtRccPass2"/> + + <Target Name="QtRccPass2_Lib" + DependsOnTargets="QtRccPass2"/> + + <PropertyGroup> + <ComputeLinkInputsTargets> + $(ComputeLinkInputsTargets); + ;QtRccPass2_Link; + </ComputeLinkInputsTargets> + <ComputeLibInputsTargets> + $(ComputeLibInputsTargets); + ;QtRccPass2_Lib; + </ComputeLibInputsTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterRcc)' != ''" + Project="$(QtMsBuildTargets_AfterRcc)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/rcc/qtrcc.xml b/QtMSBuild/QtMsBuild/rcc/qtrcc.xml new file mode 100644 index 0000000..0b02131 --- /dev/null +++ b/QtMSBuild/QtMsBuild/rcc/qtrcc.xml @@ -0,0 +1,384 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Defines the fields included in the rcc property page, as well as any +// other metadata associated to QtRcc items +// --> +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule40_Rcc" + PageTemplate="tool" + DisplayName="Qt Resource Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRcc" /> + </Rule.DataSource> + <Rule.Categories> + <Category Name="General" DisplayName="rcc"/> + <Category Name="QMLCache" DisplayName="Qt Quick Compiler"/> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRcc" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False"/> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Description="Path to installation of Qt."/> + <StringProperty + Name="InputFile" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Input File" + Description="Specifies the full path of the input file. (<file>)" + Switch=""[value]"" /> + <StringProperty + Name="OutputFile" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Output File" + Description="Specifies the full path of the output file. (-o <file>)" + Switch="-o "[value]"" /> + <StringProperty + Name="TempFile" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Temporary File" + Description="Use temporary <file> for big resources. (--temp <file>)" + Switch="--temp "[value]"" /> + <StringProperty + Name="InitFuncName" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Initialization Function Name" + Description= +"Create an external initialization function with <name>. (--name <name>)" + Switch="--name [value]" /> + <StringProperty + Name="Root" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Root" + Description="Prefix resource access path with root <path>. (--root <path>)" + Switch="--root "[value]"" /> + <EnumProperty + Name="Compression" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Compression" + Description="Compress input files to the given compression level."> + <EnumValue + Name="default" + Switch="" + DisplayName="Default (use zlib's default compression level)" + Description= +"The default value for level is -1, which means use zlib's default compression level."/> + <EnumValue + Name="level1" + Switch="--compress 1" + DisplayName="Level 1 (least compression / fastest)" + Description="Compression level 1. (--compress 1)"/> + <EnumValue + Name="level2" + Switch="--compress 2" + DisplayName="Level 2" + Description="Compression level 2. (--compress 2)"/> + <EnumValue + Name="level3" + Switch="--compress 3" + DisplayName="Level 3" + Description="Compression level 3. (--compress 3)"/> + <EnumValue + Name="level4" + Switch="--compress 4" + DisplayName="Level 4" + Description="Compression level 4. (--compress 4)"/> + <EnumValue + Name="level5" + Switch="--compress 5" + DisplayName="Level 5" + Description="Compression level 5. (--compress 5)"/> + <EnumValue + Name="level6" + Switch="--compress 6" + DisplayName="Level 6" + Description="Compression level 6. (--compress 6)"/> + <EnumValue + Name="level7" + Switch="--compress 7" + DisplayName="Level 7" + Description="Compression level 7. (--compress 7)"/> + <EnumValue + Name="level8" + Switch="--compress 8" + DisplayName="Level 8" + Description="Compression level 8. (--compress 8)"/> + <EnumValue + Name="level9" + Switch="--compress 9" + DisplayName="Level 9 (most compression / slowest)" + Description="Compression level 9. (--compress 9)"/> + </EnumProperty> + <BoolProperty + Name="NoCompression" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="No Compression" + Description="Disable all compression. (--no-compress)" + Switch="--no-compress"/> + <StringProperty + Name="CompressThreshold" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Compression Threshold" + Description= +"Threshold <level> to consider compressing files. (--threshold <level>)" + Switch="--threshold [value]" /> + <BoolProperty + Name="BinaryOutput" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Binary Output" + Description="Output a binary file for use as a dynamic resource. (--binary)" + Switch="--binary"/> + <StringProperty + Name="PassNumber" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Pass Number" + Description="Pass <number> for big resources. (--pass <number>)" + Switch="--pass [value]" /> + <BoolProperty + Name="NoNamespace" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Disable Namespace Macros" + Description="Turn off namespace macros. (--namespace)" + Switch="--namespace"/> + <BoolProperty + Name="Verbose" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Verbose" + Description="Enable verbose mode. (--verbose)" + Switch="--verbose"/> + <BoolProperty + Name="List" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="List" + Description="Only list .qrc file entries, do not generate code. (--list)" + Switch="--list"/> + <BoolProperty + Name="Project" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Project" + Description= +"Output a resource file containing all files from the current directory. (--project)" + Switch="--project"/> + <StringProperty + Name="FormatVersion" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Format Version" + Description="The RCC format version to write. (--format-version <number>)" + Switch="--format-version [value]" /> + <BoolProperty + Name="TwoPass" + DisplayName="Two-Pass Mode" + Description="1st pass: generate source file; 2nd pass: overwrite resouce data in object file"/> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtRccBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRccBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtRccAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRccAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <EnumProperty + Name="DynamicSource" + DisplayName="Dynamic C++ Source" + Description="Add file to list of C++ sources during build."> + <EnumValue + Name="output" + DisplayName="Output File" + Description="Use output as dynamic C++ source."/> + <EnumValue + Name="false" + DisplayName="Disable" + Description="Disable dynamic source."/> + </EnumProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + <BoolProperty + Name="QmlCacheGenerate" + Category="QMLCache" + DisplayName="Enable Qt Quick Compiler" + Description="Enable or disable ahead-of-time compilation using the Qt Quick Compiler." /> + <!-- + // QML cache generation --> + <StringProperty Name="QMLCacheHeader_Generation" Category="QMLCache" ReadOnly="true" + DisplayName="########## QML Cache Generation ##############################"/> + <StringProperty + Name="QmlCacheGenMessage" + Category="QMLCache" + DisplayName="Execution Description of QML cache generation" + IncludeInCommandLine="False"/> + <StringProperty + Name="QmlCacheOutputFile" + Category="QMLCache" + DisplayName="Cache File" + Description= +"Specifies the full path of the output cache files. Use %<metadata> to reference QML file +metadata (e.g. %<Filename>)." /> + <!-- + // QRC filtering --> + <StringProperty Name="QMLCacheHeader_Filtering" Category="QMLCache" ReadOnly="true" + DisplayName="########## QRC Filtering ##############################"/> + <StringProperty + Name="QmlCacheFilterMessage" + Category="QMLCache" + DisplayName="Execution Description of QRC filtering" + IncludeInCommandLine="False"/> + <StringProperty + Name="QmlCacheFilteredFile" + Category="QMLCache" + DisplayName="Filtered Resource File" + Description= +"Specifies the full path of the resource file copy with QML files filtered out." /> + <StringProperty + Name="QmlCacheFilteredOutputFile" + Category="QMLCache" + DisplayName="Filtered Resource File Output" + Description= +"Specifies the full path of the output file for the processing of the filtered resource file." /> + <StringProperty + Name="QmlCacheFilteredInitFuncName" + Category="QMLCache" + DisplayName="Filtered Resource File Initialization Function" + Description= +"Create an external initialization function with <name>. (--name <name>)" /> + <!-- + // QML cache loader generation --> + <StringProperty Name="QMLCacheHeader_Loader" Category="QMLCache" ReadOnly="true" + DisplayName="########## QML Cache Loader ##############################"/> + <StringProperty + Name="QmlCacheLoaderMessage" + Category="QMLCache" + DisplayName="Execution Description of QML cache loader generation" + IncludeInCommandLine="False"/> + <StringProperty + Name="QmlCacheLoaderFile" + Category="QMLCache" + DisplayName="Cache Loader Source File" + Description="Specifies the full path of the QML cache loader source file." /> + </Rule> + <ItemType + Name="QtRcc" + DisplayName="Qt Resource Compiler (rcc)" /> + <FileExtension + Name="*.qrc" + ContentType="QtRcc" /> + <ContentType + Name="QtRcc" + DisplayName="Qt Resource Compiler (rcc)" + ItemType="QtRcc" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/rcc/qtrcc_cl.targets_TT b/QtMSBuild/QtMsBuild/rcc/qtrcc_cl.targets_TT new file mode 100644 index 0000000..c32ef15 --- /dev/null +++ b/QtMSBuild/QtMsBuild/rcc/qtrcc_cl.targets_TT @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="targets" #> +<#@include file="$(SolutionDir)\common.tt" #> +<#@assembly Name="System.Xml" #> +<#@assembly Name="System.Xml.Linq" #> +<#@import namespace="System.Collections.Generic" #> +<#@import namespace="System.Linq" #> +<#@import namespace="System.Text" #> +<#@import namespace="System.Xml" #> +<#@import namespace="System.Xml.Linq" #> +<# + var pathRuleCl = Path.Combine(VC_TARGETS_PATH, "1033", "cl.xml"); + var xmlRule = XmlLoad(File.ReadAllText(pathRuleCl, Encoding.UTF8)); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Target Name="QtRccOverrideCpp" DependsOnTargets="QtRccPrepare;QtGetDefaultClCompile" + Inputs="%(QtRcc.Identity)" Outputs="@(QtRcc->'####### Don't skip this target #######')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtRccOverrideCpp @(QtRcc)" /> + + <PropertyGroup Condition="'$(QtVsProjectClProperties)' == 'true'"> + <override_cl_compile>%(QtRcc.OverrideClCompile)</override_cl_compile> + </PropertyGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'extend'"> + <QtRcc> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().Equals("StringListProperty")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .Select(x => new XElement((string)x.Attribute("Name"), + string.Format( + "@(ClCompile->WithMetadataValue('Identity','DefaultClCompile')->'%({0})');%(QtRcc.{0})", + (string)x.Attribute("Name"))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <AdditionalCppOptions + Condition="'%(QtRcc.AdditionalCppOptions)' != ''" + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalOptions)') %(QtRcc.AdditionalCppOptions)</AdditionalCppOptions> + <AdditionalCppOptions + Condition="'%(QtRcc.AdditionalCppOptions)' == ''" + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalOptions)')</AdditionalCppOptions> + </QtRcc> + </ItemGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'false'"> + <QtRcc> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().EndsWith("Property")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .Select(x => new XElement((string)x.Attribute("Name"), + new XAttribute("Condition", + string.Format("'%(QtRcc.{0})' != ''", (string)x.Attribute("Name"))), + string.Format( + "@(ClCompile->WithMetadataValue('Identity','DefaultClCompile')->'%({0})')", + (string)x.Attribute("Name"))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </QtRcc> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <override_cl_compile/> + </PropertyGroup> + </Target> +</Project> diff --git a/QtMSBuild/QtMsBuild/rcc/qtrcc_v3.xml_TT b/QtMSBuild/QtMsBuild/rcc/qtrcc_v3.xml_TT new file mode 100644 index 0000000..18ad393 --- /dev/null +++ b/QtMSBuild/QtMsBuild/rcc/qtrcc_v3.xml_TT @@ -0,0 +1,447 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="xml" #> +<#@include file="$(SolutionDir)\common.tt" #> +<#@assembly Name="System.Xml" #> +<#@assembly Name="System.Xml.Linq" #> +<#@import namespace="System.Collections.Generic" #> +<#@import namespace="System.Linq" #> +<#@import namespace="System.Text" #> +<#@import namespace="System.Xml" #> +<#@import namespace="System.Xml.Linq" #> +<# + var pathRuleCl = Path.Combine(VC_TARGETS_PATH, "1033", "cl.xml"); + var xmlRule = XmlLoad(File.ReadAllText(pathRuleCl, Encoding.UTF8)); + var categoryNames = xmlRule + .Elements("Rule.Categories") + .Elements("Category") + .Where(x => x.Attribute("Subtype") == null) + .ToDictionary( + x => (string)x.Attribute("Name"), + x => (string)x.Attribute("DisplayName")); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule40_Rcc" + PageTemplate="tool" + DisplayName="Qt Resource Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRcc" /> + </Rule.DataSource> + <Rule.Categories> + <Category Name="General" DisplayName="rcc"/> + <Category Name="QMLCache" DisplayName="Qt Quick Compiler"/> + <Category Name="RccCpp" DisplayName="Override C/C++"/> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRcc" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False"/> + <StringProperty + Name="QtRccDir" + DisplayName="Output Directory" + Description="Specifies the path of the generated rcc output directory."/> + <StringProperty + Name="QtRccFileName" + DisplayName="Output File Name" + Description="Specifies the name of the generated rcc output file."/> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Visible="false"/> + <StringProperty + Name="InputFile" + DisplayName="Input File" + Visible="false"/> + <StringProperty + Name="OutputFile" + DisplayName="Output File" + Visible="false"/> + <StringProperty + Name="TempFile" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Temporary File" + Description="Use temporary <file> for big resources. (--temp <file>)" + Switch="--temp "[value]"" /> + <StringProperty + Name="InitFuncName" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Initialization Function Name" + Description= +"Create an external initialization function with <name>. (--name <name>)" + Switch="--name [value]" /> + <StringProperty + Name="Root" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Root" + Description="Prefix resource access path with root <path>. (--root <path>)" + Switch="--root "[value]"" /> + <EnumProperty + Name="Compression" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Compression" + Description="Compress input files to the given compression level."> + <EnumValue + Name="default" + Switch="" + DisplayName="Default (use zlib's default compression level)" + Description= +"The default value for level is -1, which means use zlib's default compression level."/> + <EnumValue + Name="level1" + Switch="--compress 1" + DisplayName="Level 1 (least compression / fastest)" + Description="Compression level 1. (--compress 1)"/> + <EnumValue + Name="level2" + Switch="--compress 2" + DisplayName="Level 2" + Description="Compression level 2. (--compress 2)"/> + <EnumValue + Name="level3" + Switch="--compress 3" + DisplayName="Level 3" + Description="Compression level 3. (--compress 3)"/> + <EnumValue + Name="level4" + Switch="--compress 4" + DisplayName="Level 4" + Description="Compression level 4. (--compress 4)"/> + <EnumValue + Name="level5" + Switch="--compress 5" + DisplayName="Level 5" + Description="Compression level 5. (--compress 5)"/> + <EnumValue + Name="level6" + Switch="--compress 6" + DisplayName="Level 6" + Description="Compression level 6. (--compress 6)"/> + <EnumValue + Name="level7" + Switch="--compress 7" + DisplayName="Level 7" + Description="Compression level 7. (--compress 7)"/> + <EnumValue + Name="level8" + Switch="--compress 8" + DisplayName="Level 8" + Description="Compression level 8. (--compress 8)"/> + <EnumValue + Name="level9" + Switch="--compress 9" + DisplayName="Level 9 (most compression / slowest)" + Description="Compression level 9. (--compress 9)"/> + </EnumProperty> + <BoolProperty + Name="NoCompression" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="No Compression" + Description="Disable all compression. (--no-compress)" + Switch="--no-compress"/> + <StringProperty + Name="CompressThreshold" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Compression Threshold" + Description= +"Threshold <level> to consider compressing files. (--threshold <level>)" + Switch="--threshold [value]" /> + <BoolProperty + Name="BinaryOutput" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Binary Output" + Description="Output a binary file for use as a dynamic resource. (--binary)" + Switch="--binary"/> + <StringProperty + Name="PassNumber" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Pass Number" + Description="Pass <number> for big resources. (--pass <number>)" + Switch="--pass [value]" /> + <BoolProperty + Name="NoNamespace" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Disable Namespace Macros" + Description="Turn off namespace macros. (--namespace)" + Switch="--namespace"/> + <BoolProperty + Name="Verbose" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Verbose" + Description="Enable verbose mode. (--verbose)" + Switch="--verbose"/> + <BoolProperty + Name="List" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="List" + Description="Only list .qrc file entries, do not generate code. (--list)" + Switch="--list"/> + <BoolProperty + Name="Project" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Project" + Description= +"Output a resource file containing all files from the current directory. (--project)" + Switch="--project"/> + <StringProperty + Name="FormatVersion" + HelpUrl="http://doc.qt.io/qt-5/rcc.html" + DisplayName="Format Version" + Description="The RCC format version to write. (--format-version <number>)" + Switch="--format-version [value]" /> + <BoolProperty + Name="TwoPass" + DisplayName="Two-Pass Mode" + Description="1st pass: generate source file; 2nd pass: overwrite resouce data in object file"/> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtRccBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRccBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtRccAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRccAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <EnumProperty + Name="DynamicSource" + DisplayName="Dynamic C++ Source" + Description="Add file to list of C++ sources during build."> + <EnumValue + Name="output" + DisplayName="Output File" + Description="Use output as dynamic C++ source."/> + <EnumValue + Name="false" + DisplayName="Disable" + Description="Disable dynamic source."/> + </EnumProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + <BoolProperty + Name="QmlCacheGenerate" + Category="QMLCache" + DisplayName="Enable Qt Quick Compiler" + Description="Enable or disable ahead-of-time compilation using the Qt Quick Compiler." /> + <!-- + // QML cache generation --> + <StringProperty Name="QMLCacheHeader_Generation" Category="QMLCache" ReadOnly="true" + DisplayName="########## QML Cache Generation ##############################"/> + <StringProperty + Name="QmlCacheGenMessage" + Category="QMLCache" + DisplayName="Execution Description of QML cache generation" + IncludeInCommandLine="False"/> + <StringProperty + Name="QmlCacheOutputFile" + Category="QMLCache" + DisplayName="Cache File" + Description= +"Specifies the full path of the output cache files. Use %<metadata> to reference QML file +metadata (e.g. %<Filename>)." /> + <!-- + // QRC filtering --> + <StringProperty Name="QMLCacheHeader_Filtering" Category="QMLCache" ReadOnly="true" + DisplayName="########## QRC Filtering ##############################"/> + <StringProperty + Name="QmlCacheFilterMessage" + Category="QMLCache" + DisplayName="Execution Description of QRC filtering" + IncludeInCommandLine="False"/> + <StringProperty + Name="QmlCacheFilteredFile" + Category="QMLCache" + DisplayName="Filtered Resource File" + Description= +"Specifies the full path of the resource file copy with QML files filtered out." /> + <StringProperty + Name="QmlCacheFilteredOutputFile" + Category="QMLCache" + DisplayName="Filtered Resource File Output" + Description= +"Specifies the full path of the output file for the processing of the filtered resource file." /> + <StringProperty + Name="QmlCacheFilteredInitFuncName" + Category="QMLCache" + DisplayName="Filtered Resource File Initialization Function" + Description= +"Create an external initialization function with <name>. (--name <name>)" /> + <!-- + // QML cache loader generation --> + <StringProperty Name="QMLCacheHeader_Loader" Category="QMLCache" ReadOnly="true" + DisplayName="########## QML Cache Loader ##############################"/> + <StringProperty + Name="QmlCacheLoaderMessage" + Category="QMLCache" + DisplayName="Execution Description of QML cache loader generation" + IncludeInCommandLine="False"/> + <StringProperty + Name="QmlCacheLoaderFile" + Category="QMLCache" + DisplayName="Cache Loader Source File" + Description="Specifies the full path of the QML cache loader source file." /> + <EnumProperty + Name="OverrideClCompile" + Category="RccCpp" + DisplayName="Override C/C++ Properties" + Description="Define how C/C++ properties are redefined for rcc"> + <EnumValue Name="extend" DisplayName= +"Extend (append override definitions to C/C++ list properties; replace other properties)"/> + <EnumValue Name="replace" DisplayName= +"Replace (use override definitions instead of C/C++ properties)"/> + <EnumValue Name="false" DisplayName= +"Disabled (use C/C++ properties; ignore override definitions)"/> + </EnumProperty> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().EndsWith("Property")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .GroupBy(x => (string)x.Attribute("Category")) + .SelectMany(y => y.Select(x => + { + x = new XElement(x); + x.SetAttributeValue("Category", "RccCpp"); + x.SetAttributeValue("Description", "(overrides C/C++ property) " + + (string)x.Attribute("Description")); + return x; + }) + .Prepend(new XElement("StringProperty", + new XAttribute("Name", "QtCppHeader_" + y.Key.Replace(' ', '_')), + new XAttribute("Category", "RccCpp"), + new XAttribute("ReadOnly", "true"), + new XAttribute("DisplayName", string.Format("{0} {1} {2}", + new string('#', 10), categoryNames[y.Key], new string('#', 30))))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <StringProperty + Name="AdditionalCppOptions" + DisplayName="Additional Options" + Description="Additional Options." + Category="RccCpp"/> + </Rule> + <ItemType + Name="QtRcc" + DisplayName="Qt Resource Compiler (rcc)" /> + <FileExtension + Name="*.qrc" + ContentType="QtRcc" /> + <ContentType + Name="QtRcc" + DisplayName="Qt Resource Compiler (rcc)" + ItemType="QtRcc" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/repc/qt_import.props b/QtMSBuild/QtMsBuild/repc/qt_import.props new file mode 100644 index 0000000..991d575 --- /dev/null +++ b/QtMSBuild/QtMsBuild/repc/qt_import.props @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild import + // --> + <PropertyGroup> + <QtMsBuildProps> + $(QtMsBuildProps); + $(MSBuildThisFileDirectory)qtrepc.props + </QtMsBuildProps> + <QtMsBuildTargets> + $(QtMsBuildTargets); + $(MSBuildThisFileDirectory)qtrepc.targets + </QtMsBuildTargets> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/repc/qtrepc.props b/QtMSBuild/QtMsBuild/repc/qtrepc.props new file mode 100644 index 0000000..4d18719 --- /dev/null +++ b/QtMSBuild/QtMsBuild/repc/qtrepc.props @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild repc property definitions +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeRepc)' != ''" + Project="$(QtMsBuildProps_BeforeRepc)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QtRepc item template + // --> + <ItemDefinitionGroup> + <QtRepc> + <ExecutionDescription>repc %(Identity)</ExecutionDescription> + <QTDIR Condition="'$(QtVsProjectSettings)' == 'true'">$(QtInstallDir)</QTDIR> + <QTDIR Condition="'$(QtVsProjectSettings)' != 'true'">$(QTDIR)</QTDIR> + <InputFile>%(FullPath)</InputFile> + <OutputFileType>replica</OutputFileType> + <OutputFile><![CDATA[$(IntDir)\repc\rep_%(Filename)_%<OutputFileType>.h]]></OutputFile> + <InputFileType>rep</InputFileType> + <DynamicSource>moc_cpp</DynamicSource> + <ParallelProcess>true</ParallelProcess> + <CommandLineTemplate>[AllOptions] [AdditionalOptions]</CommandLineTemplate> + <Outputs>%(OutputFile)</Outputs> + </QtRepc> + </ItemDefinitionGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterRepc)' != ''" + Project="$(QtMsBuildProps_AfterRepc)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/repc/qtrepc.targets b/QtMSBuild/QtMsBuild/repc/qtrepc.targets new file mode 100644 index 0000000..796409b --- /dev/null +++ b/QtMSBuild/QtMsBuild/repc/qtrepc.targets @@ -0,0 +1,387 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Definitions specific to repc +/////////////////////////////////////////////////////////////////////////////////////////////////// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeRepc)' != ''" + Project="$(QtMsBuildTargets_BeforeRepc)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // --> + <PropertyGroup> + <QtBuildTargets>QtRepc;$(QtBuildTargets)</QtBuildTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Setup item type and property page + // --> + <Choose> + <When Condition="'$(QtVsProjectSettings)' == 'true' AND '$(QtVsProjectClProperties)' == 'true'"> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtrepc_v3.xml" /> + </ItemGroup> + </When> + <Otherwise> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtrepc.xml" /> + </ItemGroup> + </Otherwise> + </Choose> + <ItemGroup> + <AvailableItemName Include="QtRepc"> + <Targets>Qt</Targets> + </AvailableItemName> + </ItemGroup> + <PropertyGroup> + <QtRepcRuleName>QtRule60_Repc</QtRepcRuleName> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRepcPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Prepare to process sources + // --> + <Target Name="QtRepcPrepare" + Inputs="%(QtRepc.Identity)" Outputs="@(QtRepc->'####### Don't skip this target #######')"> + <ItemGroup> + <selected_files Include="$(SelectedFiles)"/> + </ItemGroup> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtRepcPrepare @(QtRepc)"/> + + <!--// Expand build-time metadata ( %<Foo> ) --> + <Expand Condition="'@(QtRepc)' != ''" + Items="@(QtRepc)" BaseItem="@(QtRepc)" Template="@(QtRepc)"> + <Output TaskParameter="Result" ItemName="QtRepcExp"/> + </Expand> + <ItemGroup> + <QtRepc Remove="@(QtRepc)"/> + <QtRepc Include="@(QtRepcExp->'%(Identity)')"/> + <QtRepcExp Remove="@(QtRepcExp)"/> + </ItemGroup> + + <PropertyGroup> + <selected_files>[@(selected_files->'%(Identity)','][')]</selected_files> + <file>[@(QtRepc->'%(Identity)')]</file> + <output_file>@(QtRepc->'%(OutputFile)')</output_file> + <is_selected Condition="$(selected_files.Contains('$(file)'))">true</is_selected> + <is_selected Condition="!$(selected_files.Contains('$(file)'))">false</is_selected> + </PropertyGroup> + + <!--// Delete output file to force build of source if it was manually selected to build + // (e.g. by the 'Compile' option in the context menu for the file) --> + <Delete Files="$(output_file)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' == 'true'" /> + + <!--// If a source was manually selected to build, remove all other sources --> + <ItemGroup Condition="'@(selected_files)' != ''"> + <QtRepc Remove="@(QtRepc)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' != 'true'" /> + </ItemGroup> + + <!--// Remove sources excluded from build --> + <ItemGroup> + <QtRepc Remove="@(QtRepc)" + Condition="'$(SelectedFiles)' == '' AND '%(QtRepc.ExcludedFromBuild)' == 'true'"/> + </ItemGroup> + + <!--// Add repc output dir to C++ include path --> + <ItemGroup Condition="'@(QtRepc)' != ''"> + <QtIncludePath Include="$([System.IO.Path]::GetDirectoryName('%(QtRepc.OutputFile)'))"/> + </ItemGroup> + + <!--// Clean-up --> + <PropertyGroup> + <selected_files/> + <file/> + <output_file/> + <is_selected/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRepcSetModified + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Set InputChanged flag if source file or dependencies have been modified + // --> + <Target Name="QtRepcSetModified" DependsOnTargets="QtRepcPrepare" + Condition="'@(QtRepc)' != ''" + Inputs="%(QtRepc.FullPath);%(QtRepc.AdditionalDependencies)" Outputs="@(QtRepc->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtRepcSetModified @(QtRepc)" /> + + <CreateProperty Value="true"> + <!-- // Using ValueSetByTask ensures $(input_changed) is only set to 'true' when the target + // is actually executed and not when MSBuild is figuring out which targets to run --> + <Output TaskParameter="ValueSetByTask" PropertyName="input_changed" /> + </CreateProperty> + <ItemGroup> + <QtRepc> + <InputChanged>$(input_changed)</InputChanged> + </QtRepc> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRepcOverrideCpp + ///////////////////////////////////////////////////////////////////////////////////////////////// + // + // --> + <Import Project="qtrepc_cl.targets"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtRepc + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Process each source file and produce the corresponding QtWork items + // --> + <PropertyGroup> + <QtRepcDependsOn> + QtPrepare; + QtRepcPrepare; + QtRepcSetModified; + QtRepcOverrideCpp; + $(QtRepcDependsOn) + </QtRepcDependsOn> + </PropertyGroup> + <Target Name="QtRepc" + DependsOnTargets="$(QtRepcDependsOn)" + BeforeTargets="$(QtRepcBeforeTargets)" AfterTargets="$(QtRepcAfterTargets)" + Condition="'@(QtRepc)' != ''" + Inputs="%(QtRepc.FullPath);%(QtRepc.AdditionalDependencies);$(MSBuildProjectFile)" + Outputs="@(QtRepc->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="## QtRepc @(QtRepc)" /> + + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="dependencies_changed" /> + </CreateProperty> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Convert string lists in source item properties to lists of items + // --> + <Flatten Items="@(QtRepc)" + Metadata="InputFile; + OutputFile; + InputFileType; + OutputFileType; + IncludePath; + AlwaysClass; + PrintDebug"> + <Output + TaskParameter="Result" ItemName="LocalOptions" /> + </Flatten> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Translate local paths to build host paths + // --> + <HostTranslatePaths + Items="@(LocalOptions)" + Names="InputFile; + OutputFile; + IncludePath"> + <Output + TaskParameter="Result" ItemName="options" /> + </HostTranslatePaths> + + <ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Remove quotes from all paths + // Escape trailing back-slash in paths + // Add quotes to paths containing spaces + // --> + <options> + <Value Condition="'%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'IncludePath'" + >$([System.String]::Copy('%(Value)').Replace('"', ''))</Value> + </options> + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'IncludePath') + AND $([System.String]::Copy('%(Value)').Contains(' ')) + AND $([System.String]::Copy('%(Value)').EndsWith('\'))" + >%(Value)\</Value> + </options> + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'IncludePath') + AND $([System.String]::Copy('%(Value)').Contains(' '))" + >"%(Value)"</Value> + </options> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate tool command line arguments + // --> + <!--// -i <rep|src> Input file type --> + <options> + <Value Condition="'%(Name)' == 'InputFileType'">-i %(Value)</Value> + </options> + + <!--// -o <source|replica|merged|rep> Output file type --> + <options> + <Value Condition="'%(Name)' == 'OutputFileType'">-o %(Value)</Value> + </options> + + <!--// -I <dir> Add dir to the include path for header files --> + <options> + <Value Condition="'%(Name)' == 'IncludePath'">-I%(Value)</Value> + </options> + + <!--// -c Always output 'class' type for .rep files --> + <options> + <Value Condition="'%(Name)' == 'AlwaysClass' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'AlwaysClass' AND '%(Value)' == 'true'">-c</Value> + </options> + + <!--// -d Print out parsing debug information --> + <options> + <Value Condition="'%(Name)' == 'PrintDebug' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'PrintDebug' AND '%(Value)' == 'true'">-d</Value> + </options> + + <!--// [header-file/rep-file] Input header/rep file to read from --> + <options> + <Value Condition="'%(Name)' == 'InputFile'">%(Value)</Value> + </options> + + <!--// [rep-file/header-file] Output header/rep file to write to --> + <options> + <Value Condition="'%(Name)' == 'OutputFile'">%(Value)</Value> + </options> + </ItemGroup> + <PropertyGroup> + <options>@(options->'%(Value)', ' ')</options> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Aux properties + // --> + <PropertyGroup> + <!--// Force modified flag if source was manually selected to build --> + <input_changed Condition="'$(SelectedFiles)' != ''" + >true</input_changed> + <input_changed Condition="'$(SelectedFiles)' == ''" + >%(QtRepc.InputChanged)</input_changed> + + <!--// Run work in parallel processes --> + <run_parallel Condition="'@(QtRepc)' != '' + AND '%(QtRepc.ParallelProcess)' == 'true' + AND '$(SelectedFiles)' == ''" + >true</run_parallel> + + <!--// Run work in single process --> + <run_single Condition="'@(QtRepc)' != '' + AND ('%(QtRepc.ParallelProcess)' != 'true' + OR '$(SelectedFiles)' != '')" + >true</run_single> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Create work item + // --> + <ItemGroup Condition="'$(run_parallel)' == 'true' OR '$(run_single)' == 'true'"> + <QtWork Include="@(QtRepc)"> + <WorkType>repc</WorkType> + <ToolPath Condition="'$(QtVsProjectSettings)' == 'true'">$(QtToolsPath)/repc</ToolPath> + <ToolPath Condition="'$(QtVsProjectSettings)' != 'true'">%(QtRepc.QTDIR)\bin\repc.exe</ToolPath> + <Options>$(options)</Options> + <Message>%(QtRepc.ExecutionDescription)</Message> + <DependenciesChanged>$(dependencies_changed)</DependenciesChanged> + <InputChanged>$(input_changed)</InputChanged> + <ParallelBuild Condition="'$(run_parallel)' == 'true'">true</ParallelBuild> + <ParallelBuild Condition="'$(run_single)' == 'true'">false</ParallelBuild> + </QtWork> + </ItemGroup> + + <ItemGroup Condition="'@(QtRepc)' != '' AND '@(QtRepc->'%(DynamicSource)')' != 'false'"> + <QtMoc Include="%(QtRepc.OutputFile)"> + <DependsOn>@(QtRepc)</DependsOn> + <DynamicSource Condition="'%(QtRepc.DynamicSource)' == 'moc_cpp'">output</DynamicSource> + <DynamicSource Condition="'%(QtRepc.DynamicSource)' == 'moc'">false</DynamicSource> + </QtMoc> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <options/> + <dependencies_changed/> + <input_changed/> + <run_parallel/> + <run_single/> + </PropertyGroup> + <ItemGroup> + <LocalOptions Remove="@(LocalOptions)"/> + <options Remove="@(options)"/> + <selected_files Remove="@(selected_files)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterRepc)' != ''" + Project="$(QtMsBuildTargets_AfterRepc)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/repc/qtrepc.xml b/QtMSBuild/QtMsBuild/repc/qtrepc.xml new file mode 100644 index 0000000..c35a877 --- /dev/null +++ b/QtMSBuild/QtMsBuild/repc/qtrepc.xml @@ -0,0 +1,237 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Defines the fields included in the repc property page, as well as any +// other metadata associated to QtRepc items +// --> +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule60_Repc" + PageTemplate="tool" + DisplayName="Qt Remote Objects Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRepc" /> + </Rule.DataSource> + <Rule.Categories> + <Category + Name="General"> + <Category.DisplayName> + <sys:String>repc.exe</sys:String> + </Category.DisplayName> + </Category> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRepc" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False" /> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Description="Path to installation of Qt."/> + <EnumProperty + Name="InputFileType" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Input File Type"> + <EnumValue + Name="rep" + DisplayName="Replicant template"/> + <EnumValue + Name="src" + DisplayName="C++ QObject derived classes"/> + </EnumProperty> + <StringProperty + Name="InputFile" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Input File" + Description="Input header/rep file to read from. ([header-file/rep-file])" /> + <EnumProperty + Name="OutputFileType" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Output File Type"> + <EnumValue + Name="source" + DisplayName="source (Source header)"/> + <EnumValue + Name="replica" + DisplayName="replica (Replica header)"/> + <EnumValue + Name="merged" + DisplayName="merged (Combined replica/source)"/> + <EnumValue + Name="rep" + DisplayName="rep (Replicant template)"/> + </EnumProperty> + <StringProperty + Name="OutputFile" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Output File" + Description="Output header/rep file to write to. ([rep-file/header-file])" /> + <StringListProperty + Name="IncludePath" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Include Path" + Description= +"Add <dir> to the include path for header files. This parameter is needed only if the input +file type is src (.h file). Separate with semicolons if more than one. (-I<dir>)"/> + <BoolProperty + Name="AlwaysClass" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Always Output Class Type" + Description="Always output 'class' type for .rep files and never 'POD'. (-c)"/> + <BoolProperty + Name="PrintDebug" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Print Debug Information" + Description="Print out parsing debug information (for troubleshooting). (-d)"/> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtRepcBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRepcBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtRepcAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRepcAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <EnumProperty + Name="DynamicSource" + DisplayName="Dynamic moc Source" + Description="Add output file to list of moc sources during build."> + <EnumValue + Name="moc_cpp" + DisplayName="Output moc header and moc-generated C++" + Description="Use output as dynamic moc source, and output of moc as dynamic C++ source."/> + <EnumValue + Name="moc" + DisplayName="Output moc header" + Description="Use output as dynamic moc source."/> + <EnumValue + Name="false" + DisplayName="Disable" + Description="Disable dynamic source."/> + </EnumProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + </Rule> + <ItemType + Name="QtRepc" + DisplayName="Qt Remote Objects Compiler (repc)" /> + <FileExtension + Name="*.rep" + ContentType="QtRepc" /> + <ContentType + Name="QtRepc" + DisplayName="Qt Remote Objects Compiler (repc)" + ItemType="QtRepc" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/repc/qtrepc_cl.targets_TT b/QtMSBuild/QtMsBuild/repc/qtrepc_cl.targets_TT new file mode 100644 index 0000000..5494239 --- /dev/null +++ b/QtMSBuild/QtMsBuild/repc/qtrepc_cl.targets_TT @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="targets" #> +<#@include file="$(SolutionDir)\common.tt" #> +<#@assembly Name="System.Xml" #> +<#@assembly Name="System.Xml.Linq" #> +<#@import namespace="System.Collections.Generic" #> +<#@import namespace="System.Linq" #> +<#@import namespace="System.Text" #> +<#@import namespace="System.Xml" #> +<#@import namespace="System.Xml.Linq" #> +<# + var pathRuleCl = Path.Combine(VC_TARGETS_PATH, "1033", "cl.xml"); + var xmlRule = XmlLoad(File.ReadAllText(pathRuleCl, Encoding.UTF8)); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Target Name="QtRepcOverrideCpp" DependsOnTargets="QtRepcPrepare" + Inputs="%(QtRepc.Identity)" Outputs="@(QtRepc->'####### Don't skip this target #######')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtRepcOverrideCpp @(QtRepc)" /> + + <PropertyGroup Condition="'$(QtVsProjectClProperties)' == 'true'"> + <override_cl_compile>%(QtRepc.OverrideClCompile)</override_cl_compile> + </PropertyGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'extend'"> + <QtRepc> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().Equals("StringListProperty")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .Select(x => new XElement((string)x.Attribute("Name"), + string.Format( +"@(ClCompile->WithMetadataValue('Identity','DefaultClCompile')->'%({0})');%(QtRepc.{0})", + (string)x.Attribute("Name"))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </QtRepc> + <QtRepc> + <IncludePath>%(QtRepc.AdditionalIncludeDirectories)</IncludePath> + </QtRepc> + </ItemGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'replace'"> + <QtRepc> + <IncludePath Condition="'%(QtRepc.AdditionalIncludeDirectories)' != ''" + >%(QtRepc.AdditionalIncludeDirectories)</IncludePath> + <IncludePath Condition="'%(QtRepc.AdditionalIncludeDirectories)' == ''" + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalIncludeDirectories)')</IncludePath> + </QtRepc> + </ItemGroup> + + <ItemGroup Condition="'$(override_cl_compile)' == 'false'"> + <QtRepc> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().EndsWith("Property")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .Select(x => new XElement((string)x.Attribute("Name"), + new XAttribute("Condition", + string.Format("'%(QtRepc.{0})' != ''", (string)x.Attribute("Name"))), + string.Format( + "@(ClCompile->WithMetadataValue('Identity','DefaultClCompile')->'%({0})')", + (string)x.Attribute("Name"))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <IncludePath + >@(ClCompile-> + WithMetadataValue('Identity','DefaultClCompile')-> + '%(AdditionalIncludeDirectories)')</IncludePath> + </QtRepc> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <override_cl_compile/> + </PropertyGroup> + </Target> +</Project> diff --git a/QtMSBuild/QtMsBuild/repc/qtrepc_v3.xml_TT b/QtMSBuild/QtMsBuild/repc/qtrepc_v3.xml_TT new file mode 100644 index 0000000..969763c --- /dev/null +++ b/QtMSBuild/QtMsBuild/repc/qtrepc_v3.xml_TT @@ -0,0 +1,282 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="xml" #> +<#@include file="$(SolutionDir)\common.tt" #> +<#@assembly Name="System.Xml" #> +<#@assembly Name="System.Xml.Linq" #> +<#@import namespace="System.Collections.Generic" #> +<#@import namespace="System.Linq" #> +<#@import namespace="System.Text" #> +<#@import namespace="System.Xml" #> +<#@import namespace="System.Xml.Linq" #> +<# + var pathRuleCl = Path.Combine(VC_TARGETS_PATH, "1033", "cl.xml"); + var xmlRule = XmlLoad(File.ReadAllText(pathRuleCl, Encoding.UTF8)); + var categoryNames = xmlRule + .Elements("Rule.Categories") + .Elements("Category") + .Where(x => x.Attribute("Subtype") == null) + .ToDictionary( + x => (string)x.Attribute("Name"), + x => (string)x.Attribute("DisplayName")); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule60_Repc" + PageTemplate="tool" + DisplayName="Qt Remote Objects Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRepc" /> + </Rule.DataSource> + <Rule.Categories> + <Category Name="General" DisplayName="repc"/> + <Category Name="RepcCpp" DisplayName="Override C/C++"/> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtRepc" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False" /> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Description="Path to installation of Qt."/> + <EnumProperty + Name="InputFileType" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Input File Type"> + <EnumValue + Name="rep" + DisplayName="Replicant template"/> + <EnumValue + Name="src" + DisplayName="C++ QObject derived classes"/> + </EnumProperty> + <StringProperty + Name="InputFile" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Input File" + Description="Input header/rep file to read from. ([header-file/rep-file])" /> + <EnumProperty + Name="OutputFileType" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Output File Type"> + <EnumValue + Name="source" + DisplayName="source (Source header)"/> + <EnumValue + Name="replica" + DisplayName="replica (Replica header)"/> + <EnumValue + Name="merged" + DisplayName="merged (Combined replica/source)"/> + <EnumValue + Name="rep" + DisplayName="rep (Replicant template)"/> + </EnumProperty> + <StringProperty + Name="OutputFile" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Output File" + Description="Output header/rep file to write to. ([rep-file/header-file])" /> + <BoolProperty + Name="AlwaysClass" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Always Output Class Type" + Description="Always output 'class' type for .rep files and never 'POD'. (-c)"/> + <BoolProperty + Name="PrintDebug" + HelpUrl="https://doc.qt.io/qt-5/qtremoteobjects-repc.html" + DisplayName="Print Debug Information" + Description="Print out parsing debug information (for troubleshooting). (-d)"/> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtRepcBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRepcBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtRepcAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtRepcAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <EnumProperty + Name="DynamicSource" + DisplayName="Dynamic moc Source" + Description="Add output file to list of moc sources during build."> + <EnumValue + Name="moc_cpp" + DisplayName="Output moc header and moc-generated C++" + Description="Use output as dynamic moc source, and output of moc as dynamic C++ source."/> + <EnumValue + Name="moc" + DisplayName="Output moc header" + Description="Use output as dynamic moc source."/> + <EnumValue + Name="false" + DisplayName="Disable" + Description="Disable dynamic source."/> + </EnumProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + <EnumProperty + Name="OverrideClCompile" + Category="RepcCpp" + DisplayName="Override C/C++ Properties" + Description="Define how C/C++ properties are redefined for Repc"> + <EnumValue Name="extend" DisplayName= +"Extend (append override definitions to C/C++ list properties; replace other properties)"/> + <EnumValue Name="replace" DisplayName= +"Replace (use override definitions instead of C/C++ properties)"/> + <EnumValue Name="false" DisplayName= +"Disabled (use C/C++ properties; ignore override definitions)"/> + </EnumProperty> + <StringListProperty Name="IncludePath" Visible="false"/> + <StringListProperty Name="Define" Visible="false"/> + <StringListProperty Name="Undefine" Visible="false"/> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#= +XmlPrint(xmlRule.Elements() + .Where(x => x.Name.ToString().EndsWith("Property")) + .Where(x => (string)x.Attribute("Visible") != "false") + .Where(x => (string)x.Attribute("Category") != "Command Line") + .Where(x => !x.Descendants("DataSource").Any()) + .GroupBy(x => (string)x.Attribute("Category")) + .SelectMany(y => y.Select(x => + { + x = new XElement(x); + x.SetAttributeValue("Category", "RepcCpp"); + x.SetAttributeValue("Description", "(overrides C/C++ property) " + + (string)x.Attribute("Description")); + return x; + }) + .Prepend(new XElement("StringProperty", + new XAttribute("Name", "QtCppHeader_" + y.Key.Replace(' ', '_')), + new XAttribute("Category", "RepcCpp"), + new XAttribute("ReadOnly", "true"), + new XAttribute("DisplayName", string.Format("{0} {1} {2}", + new string('#', 10), categoryNames[y.Key], new string('#', 30))))))) +#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Rule> + <ItemType + Name="QtRepc" + DisplayName="Qt Remote Objects Compiler (repc)" /> + <FileExtension + Name="*.rep" + ContentType="QtRepc" /> + <ContentType + Name="QtRepc" + DisplayName="Qt Remote Objects Compiler (repc)" + ItemType="QtRepc" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/translation/qt_import.props b/QtMSBuild/QtMsBuild/translation/qt_import.props new file mode 100644 index 0000000..92a555b --- /dev/null +++ b/QtMSBuild/QtMsBuild/translation/qt_import.props @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild import + // --> + <PropertyGroup> + <QtMsBuildProps> + $(QtMsBuildProps); + $(MSBuildThisFileDirectory)qttranslation.props + </QtMsBuildProps> + <QtMsBuildTargets> + $(QtMsBuildTargets); + $(MSBuildThisFileDirectory)qttranslation.targets + </QtMsBuildTargets> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/translation/qttranslation.props b/QtMSBuild/QtMsBuild/translation/qttranslation.props new file mode 100644 index 0000000..815f990 --- /dev/null +++ b/QtMSBuild/QtMsBuild/translation/qttranslation.props @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild Translation property definitions +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeTranslation)' != ''" + Project="$(QtMsBuildProps_BeforeTranslation)"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////////// + // Default values of Translation settings + // --> + <PropertyGroup> + <QtTranslationInput Condition="'$(QtTranslationInput)' == ''" + >@(ClCompile);@(ClInclude);@(QtMoc);@(QtUic);@(QtRCC)</QtTranslationInput> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QtTranslation item template + // --> + <ItemDefinitionGroup> + <QtTranslation> + <BuildAction>false</BuildAction> + <!-- lupdate --> + <UpdateDescription>lupdate %(Identity)</UpdateDescription> + <UpdateAdditionalOptions>$(QtLUpdateOptions)</UpdateAdditionalOptions> + <SourceLanguage>default</SourceLanguage> + <TargetLanguage>default</TargetLanguage> + <NoObsolete>false</NoObsolete> + <PluralOnly>false</PluralOnly> + <UpdateSilent>false</UpdateSilent> + <NoSort>false</NoSort> + <Locations>false</Locations> + <NoUiLines>false</NoUiLines> + <DisableSameTextHeuristic>false</DisableSameTextHeuristic> + <DisableSimilarTextHeuristic>false</DisableSimilarTextHeuristic> + <DisableNumberHeuristic>false</DisableNumberHeuristic> + <!-- lrelease --> + <ReleaseDescription>lrelease %(Identity)</ReleaseDescription> + <QmOutputDir>$(OutDir)</QmOutputDir> + <QmOutputFile>%(Filename).qm</QmOutputFile> + <ReleaseAdditionalOptions>$(QtLReleaseOptions)</ReleaseAdditionalOptions> + <IdBased>false</IdBased> + <Compress>false</Compress> + <NoUnfinished>false</NoUnfinished> + <RemoveIdentical>false</RemoveIdentical> + <ReleaseSilent>false</ReleaseSilent> + </QtTranslation> + </ItemDefinitionGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterTranslation)' != ''" + Project="$(QtMsBuildProps_AfterTranslation)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/translation/qttranslation.targets b/QtMSBuild/QtMsBuild/translation/qttranslation.targets new file mode 100644 index 0000000..6b599f9 --- /dev/null +++ b/QtMSBuild/QtMsBuild/translation/qttranslation.targets @@ -0,0 +1,626 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Definitions specific to translation +/////////////////////////////////////////////////////////////////////////////////////////////////// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeTranslation)' != ''" + Project="$(QtMsBuildTargets_BeforeTranslation)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // --> + <PropertyGroup> + <QtDependsOn>$(QtDependsOn);QtTranslation</QtDependsOn> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Build integration + // --> + <PropertyGroup> + <BuildDependsOn> + $(BuildDependsOn); + QtTranslation + </BuildDependsOn> + <CleanDependsOn> + $(CleanDependsOn); + QtTranslationClean + </CleanDependsOn> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Setup item type and property page + // --> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qttranslation.xml" /> + <AvailableItemName Include="QtTranslation"> + <Targets>QtTranslation</Targets> + </AvailableItemName> + </ItemGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtTranslationPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Calculate paths to input and output files + // --> + <Target Name="QtTranslationPrepare"> + <ItemGroup> + <QtTranslation Include="@(None)" Condition="'%(None.Extension)' == '.ts'" /> + <QtTranslation> + <InputFiles + >$(QtTranslationInput)</InputFiles> + <LUpdate + >$(QtToolsPath)/lupdate</LUpdate> + <LRelease + >$(QtToolsPath)/lrelease</LRelease> + <TsFile + >%(Identity)</TsFile> + <QmFile + >$([System.IO.Path]::Combine('%(QmOutputDir)', '%(QmOutputFile)'))</QmFile> + <InputListFile + >$([System.IO.Path]::Combine('$(IntDir)', '%(Filename).in.txt'))</InputListFile> + </QtTranslation> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtTranslationSelect + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Select files to include in build + // --> + <Target Name="QtTranslationSelect" + DependsOnTargets="QtTranslationPrepare" + Condition="'@(QtTranslation)' != ''"> + <!-- QtTranslation ::= list of .ts files in the project --> + + <!-- selected_files ::= list of explicitly selected files + (e.g. in VS, right-click file and select 'Compile') --> + <ItemGroup> + <selected_files Include="$(SelectedFiles)"/> + </ItemGroup> + + <!-- selected_ts_files ::= list of explicitly selected .ts files --> + <Join LeftItems="@(QtTranslation)" RightItems="@(selected_files)" On="Identity"> + <Output TaskParameter="Result" ItemName="selected_ts_files"/> + </Join> + + <!-- if there are explicitly selected files: --> + <ItemGroup Condition="'@(selected_files)' != ''"> + + <!-- QtTranslation ::= list of selected .ts files (if any) --> + <QtTranslation Remove="@(QtTranslation)" /> + <QtTranslation Include="@(selected_ts_files)" Condition="'@(selected_ts_files)' != ''"> + <ForceRebuild>####### Don't skip this target #######</ForceRebuild> + </QtTranslation> + + <!-- selected_files ::= list of selected files that are not .ts files (if any) --> + <selected_files Remove="@(QtTranslation)" /> + </ItemGroup> + + <!-- only_selected_ts ::= true if all selected files are .ts files --> + <PropertyGroup> + <only_selected_ts + Condition="'@(selected_ts_files)' != '' AND '@(selected_files)' == ''" + >true</only_selected_ts> + </PropertyGroup> + + <ItemGroup> + <!-- input files for lupdate --> + <QtTranslationUpdate + Include="@(QtTranslation)" + Condition="'$(QtTranslationForceUpdate)' == 'true' + OR $([System.String]::Copy('%(BuildAction)').Contains('lupdate'))"> + <Force Condition="'$(QtTranslationForceUpdate)' == 'true'">true</Force> + </QtTranslationUpdate> + <!-- input files for lrelease --> + <QtTranslationRelease + Include="@(QtTranslation)" + Condition="'$(QtTranslationForceRelease)' == 'true' + OR $([System.String]::Copy('%(BuildAction)').Contains('lrelease'))"> + <Force Condition="'$(QtTranslationForceRelease)' == 'true'">true</Force> + </QtTranslationRelease> + </ItemGroup> + + <!-- if translation files are marked for mandatory + processing, delete corresponding generated files --> + <Delete + Condition="'%(QtTranslationUpdate.Force)' == 'true'" + Files="%(QtTranslationUpdate.InputListFile)" + TreatErrorsAsWarnings="true"/> + <Delete + Condition="'%(QtTranslationRelease.Force)' == 'true'" + Files="%(QtTranslationRelease.QmFile)" + TreatErrorsAsWarnings="true"/> + + <!-- print message if only .ts files were selected but + lupdate and/or lrelease are not enabled during build --> + <Message Importance="high" + Condition="'$(SelectedFiles)' != '' + AND '$(only_selected_ts)' == 'true' + AND '@(QtTranslationUpdate)' == '' + AND '@(QtTranslationRelease)' == ''" + Text="Translation tools disabled during build; skipping."/> + + <!-- clean-up --> + <ItemGroup> + <selected_files Remove="@(selected_files)"/> + <selected_ts_files Remove="@(selected_ts_files)"/> + </ItemGroup> + <PropertyGroup> + <only_selected_ts/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtTranslationUpdate + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Run lupdate on .ts files + // --> + <Target Name="QtTranslationUpdate" + DependsOnTargets="QtTranslationSelect" + Condition="'@(QtTranslationUpdate)' != ''" + Inputs="%(QtTranslationUpdate.InputFiles); + %(QtTranslationUpdate.InputListFile); + $(MSBuildProjectFile)" + Outputs="%(QtTranslationUpdate.TsFile);%(QtTranslationUpdate.ForceRebuild)"> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Flatten metadata: items => list of (item, metadata name, metadata value) + // --> + <Flatten Items="@(QtTranslationUpdate)" + Metadata="LUpdate; + TsFile; + QmFile; + InputListFile; + InputFiles; + IncludePath; + SourceLanguage; + TargetLanguage; + NoObsolete; + PluralOnly; + UpdateSilent; + NoSort; + Locations; + NoUiLines; + DisableSameTextHeuristic; + DisableSimilarTextHeuristic; + DisableNumberHeuristic; + FunctionAliases"> + <Output + TaskParameter="Result" ItemName="LocalOptions" /> + </Flatten> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Translate local paths to build host paths + // --> + <HostTranslatePaths + Items="@(LocalOptions)" + Names="TsFile;QmFile;InputListFile;InputFiles;IncludePath"> + <Output + TaskParameter="Result" ItemName="Options" /> + </HostTranslatePaths> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Sanitize paths + // --> + <ItemGroup> + <!-- Remove quotes from paths --> + <Options> + <Value Condition="('%(Name)' == 'TsFile' + OR '%(Name)' == 'QmFile' + OR '%(Name)' == 'LUpdate' + OR '%(Name)' == 'InputFiles' + OR '%(Name)' == 'InputListFile' + OR '%(Name)' == 'IncludePath')" + >$([System.String]::Copy('%(Value)').Replace('"', ''))</Value> + </Options> + <!-- Escape trailing back-slash in paths with spaces --> + <Options> + <Value Condition="('%(Name)' == 'TsFile' + OR '%(Name)' == 'QmFile' + OR '%(Name)' == 'LUpdate' + OR '%(Name)' == 'InputListFile' + OR '%(Name)' == 'IncludePath') + AND $([System.String]::Copy('%(Value)').Contains(' ')) + AND $([System.String]::Copy('%(Value)').EndsWith('\'))" + >%(Value)\</Value> + </Options> + <!-- Add quotes to paths with spaces --> + <Options> + <Value Condition="('%(Name)' == 'TsFile' + OR '%(Name)' == 'QmFile' + OR '%(Name)' == 'LUpdate' + OR '%(Name)' == 'IncludePath') + AND $([System.String]::Copy('%(Value)').Contains(' '))" + >"%(Value)"</Value> + </Options> + </ItemGroup> + + <!-- + ////////////////////////////////////////////////////////////////////////////////////////////// + // Format lupdate command line arguments + // --> + <ItemGroup> + <Options> + <InputFiles Condition="'%(Name)' == 'InputFiles' AND Exists('%(Value)')" + >%(Value)</InputFiles> + </Options> + <Options> + <CmdExec Condition="'%(Name)' == 'LUpdate'" + >%(Value)</CmdExec> + </Options> + <Options> + <CmdOutput Condition="'%(Name)' == 'TsFile'" + >-ts %(Value)</CmdOutput> + <OutputFile Condition="'%(Name)' == 'TsFile'" + >%(Value)</OutputFile> + </Options> + <Options> + <CmdList Condition="'%(Name)' == 'InputListFile'" + >"@%(Value)"</CmdList> + <InputListFile Condition="'%(Name)' == 'InputListFile'" + >%(Value)</InputListFile> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'SourceLanguage' AND '%(Value)' != 'default'" + >-source-language %(Value)</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'TargetLanguage' AND '%(Value)' != 'default'" + >-target-language %(Value)</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'NoObsolete' AND '%(Value)' == 'true'" + >-no-obsolete</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'PluralOnly' AND '%(Value)' == 'true'" + >-pluralonly</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'UpdateSilent' AND '%(Value)' == 'true'" + >-silent</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'NoSort' AND '%(Value)' == 'true'" + >-no-sort</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'Locations' AND '%(Value)' == 'absolute'" + >-locations %(Value)</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'Locations' AND '%(Value)' == 'relative'" + >-locations %(Value)</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'Locations' AND '%(Value)' == 'none'" + >-locations %(Value)</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'NoUiLines' AND '%(Value)' == 'true'" + >-no-ui-lines</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'DisableSameTextHeuristic' AND '%(Value)' == 'true'" + >-disable-heuristic sametext</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'DisableSimilarTextHeuristic' AND '%(Value)' == 'true'" + >-disable-heuristic similartext</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'DisableNumberHeuristic' AND '%(Value)' == 'true'" + >-disable-heuristic number</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'IncludePath'" + >-I%(Value)</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'FunctionAliases'" + >-tr-function-alias %(Value)</CmdOption> + </Options> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Write list of input files + // --> + <WriteLinesToFile + Overwrite="true" + File="@(Options->'%(InputListFile)')" + Lines="@(Options->'%(InputFiles)')"/> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Run lupdate + // --> + <PropertyGroup> + <!-- Generate command line --> + <Cmd> + @(Options->'%(CmdExec)', '') + @(Options->'%(CmdOption)', ' ') + @(QtTranslationUpdate->'%(UpdateAdditionalOptions)') + @(Options->'%(CmdList)', '') + @(Options->'%(CmdOutput)', '') + </Cmd> + <Cmd>$([System.Text.RegularExpressions.Regex]::Replace('$(Cmd)', '[\s\r\n]+', ' '))</Cmd> + <Cmd>$(Cmd.Trim())</Cmd> + </PropertyGroup> + <HostExec + Message="%(QtTranslationUpdate.UpdateDescription)" + Command="$(Cmd)" + Inputs="@(Options->'%(InputListFile)');@(Options->'%(InputFiles)')" + Outputs="@(Options->'%(OutputFile)')" + RemoteTarget="$(ResolvedRemoteTarget)" + RemoteProjectDir="$(_ResolvedRemoteProjectDir)"> + </HostExec> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <Cmd/> + </PropertyGroup> + <ItemGroup> + <LocalOptions Remove="@(LocalOptions)"/> + <Options Remove="@(Options)"/> + <QtTranslationUpdate Remove="@(QtTranslationUpdate)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtTranslationRelease + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Run lrelease on .ts files + // --> + <Target Name="QtTranslationRelease" + DependsOnTargets="QtTranslationSelect; + QtTranslationUpdate" + Condition="'@(QtTranslationRelease)' != ''" + Inputs="%(QtTranslationRelease.TsFile); + $(MSBuildProjectFile)" + Outputs="%(QtTranslationRelease.QmFile); + %(QtTranslationRelease.ForceRebuild)"> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Flatten metadata: items => list of (item, metadata name, metadata value) + // --> + <Flatten Items="@(QtTranslationRelease)" + Metadata="LRelease; + TsFile; + QmFile; + IdBased; + Compress; + NoUnfinished; + RemoveIdentical; + ReleaseSilent; + UntranslatedPrefix"> + <Output + TaskParameter="Result" ItemName="LocalOptions" /> + </Flatten> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Translate local paths to build host paths + // --> + <HostTranslatePaths + Items="@(LocalOptions)" + Names="LRelease;TsFile;QmFile"> + <Output + TaskParameter="Result" ItemName="Options" /> + </HostTranslatePaths> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Sanitize paths + // --> + <ItemGroup> + <!-- Remove quotes from paths --> + <Options> + <Value Condition="('%(Name)' == 'TsFile' + OR '%(Name)' == 'QmFile' + OR '%(Name)' == 'LRelease')" + >$([System.String]::Copy('%(Value)').Replace('"', ''))</Value> + </Options> + <!-- Escape trailing back-slash in paths with spaces --> + <Options> + <Value Condition="('%(Name)' == 'TsFile' + OR '%(Name)' == 'QmFile' + OR '%(Name)' == 'LRelease') + AND $([System.String]::Copy('%(Value)').Contains(' ')) + AND $([System.String]::Copy('%(Value)').EndsWith('\'))" + >%(Value)\</Value> + </Options> + <!-- Add quotes to paths with spaces --> + <Options> + <Value Condition="('%(Name)' == 'TsFile' + OR '%(Name)' == 'QmFile' + OR '%(Name)' == 'LRelease') + AND $([System.String]::Copy('%(Value)').Contains(' '))" + >"%(Value)"</Value> + </Options> + </ItemGroup> + + <!-- + ////////////////////////////////////////////////////////////////////////////////////////////// + // Format lrelease command line arguments + // --> + <ItemGroup> + <Options> + <CmdExec Condition="'%(Name)' == 'LRelease'" + >%(Value)</CmdExec> + </Options> + <Options> + <CmdInput Condition="'%(Name)' == 'TsFile'" + >%(Value)</CmdInput> + <InputFile Condition="'%(Name)' == 'TsFile'" + >%(Value)</InputFile> + </Options> + <Options> + <CmdOutput Condition="'%(Name)' == 'QmFile'" + >-qm %(Value)</CmdOutput> + <OutputFile Condition="'%(Name)' == 'QmFile'" + >%(Value)</OutputFile> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'IdBased' AND '%(Value)' == 'true'" + >-idbased</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'Compress' AND '%(Value)' == 'true'" + >-compress</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'NoUnfinished' AND '%(Value)' == 'true'" + >-nounfinished</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'RemoveIdentical' AND '%(Value)' == 'true'" + >-removeidentical</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'ReleaseSilent' AND '%(Value)' == 'true'" + >-silent</CmdOption> + </Options> + <Options> + <CmdOption Condition="'%(Name)' == 'UntranslatedPrefix'" + >-markuntranslated %(Value)</CmdOption> + </Options> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Run lrelease + // --> + <PropertyGroup> + <!-- Generate command line --> + <Cmd> + @(Options->'%(CmdExec)', '') + @(Options->'%(CmdOption)', ' ') + @(QtTranslationRelease->'%(ReleaseAdditionalOptions)') + @(Options->'%(CmdInput)', '') + @(Options->'%(CmdOutput)', '') + </Cmd> + <Cmd>$([System.Text.RegularExpressions.Regex]::Replace('$(Cmd)', '[\s\r\n]+', ' '))</Cmd> + <Cmd>$(Cmd.Trim())</Cmd> + </PropertyGroup> + <HostExec + Message="%(QtTranslationRelease.ReleaseDescription)" + Command="$(Cmd)" + Inputs="@(Options->'%(InputFile)')" + Outputs="@(Options->'%(OutputFile)')" + RemoteTarget="$(ResolvedRemoteTarget)" + RemoteProjectDir="$(_ResolvedRemoteProjectDir)"> + </HostExec> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <Cmd/> + </PropertyGroup> + <ItemGroup> + <LocalOptions Remove="@(LocalOptions)"/> + <Options Remove="@(Options)"/> + <QtTranslationRelease Remove="@(QtTranslationRelease)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtTranslation + ///////////////////////////////////////////////////////////////////////////////////////////////// + // + // --> + <Target Name="QtTranslation" + DependsOnTargets="$(QtTranslationDependsOn); + QtTranslationSelect; + QtTranslationUpdate; + QtTranslationRelease" + BeforeTargets="$(QtTranslationBeforeTargets)" + AfterTargets="Link; + $(QtTranslationAfterTargets)"> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <ItemGroup> + <QtTranslationUpdate Remove="@(QtTranslationUpdate)"/> + <QtTranslationRelease Remove="@(QtTranslationRelease)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtTranslationClean + ///////////////////////////////////////////////////////////////////////////////////////////////// + // + // --> + <Target Name="QtTranslationClean" + DependsOnTargets="QtTranslationPrepare"> + <Delete + Files="@(QtTranslation->'%(InputListFile)');@(QtTranslation->'%(QmFile)')" + TreatErrorsAsWarnings="true"/> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterTranslation)' != ''" + Project="$(QtMsBuildTargets_AfterTranslation)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/translation/qttranslation.xml b/QtMSBuild/QtMsBuild/translation/qttranslation.xml new file mode 100644 index 0000000..1667a18 --- /dev/null +++ b/QtMSBuild/QtMsBuild/translation/qttranslation.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule_Translation" + PageTemplate="tool" + DisplayName="Qt Translation" + Order="9"> + <Rule.DataSource> + <DataSource Persistence="ProjectFile" ItemType="QtTranslation" /> + </Rule.DataSource> + <Rule.Categories> + <Category Name="General" DisplayName="General"/> + <Category Name="Update" DisplayName="lupdate"/> + <Category Name="Release" DisplayName="lrelease"/> + </Rule.Categories> + <!-- General --> + <EnumProperty + Name="BuildAction" Category="General" DisplayName="Run Translation Tool During Build"> + <EnumValue Name="false" DisplayName="Disabled"/> + <EnumValue Name="lupdate" DisplayName="lupdate"/> + <EnumValue Name="lrelease" DisplayName="lrelease"/> + <EnumValue Name="lupdate_lrelease" DisplayName="lupdate + lrelease"/> + </EnumProperty> + <!-- lupdate --> + <StringProperty + Name="UpdateDescription" Category="lupdate" DisplayName="Execution Description" /> + <EnumProperty + Name="SourceLanguage" Category="lupdate" DisplayName="Source Language" + Description="Specify the language of the source strings for new files. -source-language <language>[_<region>]"> + <EnumValue Name="default" DisplayName="POSIX"/> + </EnumProperty> + <EnumProperty + Name="TargetLanguage" Category="lupdate" DisplayName="Target Language" + Description="Specify the language of the translations for new files. -target-language <language>[_<region>]"> + <EnumValue Name="default" DisplayName="Guess from file name"/> + </EnumProperty> + <BoolProperty + Name="NoObsolete" Category="lupdate" DisplayName="Drop Obsolete Strings" + Description="Drop all obsolete and vanished strings. -no-obsolete"/> + <BoolProperty + Name="PluralOnly" Category="lupdate" DisplayName="Plural Only" + Description="Only include plural form messages. -pluralonly"/> + <BoolProperty + Name="UpdateSilent" Category="lupdate" DisplayName="Silent" + Description="Do not explain what is being done. -silent"/> + <BoolProperty + Name="NoSort" Category="lupdate" DisplayName="Disable Sort" + Description="Do not sort contexts in TS files. -no-sort"/> + <EnumProperty + Name="Locations" Category="lupdate" DisplayName="Locations" + Description="Specify/override how source code references are saved in TS files. Default is absolute for new files. -locations {absolute|relative|none}"> + <EnumValue Name="absolute" DisplayName="Absolute"/> + <EnumValue Name="relative" DisplayName="Relative"/> + <EnumValue Name="none" DisplayName="None"/> + <EnumValue Name="false" DisplayName="Guess from TS file"/> + </EnumProperty> + <BoolProperty + Name="NoUiLines" Category="lupdate" DisplayName="Disable UI Lines" + Description="Do not record line numbers in references to UI files. -no-ui-lines"/> + <BoolProperty + Name="DisableSameTextHeuristic" Category="lupdate" DisplayName="Disable Same Text Merge Heuristic" + Description="Disable the named merge heuristic. Can be specified multiple times. -disable-heuristic sametext"/> + <BoolProperty + Name="DisableSimilarTextHeuristic" Category="lupdate" DisplayName="Disable Similar Text Merge Heuristic" + Description="Disable the named merge heuristic. Can be specified multiple times. -disable-heuristic similartext"/> + <BoolProperty + Name="DisableNumberHeuristic" Category="lupdate" DisplayName="Disable Number Merge Heuristic" + Description="Disable the named merge heuristic. Can be specified multiple times. -disable-heuristic number"/> + <StringListProperty + Name="IncludePath" Category="lupdate" DisplayName="Include Path" + Description="Additional locations to look for include files. -I <includepath>"/> + <StringListProperty + Name="FunctionAliases" Category="lupdate" DisplayName="Function Aliases" + Description="With +=, recognize <alias> as an alternative spelling of <function>. With =, recognize <alias> as the only spelling of <function>. -tr-function-alias <function>{+=,=}<alias>[,<function>{+=,=}<alias>]..."/> + <StringProperty + Name="UpdateAdditionalOptions" Category="lupdate" DisplayName="AdditionalOptions"/> + <!-- lrelease --> + <StringProperty + Name="ReleaseDescription" Category="lrelease" DisplayName="Execution Description" /> + <StringProperty + Name="QmOutputDir" Category="lrelease" DisplayName="Output Directory" + Description="Specifies the path of the generated QM output directory."/> + <StringProperty + Name="QmOutputFile" Category="lrelease" DisplayName="Output File Name" + Description="Specifies the name of the generated QM output file."/> + <BoolProperty + Name="IdBased" Category="lrelease" DisplayName="ID Based" + Description="Use IDs instead of source strings for message keying -idbased"/> + <BoolProperty + Name="Compress" Category="lrelease" DisplayName="Compress" + Description="Compress the QM files -compress"/> + <BoolProperty + Name="NoUnfinished" Category="lrelease" DisplayName="Skip Unfinished" + Description="Do not include unfinished translations -nounfinished"/> + <BoolProperty + Name="RemoveIdentical" Category="lrelease" DisplayName="Remove Identical" + Description="If the translated text is the same as the source text, do not include the message -removeidentical"/> + <BoolProperty + Name="ReleaseSilent" Category="lrelease" DisplayName="Silent" + Description="Do not explain what is being done -silent"/> + <StringProperty + Name="UntranslatedPrefix" Category="lrelease" DisplayName="Untranslated Prefix" + Description="If a message has no real translation, use the source text prefixed with the given string instead -markuntranslated <prefix>"/> + <StringProperty + Name="ReleaseAdditionalOptions" Category="lrelease" DisplayName="AdditionalOptions"/> + </Rule> + <ItemType + Name="QtTranslation" + DisplayName="Qt Translation File" /> + <FileExtension + Name="*.ts" + ContentType="QtTranslation" /> + <ContentType + Name="QtTranslation" + DisplayName="Qt Translation File" + ItemType="QtTranslation" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/uic/qt_import.props b/QtMSBuild/QtMsBuild/uic/qt_import.props new file mode 100644 index 0000000..864be45 --- /dev/null +++ b/QtMSBuild/QtMsBuild/uic/qt_import.props @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild import + // --> + <PropertyGroup> + <QtMsBuildProps> + $(QtMsBuildProps); + $(MSBuildThisFileDirectory)qtuic.props + </QtMsBuildProps> + <QtMsBuildTargets> + $(QtMsBuildTargets); + $(MSBuildThisFileDirectory)qtuic.targets + </QtMsBuildTargets> + </PropertyGroup> +</Project> diff --git a/QtMSBuild/QtMsBuild/uic/qtuic.props b/QtMSBuild/QtMsBuild/uic/qtuic.props new file mode 100644 index 0000000..94c1f47 --- /dev/null +++ b/QtMSBuild/QtMsBuild/uic/qtuic.props @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Qt/MSBuild uic property definitions +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildProps_BeforeUic)' != ''" + Project="$(QtMsBuildProps_BeforeUic)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // QtUic item template + // --> + <ItemDefinitionGroup> + <QtUic> + <ExecutionDescription>uic %(Identity)</ExecutionDescription> + + <QTDIR Condition="'$(QtVsProjectSettings)' != 'true'" + >$(QTDIR)</QTDIR> + <QTDIR Condition="'$(QtVsProjectSettings)' == 'true'" + >$(QtInstallDir)</QTDIR> + + <InputFile>%(FullPath)</InputFile> + + <OutputFile Condition="'$(QtVsProjectSettings)' != 'true'" + >$(ProjectDir)GeneratedFiles\$(Configuration)\ui_%(Filename).h</OutputFile> + + <QtUicDir Condition="'$(QtVsProjectSettings)' == 'true'" + >$(IntDir)uic\</QtUicDir> + <QtUicFileName Condition="'$(QtVsProjectSettings)' == 'true'" + >ui_%(Filename).h</QtUicFileName> + + <ParallelProcess>true</ParallelProcess> + <CommandLineTemplate>[AllOptions] [AdditionalOptions]</CommandLineTemplate> + <Outputs>%(OutputFile)</Outputs> + </QtUic> + </ItemDefinitionGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildProps_AfterUic)' != ''" + Project="$(QtMsBuildProps_AfterUic)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/uic/qtuic.targets b/QtMSBuild/QtMsBuild/uic/qtuic.targets new file mode 100644 index 0000000..6993e99 --- /dev/null +++ b/QtMSBuild/QtMsBuild/uic/qtuic.targets @@ -0,0 +1,406 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Definitions specific to uic +/////////////////////////////////////////////////////////////////////////////////////////////////// +// --> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import pre-requisites + // --> + <Import + Condition="'$(QtMsBuildTargets_BeforeUic)' != ''" + Project="$(QtMsBuildTargets_BeforeUic)"/> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Qt/MSBuild global properties + // --> + <PropertyGroup> + <QtBuildTargets>QtUic;$(QtBuildTargets)</QtBuildTargets> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Setup item type and property page + // --> + <Choose> + <When Condition="'$(QtVsProjectSettings)' == 'true' AND '$(QtVsProjectClProperties)' == 'true'"> + <ItemGroup> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtuic_v3.xml" /> + </ItemGroup> + </When> + <Otherwise> + <ItemGroup Condition="'$(QtVsProjectSettings)' == 'false'"> + <PropertyPageSchema + Include="$(MSBuildThisFileDirectory)qtuic.xml" /> + </ItemGroup> + </Otherwise> + </Choose> + <ItemGroup> + <AvailableItemName Include="QtUic"> + <Targets>Qt</Targets> + </AvailableItemName> + </ItemGroup> + <PropertyGroup> + <QtUicRuleName>QtRule50_Uic</QtUicRuleName> + </PropertyGroup> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtUicInit + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Initialize default metadata + // --> + <Target Name="QtUicInit"> + <!--// Initialize %(OutputFile) --> + <ItemGroup Condition="'$(QtVsProjectSettings)' == 'true' AND '@(QtUic)' != ''"> + <QtUicAux Include="@(QtUic)"> + <OutputFile + >$([System.IO.Path]::Combine('%(QtUic.QtUicDir)','%(QtUic.QtUicFileName)'))</OutputFile> + </QtUicAux> + <QtUic Remove="@(QtUic)"/> + <QtUic Include="@(QtUicAux)"/> + <QtUicAux Remove="@(QtUicAux)"/> + </ItemGroup> + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Add uic output dir to C++ include path + // --> + <ItemGroup Condition="'@(QtUic)' != ''"> + <QtIncludePath Include="$([System.IO.Path]::GetDirectoryName('%(QtUic.OutputFile)'))"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtUicPrepare + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Prepare to process sources + // --> + <Target Name="QtUicPrepare" DependsOnTargets="QtUicInit" + Inputs="%(QtUic.Identity)" Outputs="@(QtUic->'####### Don't skip this target #######')"> + <ItemGroup> + <selected_files Include="$(SelectedFiles)"/> + </ItemGroup> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtUicPrepare @(QtUic)"/> + + <PropertyGroup> + <selected_files>[@(selected_files->'%(Identity)','][')]</selected_files> + <file>[@(QtUic->'%(Identity)')]</file> + <output_file>@(QtUic->'%(OutputFile)')</output_file> + <is_selected Condition="$(selected_files.Contains('$(file)'))">true</is_selected> + <is_selected Condition="!$(selected_files.Contains('$(file)'))">false</is_selected> + </PropertyGroup> + + <!--// Delete output file to force build of source if it was manually selected to build + // (e.g. by the 'Compile' option in the context menu for the file) --> + <Delete Files="$(output_file)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' == 'true'" /> + + <!--// If a source was manually selected to build, remove all other sources --> + <ItemGroup Condition="'@(selected_files)' != ''"> + <QtUic Remove="@(QtUic)" + Condition="'$(SelectedFiles)' != '' AND '$(is_selected)' != 'true'" /> + </ItemGroup> + + <!--// Remove sources excluded from build --> + <ItemGroup> + <QtUic Remove="@(QtUic)" + Condition="'$(SelectedFiles)' == '' AND '%(QtUic.ExcludedFromBuild)' == 'true'"/> + </ItemGroup> + + <PropertyGroup> + <selected_files/> + <file/> + <output_file/> + <is_selected/> + </PropertyGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtUicSetModified + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Set InputChanged flag if source file or dependencies have been modified + // --> + <Target Name="QtUicSetModified" DependsOnTargets="QtUicPrepare" + Condition="'@(QtUic)' != ''" + Inputs="%(QtUic.FullPath);%(QtUic.AdditionalDependencies)" Outputs="@(QtUic->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" + Text="## QtUicSetModified @(QtUic)" /> + + <CreateProperty Value="true"> + <!-- // Using ValueSetByTask ensures $(input_changed) is only set to 'true' when the target + // is actually executed and not when MSBuild is figuring out which targets to run --> + <Output TaskParameter="ValueSetByTask" PropertyName="input_changed" /> + </CreateProperty> + <ItemGroup> + <QtUic> + <InputChanged>$(input_changed)</InputChanged> + </QtUic> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + /// TARGET QtUic + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Process each source file and produce the corresponding QtWork items + // --> + <PropertyGroup> + <QtUicDependsOn> + QtPrepare; + QtUicPrepare; + QtUicSetModified; + $(QtUicDependsOn) + </QtUicDependsOn> + </PropertyGroup> + <Target Name="QtUic" + DependsOnTargets="$(QtUicDependsOn)" + BeforeTargets="$(QtUicBeforeTargets)" AfterTargets="$(QtUicAfterTargets)" + Condition="'@(QtUic)' != ''" + Inputs="%(QtUic.FullPath);%(QtUic.AdditionalDependencies);$(MSBuildProjectFile)" + Outputs="@(QtUic->'%(OutputFile)')"> + + <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="## QtUic @(QtUic)" /> + + <CreateProperty Value="true"> + <Output TaskParameter="ValueSetByTask" PropertyName="dependencies_changed" /> + </CreateProperty> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Convert string lists in source item properties to lists of items + // --> + <Flatten Items="@(QtUic)" + Metadata="InputFile; + OutputFile; + DisplayDependencies; + NoProtection; + NoImplicitIncludes; + Postfix; + Translate; + Include; + Generator; + IdBased"> + <Output + TaskParameter="Result" ItemName="LocalOptions" /> + </Flatten> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Translate local paths to build host paths + // --> + <HostTranslatePaths + Items="@(LocalOptions)" + Names="InputFile; + OutputFile; + Include"> + <Output + TaskParameter="Result" ItemName="options" /> + </HostTranslatePaths> + + <ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + // Remove quotes from all paths + // Escape trailing back-slash in paths + // Add quotes to paths containing spaces + // --> + <options> + <Value Condition="'%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'Include'" + >$([System.String]::Copy('%(Value)').Replace('"', ''))</Value> + </options> + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'Include') + AND $([System.String]::Copy('%(Value)').Contains(' ')) + AND $([System.String]::Copy('%(Value)').EndsWith('\'))" + >%(Value)\</Value> + </options> + <options> + <Value Condition="('%(Name)' == 'InputFile' OR '%(Name)' == 'OutputFile' + OR '%(Name)' == 'Include') + AND $([System.String]::Copy('%(Value)').Contains(' '))" + >"%(Value)"</Value> + </options> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Generate tool command line arguments + // --> + <!--// [uifile] Input file (*.ui) --> + <options> + <Value Condition="'%(Name)' == 'InputFile'">%(Value)</Value> + </options> + + <!--// -o, -output <file> Place the output into <file> --> + <options> + <Value Condition="'%(Name)' == 'OutputFile'">-o %(Value)</Value> + </options> + + <!--// -d, -dependencies Display the dependencies --> + <options> + <Value Condition="'%(Name)' == 'DisplayDependencies' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'DisplayDependencies' AND '%(Value)' == 'true'" + >--dependencies</Value> + </options> + + <!--// -p, -no-protection Disable header protection --> + <options> + <Value Condition="'%(Name)' == 'NoProtection' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'NoProtection' AND '%(Value)' == 'true'" + >--no-protection</Value> + </options> + + <!--// -n, -no-implicit-includes Disable generation of #include-directives --> + <options> + <Value Condition="'%(Name)' == 'NoImplicitIncludes' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'NoImplicitIncludes' AND '%(Value)' == 'true'" + >--no-implicit-includes</Value> + </options> + + <!--// -postfix <postfix> Postfix to add to all generated classnames --> + <options> + <Value Condition="'%(Name)' == 'Postfix'">--postfix %(Value)</Value> + </options> + + <!--// -tr, -translate <function> Use <function> for i18n --> + <options> + <Value Condition="'%(Name)' == 'Translate'">--tr %(Value)</Value> + </options> + + <!--// -include <include-file> Add #include <include-file> to <file> --> + <options> + <Value Condition="'%(Name)' == 'Include'">--include %(Value)</Value> + </options> + + <!--// -g, -generator <java|cpp> Select generator --> + <options> + <Value Condition="'%(Name)' == 'Generator'">-g %(Value)</Value> + </options> + + <!--// -idbased Use id based function for i18n --> + <options> + <Value Condition="'%(Name)' == 'IdBased' AND '%(Value)' != 'true'"></Value> + </options> + <options> + <Value Condition="'%(Name)' == 'IdBased' AND '%(Value)' == 'true'">--idbased</Value> + </options> + </ItemGroup> + <PropertyGroup> + <options>@(options->'%(Value)', ' ')</options> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Aux properties + // --> + <PropertyGroup> + <!--// Force modified flag if source was manually selected to build --> + <input_changed Condition="'$(SelectedFiles)' != ''" + >true</input_changed> + <input_changed Condition="'$(SelectedFiles)' == ''" + >%(QtUic.InputChanged)</input_changed> + + <!--// Run work in parallel processes --> + <run_parallel Condition="'@(QtUic)' != '' + AND '%(QtUic.ParallelProcess)' == 'true' + AND '$(SelectedFiles)' == ''" + >true</run_parallel> + + <!--// Run work in single process --> + <run_single Condition="'@(QtUic)' != '' + AND ('%(QtUic.ParallelProcess)' != 'true' + OR '$(SelectedFiles)' != '')" + >true</run_single> + </PropertyGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Create work item + // --> + <ItemGroup Condition="'$(run_parallel)' == 'true' OR '$(run_single)' == 'true'"> + <QtWork Include="@(QtUic)"> + <WorkType>uic</WorkType> + <ToolPath Condition="'$(QtVsProjectSettings)' == 'true'">$(QtToolsPath)/uic</ToolPath> + <ToolPath Condition="'$(QtVsProjectSettings)' != 'true'">%(QtUic.QTDIR)\bin\uic.exe</ToolPath> + <Options>$(options)</Options> + <Message>%(QtUic.ExecutionDescription)</Message> + <DependenciesChanged>$(dependencies_changed)</DependenciesChanged> + <InputChanged>$(input_changed)</InputChanged> + <ParallelBuild Condition="'$(run_parallel)' == 'true'">true</ParallelBuild> + <ParallelBuild Condition="'$(run_single)' == 'true'">false</ParallelBuild> + </QtWork> + </ItemGroup> + + <!-- + /////////////////////////////////////////////////////////////////////////////////////////////// + // Clean-up + // --> + <PropertyGroup> + <options/> + <dependencies_changed/> + <input_changed/> + <run_parallel/> + <run_single/> + </PropertyGroup> + <ItemGroup> + <LocalOptions Remove="@(LocalOptions)"/> + <options Remove="@(options)"/> + <selected_files Remove="@(selected_files)"/> + </ItemGroup> + </Target> + + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Import dependants + // --> + <Import + Condition="'$(QtMsBuildTargets_AfterUic)' != ''" + Project="$(QtMsBuildTargets_AfterUic)"/> + +</Project> diff --git a/QtMSBuild/QtMsBuild/uic/qtuic.xml b/QtMSBuild/QtMsBuild/uic/qtuic.xml new file mode 100644 index 0000000..2326fd6 --- /dev/null +++ b/QtMSBuild/QtMsBuild/uic/qtuic.xml @@ -0,0 +1,235 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> + +<!-- +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Defines the fields included in the uic property page, as well as any +// other metadata associated to QtUic items +// --> +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule50_Uic" + PageTemplate="tool" + DisplayName="Qt User Interface Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtUic" /> + </Rule.DataSource> + <Rule.Categories> + <Category + Name="General"> + <Category.DisplayName> + <sys:String>uic.exe</sys:String> + </Category.DisplayName> + </Category> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtUic" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False" /> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Description="Path to installation of Qt."/> + <StringProperty + Name="QtUicDir" + DisplayName="Output Directory" + Description="Specifies the path of the generated uic output directory." + Visible="false"/> + <StringProperty + Name="QtUicFileName" + DisplayName="Output File Name" + Description="Specifies the name of the generated uic output file." + Visible="false"/> + <StringProperty + Name="InputFile" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Input File" + Description="Specifies the full path of the input file. (<file>)" + Switch=""[value]"" /> + <StringProperty + Name="OutputFile" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Output File" + Description="Specifies the full path of the output file. (-o <file>)" + Switch="-o "[value]"" /> + <BoolProperty + Name="DisplayDependencies" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Display Dependencies" + Description="Display the dependencies. (--dependencies)" + Switch="--dependencies"/> + <BoolProperty + Name="NoProtection" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="No Protection" + Description="Disable header protection. (--no-protection)" + Switch="--no-protection"/> + <BoolProperty + Name="NoImplicitIncludes" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="No Implicit Includes" + Description="Disable generation of #include-directives. (--no-implicit-includes)" + Switch="--no-implicit-includes"/> + <StringProperty + Name="Postfix" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Postfix" + Description="<postfix> to add to all generated classnames. (--postfix <postfix>)" + Switch="--postfix [value]"/> + <StringProperty + Name="Translate" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Translate" + Description="Use <function> for i18n. (--tr <function>)" + Switch="--tr [value]" /> + <StringProperty + Name="Include" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Include" + Description="Add #include <file> to the output. (--include <file>)" + Switch="--include "[value]"" /> + <StringProperty + Name="Generator" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Generator" + Description="Select generator. (-g <java|cpp>)" + Switch="-g [value]" /> + <BoolProperty + Name="IdBased" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="ID Based" + Description="Use id based function for i18n. (--idbased)" + Switch="--idbased"/> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtUicBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtUicBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtUicAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtUicAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + </Rule> + <ItemType + Name="QtUic" + DisplayName="Qt User Interface Compiler (uic)" /> + <FileExtension + Name="*.ui" + ContentType="QtUic" /> + <ContentType + Name="QtUic" + DisplayName="Qt User Interface Compiler (uic)" + ItemType="QtUic" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/QtMsBuild/uic/qtuic_v3.xml b/QtMSBuild/QtMsBuild/uic/qtuic_v3.xml new file mode 100644 index 0000000..71a14dd --- /dev/null +++ b/QtMSBuild/QtMsBuild/uic/qtuic_v3.xml @@ -0,0 +1,218 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<ProjectSchemaDefinitions + xmlns="http://schemas.microsoft.com/build/2009/properties" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:sys="clr-namespace:System;assembly=mscorlib"> + <Rule + Name="QtRule50_Uic" + PageTemplate="tool" + DisplayName="Qt User Interface Compiler" + Order="9"> + <Rule.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtUic" /> + </Rule.DataSource> + <Rule.Categories> + <Category Name="General" DisplayName="uic"/> + </Rule.Categories> + <StringListProperty + Name="Inputs" + Category="General" + IsRequired="true" + Visible="False"> + <StringListProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="QtUic" + SourceType="Item" /> + </StringListProperty.DataSource> + </StringListProperty> + <StringProperty + Name="ExecutionDescription" + DisplayName="Execution Description" + IncludeInCommandLine="False" /> + <StringProperty + Name="QtUicDir" + DisplayName="Output Directory" + Description="Specifies the path of the generated uic output directory."/> + <StringProperty + Name="QtUicFileName" + DisplayName="Output File Name" + Description="Specifies the name of the generated uic output file."/> + <StringProperty + Name="QTDIR" + DisplayName="Qt Directory" + Visible="false"/> + <StringProperty + Name="InputFile" + DisplayName="Input File" + Visible="false"/> + <StringProperty + Name="OutputFile" + DisplayName="Output File" + Visible="false"/> + <BoolProperty + Name="DisplayDependencies" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Display Dependencies" + Description="Display the dependencies. (--dependencies)" + Switch="--dependencies"/> + <BoolProperty + Name="NoProtection" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="No Protection" + Description="Disable header protection. (--no-protection)" + Switch="--no-protection"/> + <BoolProperty + Name="NoImplicitIncludes" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="No Implicit Includes" + Description="Disable generation of #include-directives. (--no-implicit-includes)" + Switch="--no-implicit-includes"/> + <StringProperty + Name="Postfix" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Postfix" + Description="<postfix> to add to all generated classnames. (--postfix <postfix>)" + Switch="--postfix [value]"/> + <StringProperty + Name="Translate" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Translate" + Description="Use <function> for i18n. (--tr <function>)" + Switch="--tr [value]" /> + <StringProperty + Name="Include" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Include" + Description="Add #include <file> to the output. (--include <file>)" + Switch="--include "[value]"" /> + <StringProperty + Name="Generator" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="Generator" + Description="Select generator. (-g <java|cpp>)" + Switch="-g [value]" /> + <BoolProperty + Name="IdBased" + HelpUrl="http://doc.qt.io/qt-5/uic.html" + DisplayName="ID Based" + Description="Use id based function for i18n. (--idbased)" + Switch="--idbased"/> + <StringProperty + Name="CommandLineTemplate" + DisplayName="Command Line" + Visible="False" + IncludeInCommandLine="False" /> + <DynamicEnumProperty + Name="QtUicBeforeTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute Before</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run before.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtUicBeforeTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <DynamicEnumProperty + Name="QtUicAfterTargets" + Category="General" + EnumProvider="Targets" + IncludeInCommandLine="False" Visible="False"> + <DynamicEnumProperty.DisplayName> + <sys:String>Execute After</sys:String> + </DynamicEnumProperty.DisplayName> + <DynamicEnumProperty.Description> + <sys:String>Specifies the targets for the build customization to run after.</sys:String> + </DynamicEnumProperty.Description> + <DynamicEnumProperty.ProviderSettings> + <NameValuePair + Name="Exclude" + Value="^QtUicAfterTargets|^Compute" /> + </DynamicEnumProperty.ProviderSettings> + <DynamicEnumProperty.DataSource> + <DataSource + Persistence="ProjectFile" + ItemType="" + HasConfigurationCondition="true" /> + </DynamicEnumProperty.DataSource> + </DynamicEnumProperty> + <StringListProperty + Name="Outputs" + DisplayName="Outputs" + IncludeInCommandLine="False" + Visible="False" /> + <StringListProperty + Name="AdditionalDependencies" + DisplayName="Additional Dependencies" + IncludeInCommandLine="False"/> + <StringProperty + Subtype="AdditionalOptions" + Name="AdditionalOptions" + Category="General"> + <StringProperty.DisplayName> + <sys:String>Additional Options</sys:String> + </StringProperty.DisplayName> + <StringProperty.Description> + <sys:String>Additional Options</sys:String> + </StringProperty.Description> + </StringProperty> + <BoolProperty + Name="ParallelProcess" + DisplayName="Parallel Process" + Description="Run tool in parallel process."/> + </Rule> + <ItemType + Name="QtUic" + DisplayName="Qt User Interface Compiler (uic)" /> + <FileExtension + Name="*.ui" + ContentType="QtUic" /> + <ContentType + Name="QtUic" + DisplayName="Qt User Interface Compiler (uic)" + ItemType="QtUic" /> +</ProjectSchemaDefinitions> diff --git a/QtMSBuild/Tasks/CriticalSection.cs b/QtMSBuild/Tasks/CriticalSection.cs new file mode 100644 index 0000000..e2a44d6 --- /dev/null +++ b/QtMSBuild/Tasks/CriticalSection.cs @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="CriticalSection" + +#region Using +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using Microsoft.Build.Framework; +using Microsoft.Win32; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK CriticalSection +///////////////////////////////////////////////////////////////////////////////////////////////// +// Enter or leave a critical section during build +// Parameters: +// in bool Lock: 'true' when entering the critical section, 'false' when leaving +// in string Name: Critical section lock name +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class CriticalSection + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + public static IBuildEngine BuildEngine { get; set; } + + public static bool Execute( + #region Parameters + System.Boolean Lock, + System.String Name) + #endregion + { + #region Code + var buildEngine = BuildEngine as IBuildEngine4; + + // Acquire lock + string lockName = string.Format("Global\\{0}", Name); + EventWaitHandle buildLock = null; + if (!EventWaitHandle.TryOpenExisting(lockName, out buildLock)) { + // Lock does not exist; create lock + bool lockCreated; + buildLock = new EventWaitHandle( + true, EventResetMode.AutoReset, lockName, out lockCreated); + if (lockCreated) { + // Keep lock alive until end of build + buildEngine.RegisterTaskObject( + Name, buildLock, RegisteredTaskObjectLifetime.Build, false); + } + } + if (buildLock == null) { + Log.LogError("Qt::BuildLock[{0}]: Error accessing lock", Name); + return false; + } + if (Lock) { + // Wait until locked + if (!buildLock.WaitOne(1000)) { + // Issue waiting warning + Log.LogWarning("Qt::BuildLock[{0}]: Waiting...", Name); + var t = Stopwatch.StartNew(); + do { + // Check for build errors + if (Log.HasLoggedErrors) { + Log.LogError("Qt::BuildLock[{0}]: Errors logged; wait aborted", Name); + return false; + } + // Timeout after 10 secs. + if (t.ElapsedMilliseconds >= 10000) { + Log.LogError("Qt::BuildLock[{0}]: Timeout; wait aborted", Name); + return false; + } + } while (!buildLock.WaitOne(1000)); + } + } else { + // Unlock + buildLock.Set(); + } + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/DumpItems.cs b/QtMSBuild/Tasks/DumpItems.cs new file mode 100644 index 0000000..73b9d0b --- /dev/null +++ b/QtMSBuild/Tasks/DumpItems.cs @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="DumpItems" + +#region Reference +#endregion + +#region Using +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.Framework; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK DumpItems +///////////////////////////////////////////////////////////////////////////////////////////////// +// Dump contents of items as a log message. The contents are formatted as XML. +// Parameters: +// in string ItemType: type of the items; this is used as the parent node of each +// item dump +// in ITaskItem[] Items: items to dump +// in bool DumpReserved: include MSBuild reserved metadata in dump? +// in string Metadata: list of names of metadata to include in dump; omit to +// include all metadata +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class DumpItems + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + + public static bool Execute( + #region Parameters + System.String ItemType, + Microsoft.Build.Framework.ITaskItem[] Items, + System.Boolean DumpReserved = false, + System.String Metadata = null) + #endregion + { + #region Code + var reserved = new HashSet<string> + { + "AccessedTime", "CreatedTime", "DefiningProjectDirectory", + "DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName", + "Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime", + "RecursiveDir", "RelativeDir", "RootDir", + }; + if (Metadata == null) + Metadata = ""; + var requestedNames = new HashSet<string>(Metadata.Split(new[] { ';' }, + StringSplitOptions.RemoveEmptyEntries)); + var itemXml = new StringBuilder(); + if (Items.Any()) { + foreach (var item in Items) { + if (itemXml.Length > 0) + itemXml.Append("\r\n"); + itemXml.AppendFormat("<{0} Include=\"{1}\"", ItemType, item.ItemSpec); + var names = item.MetadataNames.Cast<string>() + .Where(x => (DumpReserved || !reserved.Contains(x)) + && (!requestedNames.Any() || requestedNames.Contains(x))) + .OrderBy(x => x); + if (names.Any()) { + itemXml.Append(">\r\n"); + foreach (string name in names) { + if (!DumpReserved && reserved.Contains(name)) + continue; + if (!item.MetadataNames.Cast<string>().Contains(name)) + continue; + var value = item.GetMetadata(name); + if (!string.IsNullOrEmpty(value)) + itemXml.AppendFormat(" <{0}>{1}</{0}>\r\n", name, value); + else + itemXml.AppendFormat(" <{0}/>\r\n", name); + } + itemXml.AppendFormat("</{0}>", ItemType); + } else { + itemXml.Append("/>"); + } + } + } else { + itemXml.AppendFormat("<{0}/>", ItemType); + } + Log.LogMessage(MessageImportance.High, itemXml.ToString()); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/Expand.cs b/QtMSBuild/Tasks/Expand.cs new file mode 100644 index 0000000..31d0bee --- /dev/null +++ b/QtMSBuild/Tasks/Expand.cs @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="Expand" + +#region Using +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK Expand +///////////////////////////////////////////////////////////////////////////////////////////////// +// Expand a list of items, taking additional metadata from a base item and from a template item. +// Parameters: +// in ITaskItem[] Items: items to expand +// in ITaskItem BaseItem: base item from which the expanded items derive +// in ITaskItem Template = null: (optional) template containing metadata to add / update +// out ITaskItem[] Result: list of new items +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class Expand + { + public static bool Execute( + #region Parameters + out Microsoft.Build.Framework.ITaskItem[] Result, + Microsoft.Build.Framework.ITaskItem[] Items, + Microsoft.Build.Framework.ITaskItem BaseItem, + Microsoft.Build.Framework.ITaskItem Template = null) + #endregion + { + #region Code + Result = new ITaskItem[] { }; + var reserved = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) + { + "AccessedTime", "CreatedTime", "DefiningProjectDirectory", + "DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName", + "Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime", + "RecursiveDir", "RelativeDir", "RootDir", + }; + var newItems = new List<ITaskItem>(); + foreach (var item in Items) { + var newItem = new TaskItem(item); + if (BaseItem != null) + BaseItem.CopyMetadataTo(newItem); + var itemExt = newItem.GetMetadata("Extension"); + if (!string.IsNullOrEmpty(itemExt)) + newItem.SetMetadata("Suffix", itemExt.Substring(1)); + if (Template != null) { + var metadataNames = Template.MetadataNames + .Cast<string>().Where(x => !reserved.Contains(x)); + foreach (var metadataName in metadataNames) { + var metadataValue = Template.GetMetadata(metadataName); + newItem.SetMetadata(metadataName, + Regex.Replace(metadataValue, @"(%<)(\w+)(>)", + match => newItem.GetMetadata(match.Groups[2].Value))); + } + } + newItems.Add(newItem); + } + Result = newItems.ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/Flatten.cs b/QtMSBuild/Tasks/Flatten.cs new file mode 100644 index 0000000..81d9b37 --- /dev/null +++ b/QtMSBuild/Tasks/Flatten.cs @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="Flatten" + +#region Using +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK Flatten +///////////////////////////////////////////////////////////////////////////////////////////////// +// Destructure items into a "flat" list of metadata. The output is a list of (Name, Value) pairs, +// each corresponding to one item metadata. Semi-colon-separated lists will also expand to many +// items in the output list, with the metadata name shared among them. +// Example: +// INPUT: +// <QtMoc> +// <InputFile>foo.h</InputFile> +// <IncludePath>C:\FOO;D:\BAR</IncludePath> +// </QtMoc> +// OUTPUT: +// <Result> +// <Name>InputFile</Name> +// <Value>foo.h</Value> +// </Result> +// <Result> +// <Name>IncludePath</Name> +// <Value>C:\FOO</Value> +// </Result> +// <Result> +// <Name>IncludePath</Name> +// <Value>D:\BAR</Value> +// </Result> +// Parameters: +// in ITaskItem[] Items: list of items to flatten +// in string[] Metadata: names of metadata to look for; omit to include all metadata +// out ITaskItem[] Result: list of metadata from all items +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class Flatten + { + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem[] Items, + out Microsoft.Build.Framework.ITaskItem[] Result, + System.String[] Metadata = null) + #endregion + { + #region Code + Result = new ITaskItem[] { }; + var reserved = new HashSet<string> + { + "AccessedTime", "CreatedTime", "DefiningProjectDirectory", + "DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName", + "Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime", + "RecursiveDir", "RelativeDir", "RootDir", + }; + if (Metadata == null) + Metadata = new string[0]; + var requestedNames = new HashSet<string>(Metadata.Where(x => !string.IsNullOrEmpty(x))); + var newItems = new List<ITaskItem>(); + foreach (var item in Items) { + var itemName = item.ItemSpec; + var names = item.MetadataNames.Cast<string>().Where(x => !reserved.Contains(x) + && (!requestedNames.Any() || requestedNames.Contains(x))); + foreach (string name in names) { + var values = item.GetMetadata(name).Split(';'); + foreach (string value in values.Where(v => !string.IsNullOrEmpty(v))) { + newItems.Add(new TaskItem(string.Format("{0}={1}", name, value), + new Dictionary<string, string> + { + { "Item", itemName }, + { "Name", name }, + { "Value", value }, + })); + } + } + } + Result = newItems.ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/GetItemHash.cs b/QtMSBuild/Tasks/GetItemHash.cs new file mode 100644 index 0000000..9682d90 --- /dev/null +++ b/QtMSBuild/Tasks/GetItemHash.cs @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="GetItemHash" + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK GetItemHash +///////////////////////////////////////////////////////////////////////////////////////////////// +// Calculate an hash code (Deflate + Base64) for an item, given a list of metadata to use as key +// Parameters: +// in ITaskItem Item: item for which the hash will be calculated +// in string[] Keys: list of names of the metadata to use as item key +// out string Hash: hash code (Base64 representation of Deflate'd UTF-8 item key) +#endregion + +#region Using +using System; +using System.Linq; +using System.Text; +using System.IO; +using System.IO.Compression; +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class GetItemHash + { + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem Item, + System.String[] Keys, + out System.String Hash) + #endregion + { + #region Code + var data = Encoding.UTF8.GetBytes(string.Concat(Keys.OrderBy(x => x) + .Select(x => string.Format("[{0}={1}]", x, Item.GetMetadata(x)))) + .ToUpper()); + using (var dataZipped = new MemoryStream()) { + using (var zip = new DeflateStream(dataZipped, CompressionLevel.Fastest)) + zip.Write(data, 0, data.Length); + Hash = Convert.ToBase64String(dataZipped.ToArray()); + } + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/GetVarsFromMSBuild.cs b/QtMSBuild/Tasks/GetVarsFromMSBuild.cs new file mode 100644 index 0000000..ad6fea3 --- /dev/null +++ b/QtMSBuild/Tasks/GetVarsFromMSBuild.cs @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="GetVarsFromMSBuild" + +#region Reference +//System.Xml +//System.Xml.Linq +//System.Xml.XPath.XDocument +#endregion + +#region Using +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Linq; +using System.Xml.XPath; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK GetVarsFromMSBuild +///////////////////////////////////////////////////////////////////////////////////////////////// +// +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class GetVarsFromMSBuild + { + public static bool Execute( + #region Parameters + System.String Project, + Microsoft.Build.Framework.ITaskItem[] VarDefs, + System.String[] ExcludeValues, + out Microsoft.Build.Framework.ITaskItem[] OutVars) + #endregion + { + #region Code + OutVars = null; + var result = new List<Microsoft.Build.Framework.ITaskItem>(); + var projectXml = XDocument.Load(Project); + projectXml.Descendants().ToList().ForEach(x => x.Name = x.Name.LocalName); + foreach (var varDef in VarDefs) { + string varName = varDef.GetMetadata("Name"); + string varXPath = varDef.GetMetadata("XPath"); + string varValue = ""; + var valueElement = projectXml.XPathSelectElement(varXPath); + if (valueElement != null) { + var propertyName = valueElement.Name.LocalName; + varValue = Regex.Replace(valueElement.Value, + string.Format(@"[ ;][%$]\({0}\)$", propertyName), + string.Empty); + } + if (!ExcludeValues.Contains(varValue, StringComparer.InvariantCultureIgnoreCase)) { + var outVar = new TaskItem(varName.Trim()); + outVar.SetMetadata("Value", varValue.Trim()); + result.Add(outVar); + } + } + OutVars = result.ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/GetVarsFromMakefile.cs b/QtMSBuild/Tasks/GetVarsFromMakefile.cs new file mode 100644 index 0000000..ff44a7d --- /dev/null +++ b/QtMSBuild/Tasks/GetVarsFromMakefile.cs @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="GetVarsFromMakefile" + +#region Using +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.IO; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK GetVarsFromMakefile +///////////////////////////////////////////////////////////////////////////////////////////////// +// +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class GetVarsFromMakefile + { + public static bool Execute( + #region Parameters + System.String Makefile, + System.String[] ExcludeValues, + out Microsoft.Build.Framework.ITaskItem[] OutVars, + Microsoft.Build.Framework.ITaskItem[] VarDefs = null) + #endregion + { + #region Code + var makefileVars = Regex.Matches( + File.ReadAllText(Makefile), + @"^(\w+)[^\=\r\n\S]*\=[^\r\n\S]*([^\r\n]+)[\r\n]", + RegexOptions.Multiline) + .Cast<Match>() + .Where(x => x.Groups.Count > 2 && x.Groups[1].Success && x.Groups[2].Success + && !string.IsNullOrEmpty(x.Groups[1].Value)) + .GroupBy(x => x.Groups[1].Value) + .ToDictionary(g => g.Key, g => g.Last().Groups[2].Value); + OutVars = VarDefs + .Where(x => makefileVars.ContainsKey(x.GetMetadata("Name"))) + .Select(x => new TaskItem(x.ItemSpec, new Dictionary<string, string> + { { + "Value", + string.Join(";", Regex + .Matches(makefileVars[x.GetMetadata("Name")], x.GetMetadata("Pattern")) + .Cast<Match>() + .Select(y => Regex + .Replace(y.Value, x.GetMetadata("Pattern"), x.GetMetadata("Value"))) + .Where(y => !string.IsNullOrEmpty(y) + && !ExcludeValues.Contains(y, + StringComparer.InvariantCultureIgnoreCase)) + .ToHashSet()) + } })) + .Where(x => !string.IsNullOrEmpty(x.GetMetadata("Value"))) + .ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostExec_LinuxSSL.cs b/QtMSBuild/Tasks/HostExec_LinuxSSL.cs new file mode 100644 index 0000000..4c1cd3a --- /dev/null +++ b/QtMSBuild/Tasks/HostExec_LinuxSSL.cs @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostExec" Condition="'$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' != 'WSL_1_0'" + +#region Reference +//$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll +#endregion + +#region Using +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK HostExec +/// * Linux build over SSL +///////////////////////////////////////////////////////////////////////////////////////////////// +// Run command in build host. +// Parameters: +// in string Command: Command to run on the build host +// in string RedirectStdOut: Path to file to receive redirected output messages +// * can be NUL to discard messages +// in string RedirectStdErr: Path to file to receive redirected error messages +// * can be NUL to discard messages +// * can be STDOUT to apply the same redirection as output messages +// in string WorkingDirectory: Path to directory where command will be run +// in ITaskItem[] Inputs: List of local -> host path mappings for command inputs +// * item format: cf. HostTranslatePaths task +// in ITaskItem[] Outputs: List of host -> local path mappings for command outputs +// * item format: cf. HostTranslatePaths task +// in string RemoteTarget: Set by ResolveRemoteDir in SSH mode; null otherwise +// in string RemoteProjectDir: Set by ResolveRemoteDir in SSH mode; null otherwise +// in bool IgnoreExitCode: Set flag to disable build error if command failed +// out int ExitCode: status code at command exit +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostExec_LinuxSSL + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + public static IBuildEngine BuildEngine { get; set; } + public static ITaskHost HostObject { get; set; } + + public static bool Execute( + #region Parameters + System.String Command, + out System.Int32 ExitCode, + System.String Message = null, + System.String RedirectStdOut = null, + System.String RedirectStdErr = null, + System.String WorkingDirectory = null, + Microsoft.Build.Framework.ITaskItem[] Inputs = null, + Microsoft.Build.Framework.ITaskItem[] Outputs = null, + System.String RemoteTarget = null, + System.String RemoteProjectDir = null, + System.Boolean IgnoreExitCode = false) + #endregion + { + #region Code + if (!string.IsNullOrEmpty(Message)) + Log.LogMessage(MessageImportance.High, Message); + var createDirs = new List<string> + { + string.Format("{0}/{1}", RemoteProjectDir, WorkingDirectory) + }; + + var localFilesToCopyRemotelyMapping = new string[0]; + if (Inputs != null) { + localFilesToCopyRemotelyMapping = Inputs + .Select(x => string.Format(@"{0}:={1}/{2}", + x.GetMetadata("Item"), + RemoteProjectDir, + x.GetMetadata("Value"))) + .ToArray(); + createDirs.AddRange(Inputs + .Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value")))); + } + + var remoteFilesToCopyLocallyMapping = new string[0]; + if (Outputs != null) { + remoteFilesToCopyLocallyMapping = Outputs + .Select(x => string.Format(@"{0}/{1}:={2}", + RemoteProjectDir, + x.GetMetadata("Value"), + x.GetMetadata("Item"))) + .ToArray(); + createDirs.AddRange(Outputs + .Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value")))); + } + + Command = "(" + Command + ")"; + if (RedirectStdOut == "NUL" || RedirectStdOut == "/dev/null") + Command += " 1> /dev/null"; + else if (!string.IsNullOrEmpty(RedirectStdOut)) + Command += " 1> " + RedirectStdOut; + if (RedirectStdErr == "NUL" || RedirectStdErr == "/dev/null") + Command += " 2> /dev/null"; + else if (RedirectStdErr == "STDOUT") + Command += " 2>&1"; + else if (!string.IsNullOrEmpty(RedirectStdErr)) + Command += " 2> " + RedirectStdErr; + Command = string.Format("cd {0}/{1}; {2}", RemoteProjectDir, WorkingDirectory, Command); + + var taskCopyFiles = new Microsoft.Build.Linux.Tasks.Execute() + { + BuildEngine = BuildEngine, + HostObject = HostObject, + ProjectDir = @"$(ProjectDir)", + IntermediateDir = @"$(IntDir)", + RemoteTarget = RemoteTarget, + RemoteProjectDir = RemoteProjectDir, + Command = string.Join("; ", createDirs.Select(x => string.Format("mkdir -p {0}", x))), + LocalFilesToCopyRemotelyMapping = localFilesToCopyRemotelyMapping, + }; + var taskExec = new Microsoft.Build.Linux.Tasks.Execute() + { + BuildEngine = BuildEngine, + HostObject = HostObject, + ProjectDir = @"$(ProjectDir)", + IntermediateDir = @"$(IntDir)", + RemoteTarget = RemoteTarget, + RemoteProjectDir = RemoteProjectDir, + Command = Command, + RemoteFilesToCopyLocallyMapping = remoteFilesToCopyLocallyMapping, + }; + + Log.LogMessage("\r\n==== HostExec: Microsoft.Build.Linux.Tasks.Execute"); + Log.LogMessage("ProjectDir: {0}", taskExec.ProjectDir); + Log.LogMessage("IntermediateDir: {0}", taskExec.IntermediateDir); + Log.LogMessage("RemoteTarget: {0}", taskExec.RemoteTarget); + Log.LogMessage("RemoteProjectDir: {0}", taskExec.RemoteProjectDir); + if (taskExec.LocalFilesToCopyRemotelyMapping.Any()) + Log.LogMessage("LocalFilesToCopyRemotelyMapping: {0}", + taskExec.LocalFilesToCopyRemotelyMapping); + if (taskExec.RemoteFilesToCopyLocallyMapping.Any()) + Log.LogMessage("RemoteFilesToCopyLocallyMapping: {0}", + taskExec.RemoteFilesToCopyLocallyMapping); + Log.LogMessage("CreateDirs: {0}", taskCopyFiles.Command); + Log.LogMessage("Command: {0}", taskExec.Command); + + if (!taskCopyFiles.ExecuteTool()) { + ExitCode = taskCopyFiles.ExitCode; + return false; + } + bool ok = taskExec.ExecuteTool(); + Log.LogMessage("== {0} ExitCode: {1}\r\n", ok ? "OK" : "FAIL", taskExec.ExitCode); + + ExitCode = taskExec.ExitCode; + if (!ok && !IgnoreExitCode) { + Log.LogError("Host command failed."); + return false; + } + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostExec_LinuxWSL.cs b/QtMSBuild/Tasks/HostExec_LinuxWSL.cs new file mode 100644 index 0000000..8b17443 --- /dev/null +++ b/QtMSBuild/Tasks/HostExec_LinuxWSL.cs @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostExec" Condition="('$(VisualStudioVersion)' == '16.0' OR '$(VisualStudioVersion)' == '17.0') AND '$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' == 'WSL_1_0'" + +#region Reference +//$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll +#endregion + +#region Using +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK HostExec +/// * Linux build over WSL +///////////////////////////////////////////////////////////////////////////////////////////////// +// Run command in build host. +// Parameters: +// in string Command: Command to run on the build host +// in string RedirectStdOut: Path to file to receive redirected output messages +// * can be NUL to discard messages +// in string RedirectStdErr: Path to file to receive redirected error messages +// * can be NUL to discard messages +// * can be STDOUT to apply the same redirection as output messages +// in string WorkingDirectory: Path to directory where command will be run +// in ITaskItem[] Inputs: List of local -> host path mappings for command inputs +// * item format: cf. HostTranslatePaths task +// in ITaskItem[] Outputs: List of host -> local path mappings for command outputs +// * item format: cf. HostTranslatePaths task +// in string RemoteTarget: Set by ResolveRemoteDir in SSH mode; null otherwise +// in string RemoteProjectDir: Set by ResolveRemoteDir in SSH mode; null otherwise +// in bool IgnoreExitCode: Set flag to disable build error if command failed +// out int ExitCode: status code at command exit +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostExec_LinuxWSL + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + public static IBuildEngine BuildEngine { get; set; } + public static ITaskHost HostObject { get; set; } + + public static bool Execute( + #region Parameters + System.String Command, + out System.Int32 ExitCode, + System.String Message = null, + System.String RedirectStdOut = null, + System.String RedirectStdErr = null, + System.String WorkingDirectory = null, + Microsoft.Build.Framework.ITaskItem[] Inputs = null, + Microsoft.Build.Framework.ITaskItem[] Outputs = null, + System.String RemoteTarget = null, + System.String RemoteProjectDir = null, + System.Boolean IgnoreExitCode = false) + #endregion + { +#if (VS2019 || VS2022) + #region Code + if (!string.IsNullOrEmpty(Message)) + Log.LogMessage(MessageImportance.High, Message); + Command = "(" + Command + ")"; + if (RedirectStdOut == "NUL" || RedirectStdOut == "/dev/null") + Command += " 1> /dev/null"; + else if (!string.IsNullOrEmpty(RedirectStdOut)) + Command += " 1> " + RedirectStdOut; + if (RedirectStdErr == "NUL" || RedirectStdErr == "/dev/null") + Command += " 2> /dev/null"; + else if (RedirectStdErr == "STDOUT") + Command += " 2>&1"; + else if (!string.IsNullOrEmpty(RedirectStdErr)) + Command += " 2> " + RedirectStdErr; + + var createDirs = new List<string>(); + if (Inputs != null) { + createDirs.AddRange(Inputs + .Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value")))); + } + if (Outputs != null) { + createDirs.AddRange(Outputs + .Select(x => string.Format("\x24(dirname {0})", x.GetMetadata("Value")))); + } + if (!string.IsNullOrEmpty(WorkingDirectory)) { + createDirs.Add(WorkingDirectory); + Command = string.Format("cd {0}; {1}", WorkingDirectory, Command); + } + if (createDirs.Any()) { + Command = string.Format("{0}; {1}", + string.Join("; ", createDirs.Select(x => string.Format("mkdir -p {0}", x))), + Command); + } + + var taskExec = new Microsoft.Build.Linux.WSL.Tasks.ExecuteCommand() + { + BuildEngine = BuildEngine, + HostObject = HostObject, + ProjectDir = @"$(ProjectDir)", + IntermediateDir = @"$(IntDir)", + WSLPath = @"$(WSLPath)", + Command = Command, + }; + Log.LogMessage("\r\n==== HostExec: Microsoft.Build.Linux.WSL.Tasks.ExecuteCommand"); + Log.LogMessage("ProjectDir: {0}", taskExec.ProjectDir); + Log.LogMessage("IntermediateDir: {0}", taskExec.IntermediateDir); + Log.LogMessage("WSLPath: {0}", taskExec.WSLPath); + Log.LogMessage("Command: {0}", taskExec.Command); + + bool ok = taskExec.Execute(); + Log.LogMessage("== {0} ExitCode: {1}\r\n", ok ? "OK" : "FAIL", taskExec.ExitCode); + + ExitCode = taskExec.ExitCode; + if (!ok && !IgnoreExitCode) { + Log.LogError("Host command failed."); + return false; + } + #endregion +#else + ExitCode = 0; +#endif + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostExec_LinuxWSL_Error.cs b/QtMSBuild/Tasks/HostExec_LinuxWSL_Error.cs new file mode 100644 index 0000000..acb36fd --- /dev/null +++ b/QtMSBuild/Tasks/HostExec_LinuxWSL_Error.cs @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostExec" Condition="('$(VisualStudioVersion)' != '16.0' AND '$(VisualStudioVersion)' != '17.0') AND '$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' == 'WSL_1_0'" + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostExec_LinuxWSL_Error + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + + public static bool Execute( + #region Parameters + System.String Command, + out System.Int32 ExitCode, + System.String Message = null, + System.String RedirectStdOut = null, + System.String RedirectStdErr = null, + System.String WorkingDirectory = null, + Microsoft.Build.Framework.ITaskItem[] Inputs = null, + Microsoft.Build.Framework.ITaskItem[] Outputs = null, + System.String RemoteTarget = null, + System.String RemoteProjectDir = null, + System.Boolean IgnoreExitCode = false) + #endregion + { + #region Code + ExitCode = 1; + Log.LogError("Cross-compilation of Qt projects in WSL not supported."); + return false; + #endregion + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostExec_Windows.cs b/QtMSBuild/Tasks/HostExec_Windows.cs new file mode 100644 index 0000000..cb23326 --- /dev/null +++ b/QtMSBuild/Tasks/HostExec_Windows.cs @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostExec" Condition="'$(ApplicationType)' != 'Linux'" + +#region Reference +//$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll +//$(MSBuildToolsPath)\Microsoft.Build.Utilities.Core.dll +#endregion + +#region Using +using Microsoft.Build.Framework; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK HostTranslatePaths +/// * Local (Windows) build +///////////////////////////////////////////////////////////////////////////////////////////////// +// Translate local (Windows) paths to build host paths. This could be a Linux host for cross +// compilation, or a simple copy (i.e. "no-op") when building in Windows. +// Input and output items are in the form: +// <...> +// <Item>...</Item> +// <Name>...</Name> +// <Value>...</Value> +// </...> +// where <Item> is the local path, <Name> is a filter criteria identifier matched with the Names +// parameter, and <Value> is set to the host path in output items (for input items <Value> must +// be equal to <Item>). +// Parameters: +// in ITaskItem[] Items: input items with local paths +// in string[] Names: filter criteria; unmatched items will simply be copied (i.e. no-op) +// out ITaskItem[] Result: output items with translated host paths +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostExec_Windows + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + public static IBuildEngine BuildEngine { get; set; } + public static ITaskHost HostObject { get; set; } + + public static bool Execute( + #region Parameters + System.String Command, + out System.Int32 ExitCode, + System.String Message = null, + System.String RedirectStdOut = null, + System.String RedirectStdErr = null, + System.String WorkingDirectory = null, + Microsoft.Build.Framework.ITaskItem[] Inputs = null, + Microsoft.Build.Framework.ITaskItem[] Outputs = null, + System.String RemoteTarget = null, + System.String RemoteProjectDir = null, + System.Boolean IgnoreExitCode = false) + #endregion + { + #region Code + if (!string.IsNullOrEmpty(Message)) + Log.LogMessage(MessageImportance.High, Message); + Command = "(" + Command + ")"; + if (RedirectStdOut == "NUL" || RedirectStdOut == "/dev/null") + Command += " 1> NUL"; + else if (!string.IsNullOrEmpty(RedirectStdOut)) + Command += " 1> " + RedirectStdOut; + if (RedirectStdErr == "NUL" || RedirectStdErr == "/dev/null") + Command += " 2> NUL"; + else if (RedirectStdErr == "STDOUT") + Command += " 2>&1"; + else if (!string.IsNullOrEmpty(RedirectStdErr)) + Command += " 2> " + RedirectStdErr; + + var taskExec = new Microsoft.Build.Tasks.Exec() + { + BuildEngine = BuildEngine, + HostObject = HostObject, + WorkingDirectory = WorkingDirectory, + Command = Command, + IgnoreExitCode = IgnoreExitCode, + }; + + Log.LogMessage("\r\n==== HostExec: Microsoft.Build.Tasks.Exec"); + Log.LogMessage("WorkingDirectory: {0}", taskExec.WorkingDirectory); + Log.LogMessage("Command: {0}", taskExec.Command); + + bool ok = taskExec.Execute(); + Log.LogMessage("== {0} ExitCode: {1}\r\n", ok ? "OK" : "FAIL", taskExec.ExitCode); + + ExitCode = taskExec.ExitCode; + if (!ok) + return false; + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostTranslatePaths_LinuxSSL.cs b/QtMSBuild/Tasks/HostTranslatePaths_LinuxSSL.cs new file mode 100644 index 0000000..3e0fb60 --- /dev/null +++ b/QtMSBuild/Tasks/HostTranslatePaths_LinuxSSL.cs @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostTranslatePaths" Condition = "'$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' != 'WSL_1_0'" + +#region Reference +//$(VCTargetsPath)\Application Type\Linux\1.0\Microsoft.Build.Linux.Tasks.dll +#endregion + +#region Using +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK HostTranslatePaths +/// * Linux build over SSL +///////////////////////////////////////////////////////////////////////////////////////////////// +// Translate local (Windows) paths to build host paths. This could be a Linux host for cross +// compilation, or a simple copy (i.e. "no-op") when building in Windows. +// Input and output items are in the form: +// <...> +// <Item>...</Item> +// <Name>...</Name> +// <Value>...</Value> +// </...> +// where <Item> is the local path, <Name> is a filter criteria identifier matched with the Names +// parameter, and <Value> is set to the host path in output items (for input items <Value> must +// be equal to <Item>). +// Parameters: +// in ITaskItem[] Items: input items with local paths +// in string[] Names: filter criteria; unmatched items will simply be copied (i.e. no-op) +// out ITaskItem[] Result: output items with translated host paths +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostTranslatePaths_LinuxSSL + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem[] Items, + out Microsoft.Build.Framework.ITaskItem[] Result, + System.String[] Names = null) + #endregion + { + #region Code + Result = new ITaskItem[] { }; + var newItems = new List<ITaskItem>(); + foreach (var item in Items) { + string itemName = item.GetMetadata("Name"); + string itemValue = item.GetMetadata("Value"); + if (Names.Contains(itemName)) { + if (Path.IsPathRooted(itemValue) && !itemValue.StartsWith("/")) { + var projectdir = new Uri(@"$(ProjectDir)"); + var itemFileName = Path.GetFileName(itemValue); + var itemDirName = Path.GetFullPath(Path.GetDirectoryName(itemValue)); + if (!itemDirName.EndsWith(@"\")) + itemDirName += @"\"; + var itemDir = new Uri(itemDirName); + if (projectdir.IsBaseOf(itemDir)) { + itemValue = projectdir.MakeRelativeUri(itemDir).OriginalString + + itemFileName; + } else { + Log.LogWarning("Unable to translate path: {0}", itemValue); + } + } else { + itemValue = itemValue.Replace(@"\", "/"); + } + } + newItems.Add(new TaskItem(item.ItemSpec, + new Dictionary<string, string> + { + { "Item", item.GetMetadata("Item") }, + { "Name", itemName }, + { "Value", itemValue }, + })); + } + Result = newItems.ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL.cs b/QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL.cs new file mode 100644 index 0000000..bdbbc5c --- /dev/null +++ b/QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL.cs @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostTranslatePaths" Condition="('$(VisualStudioVersion)' == '16.0' OR '$(VisualStudioVersion)' == '17.0') AND '$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' == 'WSL_1_0'" + +#region Reference +//$(VCTargetsPath)\Application Type\Linux\1.0\liblinux.dll +#endregion + +#region Using +using System.Collections.Generic; +using System.Linq; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using liblinux.IO; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK HostTranslatePaths +/// * Linux build over WSL +///////////////////////////////////////////////////////////////////////////////////////////////// +// Translate local (Windows) paths to build host paths. This could be a Linux host for cross +// compilation, or a simple copy (i.e. "no-op") when building in Windows. +// Input and output items are in the form: +// <...> +// <Item>...</Item> +// <Name>...</Name> +// <Value>...</Value> +// </...> +// where <Item> is the local path, <Name> is a filter criteria identifier matched with the Names +// parameter, and <Value> is set to the host path in output items (for input items <Value> must +// be equal to <Item>). +// Parameters: +// in ITaskItem[] Items: input items with local paths +// in string[] Names: filter criteria; unmatched items will simply be copied (i.e. no-op) +// out ITaskItem[] Result: output items with translated host paths +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostTranslatePaths_LinuxWSL + { + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem[] Items, + out Microsoft.Build.Framework.ITaskItem[] Result, + System.String[] Names = null) + #endregion + { +#if (VS2019 || VS2022) + #region Code + Result = new ITaskItem[] { }; + var newItems = new List<ITaskItem>(); + foreach (var item in Items) { + string itemName = item.GetMetadata("Name"); + string itemValue = item.GetMetadata("Value"); + if (Names.Contains(itemName)) { + if (Path.IsPathRooted(itemValue) && !itemValue.StartsWith("/")) + itemValue = PathUtils.TranslateWindowsPathToWSLPath(itemValue); + else + itemValue = itemValue.Replace(@"\", "/"); + } + newItems.Add(new TaskItem(item.ItemSpec, + new Dictionary<string, string> + { + { "Item", item.GetMetadata("Item") }, + { "Name", itemName }, + { "Value", itemValue }, + })); + } + Result = newItems.ToArray(); + #endregion +#else + Result = null; +#endif + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL_Error.cs b/QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL_Error.cs new file mode 100644 index 0000000..ae404b2 --- /dev/null +++ b/QtMSBuild/Tasks/HostTranslatePaths_LinuxWSL_Error.cs @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostTranslatePaths" Condition="('$(VisualStudioVersion)' != '16.0' AND '$(VisualStudioVersion)' != '17.0') AND '$(ApplicationType)' == 'Linux' AND '$(PlatformToolset)' == 'WSL_1_0'" + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostTranslatePaths_LinuxWSL_Error + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem[] Items, + out Microsoft.Build.Framework.ITaskItem[] Result, + System.String[] Names = null) + #endregion + { + #region Code + Result = null; + Log.LogError("Cross-compilation of Qt projects in WSL not supported."); + return false; + #endregion + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/HostTranslatePaths_Windows.cs b/QtMSBuild/Tasks/HostTranslatePaths_Windows.cs new file mode 100644 index 0000000..b26a6f0 --- /dev/null +++ b/QtMSBuild/Tasks/HostTranslatePaths_Windows.cs @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="HostTranslatePaths" Condition = "'$(ApplicationType)' != 'Linux'" + +#region Using +using System.Linq; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK HostTranslatePaths +/// * Local (Windows) build +///////////////////////////////////////////////////////////////////////////////////////////////// +// Translate local (Windows) paths to build host paths. This could be a Linux host for cross +// compilation, or a simple copy (i.e. "no-op") when building in Windows. +// Input and output items are in the form: +// <...> +// <Item>...</Item> +// <Name>...</Name> +// <Value>...</Value> +// </...> +// where <Item> is the local path, <Name> is a filter criteria identifier matched with the Names +// parameter, and <Value> is set to the host path in output items (for input items <Value> must +// be equal to <Item>). +// Parameters: +// in ITaskItem[] Items: input items with local paths +// in string[] Names: filter criteria; unmatched items will simply be copied (i.e. no-op) +// out ITaskItem[] Result: output items with translated host paths +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class HostTranslatePaths_Windows + { + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem[] Items, + out Microsoft.Build.Framework.ITaskItem[] Result, + System.String[] Names = null) + #endregion + { + #region Code + Result = Items.ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/Join.cs b/QtMSBuild/Tasks/Join.cs new file mode 100644 index 0000000..5d707a5 --- /dev/null +++ b/QtMSBuild/Tasks/Join.cs @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="Join" + +#region Using +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK Join +///////////////////////////////////////////////////////////////////////////////////////////////// +// Combines two lists of items into a single list containing items with common metadata values. +// Example: +// INPUT: +// Left = <LeftItem><X>foo</X><Y>42</Y></LeftItem> +// <LeftItem><X>sna</X><Y>99</Y></LeftItem> +// <LeftItem><X>bar</X><Y>3.14159</Y></LeftItem> +// Right = <RightItem><Z>foo</Z><Y>99</Y></RightItem> +// <RightItem><Z>sna</Z><Y>2.71828</Y></RightItem> +// <RightItem><Z>bar</Z><Y>42</Y></RightItem> +// <RightItem><Z>bar</Z><Y>99</Y></RightItem> +// OUTPUT: +// Result = <Item><X>foo</X><Y>42</Y><Z>bar</Z></Item> +// <Item><X>sna</X><Y>99</Y><Z>foo</Z></Item> +// <Item><X>sna</X><Y>99</Y><Z>bar</Z></Item> +// Parameters: +// in ITaskItem[] LeftItems: first list of items to join +// in ITaskItem[] RightItems: second list of items to join +// out ITaskItem[] Result: resulting list of items with common metadata +// optional in string[] On: join criteria, list of names of metadata to match in both +// input lists; all metadata in the criteria must be matched; +// by default, %(Identity) will be used as criteria; the +// special value 'ROW_NUMBER' will match the position of items +// (i.e. items with the same index in each input list). +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class Join + { + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem[] LeftItems, + Microsoft.Build.Framework.ITaskItem[] RightItems, + out Microsoft.Build.Framework.ITaskItem[] Result, + System.String[] On = null) + #endregion + { + #region Code + Result = new ITaskItem[] { }; + var resultItems = new List<ITaskItem>(); + List<string> matchFields = null; + if (On != null) + matchFields = new List<string>(On); + if (matchFields == null || !matchFields.Any()) + matchFields = new List<string> { "Identity" }; + + var reserved = new HashSet<string> + { + "AccessedTime", "CreatedTime", "DefiningProjectDirectory", + "DefiningProjectExtension", "DefiningProjectFullPath", "DefiningProjectName", + "Directory", "Extension", "Filename", "FullPath", "Identity", "ModifiedTime", + "RecursiveDir", "RelativeDir", "RootDir", + }; + + for (int leftRowNum = 0; leftRowNum < LeftItems.Length; leftRowNum++) { + for (int rightRowNum = 0; rightRowNum < RightItems.Length; rightRowNum++) { + var leftItem = LeftItems[leftRowNum]; + var rightItem = RightItems[rightRowNum]; + bool match = true; + foreach (string field in matchFields) { + if (field.Equals("ROW_NUMBER", StringComparison.OrdinalIgnoreCase)) { + match = (leftRowNum == rightRowNum); + } else { + string leftField = leftItem.GetMetadata(field); + string rightField = rightItem.GetMetadata(field); + match = leftField.Equals(rightField, StringComparison.OrdinalIgnoreCase); + } + if (!match) + break; + } + if (match) { + var resultItem = new TaskItem(leftItem.ItemSpec); + foreach (string rightMetadata in rightItem.MetadataNames) { + if (!reserved.Contains(rightMetadata)) { + var metadataValue = rightItem.GetMetadata(rightMetadata); + if (!string.IsNullOrEmpty(metadataValue)) + resultItem.SetMetadata(rightMetadata, metadataValue); + } + } + foreach (string leftMetadata in leftItem.MetadataNames) { + if (!reserved.Contains(leftMetadata)) { + var metadataValue = leftItem.GetMetadata(leftMetadata); + if (!string.IsNullOrEmpty(metadataValue)) + resultItem.SetMetadata(leftMetadata, metadataValue); + } + } + resultItems.Add(resultItem); + } + } + } + + Result = resultItems.ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/ListQrc.cs b/QtMSBuild/Tasks/ListQrc.cs new file mode 100644 index 0000000..c82b30d --- /dev/null +++ b/QtMSBuild/Tasks/ListQrc.cs @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="ListQrc" + +#region Reference +//System.Xml +//System.Xml.Linq +#endregion + +#region Using +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK ListQrc +///////////////////////////////////////////////////////////////////////////////////////////////// +// List resource paths in a QRC file. +// Parameters: +// in string QrcFilePath: path to QRC file +// out string[] Result: paths to files referenced in QRC +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class ListQrc + { + public static bool Execute( + #region Parameters + System.String QrcFilePath, + out System.String[] Result) + #endregion + { + #region Code + XDocument qrc = XDocument.Load(QrcFilePath, LoadOptions.SetLineInfo); + IEnumerable<XElement> files = qrc + .Element("RCC") + .Elements("qresource") + .Elements("file"); + Uri QrcPath = new Uri(QrcFilePath); + Result = files + .Select(x => new Uri(QrcPath, x.Value).LocalPath) + .ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/ParseVarDefs.cs b/QtMSBuild/Tasks/ParseVarDefs.cs new file mode 100644 index 0000000..77fa36a --- /dev/null +++ b/QtMSBuild/Tasks/ParseVarDefs.cs @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="ParseVarDefs" + +#region Using +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK ParseVarDefs +///////////////////////////////////////////////////////////////////////////////////////////////// +// +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class ParseVarDefs + { + public static bool Execute( + #region Parameters + System.String QtVars, + out Microsoft.Build.Framework.ITaskItem[] OutVarDefs + ) + #endregion + { + #region Code + OutVarDefs = Regex.Matches(QtVars, + @"\s*(\w+)\s*(?:;|=\s*(\w*)\s*(?:\/((?:\\.|[^;\/])*)\/((?:\\.|[^;\/])*)\/)?)?") + .Cast<Match>() + .Where((Match x) => x.Groups.Count > 4 && !string.IsNullOrEmpty(x.Groups[1].Value)) + .Select((Match x) => x.Groups + .Cast<Group>() + .Select((Group y) => !string.IsNullOrEmpty(y.Value) ? y.Value : null) + .ToArray()) + .Select((string[] x) => new TaskItem(x[1], + new Dictionary<string, string> + { + { "Name" , x[2] ?? x[1] }, + { "Pattern" , x[3] ?? ".*" }, + { "Value" , x[4] ?? "$0" }, + })) + .ToArray(); + #endregion + + return true; + } + } +} +#endregion diff --git a/QtMSBuild/Tasks/QtRunWork.cs b/QtMSBuild/Tasks/QtRunWork.cs new file mode 100644 index 0000000..d48dccf --- /dev/null +++ b/QtMSBuild/Tasks/QtRunWork.cs @@ -0,0 +1,320 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#region Task TaskName="QtRunWork" + +#region Reference +#endregion + +#region Using +using System; +using System.Diagnostics; +using System.Linq; +using System.Collections.Generic; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +#endregion + +#region Comment +///////////////////////////////////////////////////////////////////////////////////////////////// +/// TASK QtRunWork +///////////////////////////////////////////////////////////////////////////////////////////////// +// Run work items in parallel processes. +// Parameters: +// in ITaskItem[] QtWork: work items +// in int QtMaxProcs: maximum number of processes to run in parallel +// in bool QtDebug: generate debug messages +// out ITaskItem[] Result: list of new items with the result of each work item +#endregion + +namespace QtVsTools.QtMsBuild.Tasks +{ + public static class QtRunWork + { + public static QtMSBuild.ITaskLoggingHelper Log { get; set; } + + public static bool Execute( + #region Parameters + Microsoft.Build.Framework.ITaskItem[] QtWork, + System.Int32 QtMaxProcs, + System.Boolean QtDebug, + out Microsoft.Build.Framework.ITaskItem[] Result + ) + #endregion + { + #region Code + Result = new ITaskItem[] { }; + bool ok = true; + var Comparer = StringComparer.InvariantCultureIgnoreCase; + var Comparison = StringComparison.InvariantCultureIgnoreCase; + + // Work item key = "%(WorkType)(%(Identity))" + Func<string, string, string> KeyString = (x, y) => string.Format("{0}{{{1}}}", x, y); + Func<ITaskItem, string> Key = (item) => + KeyString(item.GetMetadata("WorkType"), item.ItemSpec); + var workItemKeys = new HashSet<string>(QtWork.Select(x => Key(x)), Comparer); + + // Work items, indexed by %(Identity) + var workItemsByIdentity = QtWork + .GroupBy(x => x.ItemSpec, x => Key(x), Comparer) + .ToDictionary(x => x.Key, x => new List<string>(x), Comparer); + + // Work items, indexed by work item key + var workItems = QtWork.Select(x => new + { + Self = x, + Key = Key(x), + ToolPath = x.GetMetadata("ToolPath"), + Message = x.GetMetadata("Message"), + DependsOn = new HashSet<string>(comparer: Comparer, + collection: x.GetMetadata("DependsOn") + .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) + .Where(y => workItemsByIdentity.ContainsKey(y)) + .SelectMany(y => workItemsByIdentity[y]) + .Union(x.GetMetadata("DependsOnWork") + .Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(y => KeyString(y, x.ItemSpec)) + .Where(y => workItemKeys.Contains(y))) + .GroupBy(y => y, Comparer).Select(y => y.Key) + .Where(y => !y.Equals(Key(x), Comparison))), + ProcessStartInfo = new ProcessStartInfo + { + FileName = x.GetMetadata("ToolPath"), + Arguments = x.GetMetadata("Options"), + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + }, + }) + // In case of items with duplicate keys, use only the first one + .GroupBy(x => x.Key, Comparer) + .ToDictionary(x => x.Key, x => x.First(), Comparer); + + // Result + var result = workItems.Values + .ToDictionary(x => x.Key, x => new TaskItem(x.Self)); + + // Dependency relation [item -> dependent items] + var dependentsOf = workItems.Values + .Where(x => x.DependsOn.Any()) + .SelectMany(x => x.DependsOn.Select(y => new { Dependent = x.Key, Dependency = y })) + .GroupBy(x => x.Dependency, x => x.Dependent, Comparer) + .ToDictionary(x => x.Key, x => new List<string>(x), Comparer); + + // Work items that are ready to start; initially queue all independent items + var workQueue = new Queue<string>(workItems.Values + .Where(x => !x.DependsOn.Any()) + .Select(x => x.Key)); + + if (QtDebug) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork queueing\r\n## {0}", + string.Join("\r\n## ", workQueue))); + } + + // Postponed items; save dependent items to queue later when ready + var postponedItems = new HashSet<string>(workItems.Values + .Where(x => x.DependsOn.Any()) + .Select(x => x.Key)); + + if (QtDebug && postponedItems.Any()) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork postponed dependents\r\n## {0}", + string.Join("\r\n## ", postponedItems + .Select(x => string.Format("{0} <- {1}", x, + string.Join(", ", workItems[x].DependsOn)))))); + } + + // Work items that are running; must synchronize with the exit of all processes + var running = new Queue<KeyValuePair<string, Process>>(); + + // Work items that have terminated + var terminated = new HashSet<string>(Comparer); + + // While there are work items queued, start a process for each item + while (ok && workQueue.Any()) { + + var workItem = workItems[workQueue.Dequeue()]; + Log.LogMessage(MessageImportance.High, workItem.Message); + + try { + var proc = Process.Start(workItem.ProcessStartInfo); + proc.OutputDataReceived += (object sender, DataReceivedEventArgs e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Log.LogMessage(MessageImportance.High, string.Join(" ", new[] + { + (QtDebug ? "[" + (((Process)sender).Id.ToString()) + "]" : ""), + e.Data + })); + }; + proc.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Log.LogMessage(MessageImportance.High, string.Join(" ", new[] + { + (QtDebug ? "[" + (((Process)sender).Id.ToString()) + "]" : ""), + e.Data + })); + }; + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + running.Enqueue(new KeyValuePair<string, Process>(workItem.Key, proc)); + } catch (Exception e) { + Log.LogError( + string.Format("[QtRunWork] Error starting process {0}: {1}", + workItem.ToolPath, e.Message)); + ok = false; + } + + string qtDebugRunning = ""; + if (QtDebug) { + qtDebugRunning = string.Format("## QtRunWork waiting {0}", + string.Join(", ", running + .Select(x => string.Format("{0} [{1}]", x.Key, x.Value.Id)))); + } + + // Wait for process to terminate when there are processes running, and... + while (ok && running.Any() + // ...work is queued but already reached the maximum number of processes, or... + && ((workQueue.Any() && running.Count >= QtMaxProcs) + // ...work queue is empty but there are dependents that haven't yet been queued + || (!workQueue.Any() && postponedItems.Any()))) { + + var itemProc = running.Dequeue(); + workItem = workItems[itemProc.Key]; + var proc = itemProc.Value; + + if (QtDebug && !string.IsNullOrEmpty(qtDebugRunning)) { + Log.LogMessage(MessageImportance.High, qtDebugRunning); + qtDebugRunning = ""; + } + + if (proc.WaitForExit(100)) { + if (QtDebug) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork exit {0} [{1}] = {2} ({3:0.00} msecs)", + workItem.Key, proc.Id, proc.ExitCode, + (proc.ExitTime - proc.StartTime).TotalMilliseconds)); + } + + // Process terminated; check exit code and close + terminated.Add(workItem.Key); + result[workItem.Key].SetMetadata("ExitCode", proc.ExitCode.ToString()); + ok &= (proc.ExitCode == 0); + proc.Close(); + + // Add postponed dependent items to work queue + if (ok && dependentsOf.ContainsKey(workItem.Key)) { + // Dependents of workItem... + var readyDependents = dependentsOf[workItem.Key] + // ...that have not yet been queued... + .Where(x => postponedItems.Contains(x) + // ...and whose dependending items have all terminated. + && workItems[x].DependsOn.All(y => terminated.Contains(y))); + + if (QtDebug && readyDependents.Any()) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork queueing\r\n## {0}", + string.Join("\r\n## ", readyDependents))); + } + + foreach (var dependent in readyDependents) { + postponedItems.Remove(dependent); + workQueue.Enqueue(dependent); + } + } + } else { + // Process is still running; feed it back into the running queue + running.Enqueue(itemProc); + } + } + } + + // If there are items still haven't been queued, that means a circular dependency exists + if (ok && postponedItems.Any()) { + ok = false; + Log.LogError("[QtRunWork] Error: circular dependency"); + if (QtDebug) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork circularity\r\n## {0}", + string.Join("\r\n## ", postponedItems + .Select(x => string.Format("{0} <- {1}", x, + string.Join(", ", workItems[x].DependsOn)))))); + } + } + + if (ok && QtDebug) { + Log.LogMessage(MessageImportance.High, + "## QtRunWork all work queued"); + if (running.Any()) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork waiting {0}", + string.Join(", ", running + .Select(x => string.Format("{0} [{1}]", x.Key, x.Value.Id))))); + } + } + + // Wait for all running processes to terminate + while (running.Any()) { + var itemProc = running.Dequeue(); + var workItem = workItems[itemProc.Key]; + var proc = itemProc.Value; + if (proc.WaitForExit(100)) { + if (QtDebug) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork exit {0} [{1}] = {2} ({3:0.00} msecs)", + workItem.Key, proc.Id, proc.ExitCode, + (proc.ExitTime - proc.StartTime).TotalMilliseconds)); + } + // Process terminated; check exit code and close + result[workItem.Key].SetMetadata("ExitCode", proc.ExitCode.ToString()); + ok &= (proc.ExitCode == 0); + proc.Close(); + } else { + // Process is still running; feed it back into the running queue + running.Enqueue(itemProc); + } + } + + if (QtDebug) { + Log.LogMessage(MessageImportance.High, + string.Format("## QtRunWork result {0}", (ok ? "ok" : "FAILED!"))); + } + + Result = result.Values.ToArray(); + if (!ok) + return false; + #endregion + + return true; + } + } +} +#endregion diff --git a/QtTmLanguage/qt/LICENSE b/QtTmLanguage/qt/LICENSE new file mode 100644 index 0000000..76115ee --- /dev/null +++ b/QtTmLanguage/qt/LICENSE @@ -0,0 +1,4 @@ +Permission to copy, use, modify, sell and distribute this +software is granted. This software is provided "as is" without +express or implied warranty, and with no claim as to its +suitability for any purpose. diff --git a/QtTmLanguage/qt/pri.pro.tmLanguage b/QtTmLanguage/qt/pri.pro.tmLanguage new file mode 100644 index 0000000..059a53c --- /dev/null +++ b/QtTmLanguage/qt/pri.pro.tmLanguage @@ -0,0 +1,346 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>fileTypes</key> + <array> + <string>pro</string> + <string>pri</string> + </array> + <key>keyEquivalent</key> + <string>^~Q</string> + <key>name</key> + <string>qmake Project file</string> + <key>patterns</key> + <array> + <dict> + <key>begin</key> + <string>(TEMPLATE)\s*(=)</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>variable.language.qmake</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>punctuation.separator.key-value.qmake</string> + </dict> + </dict> + <key>end</key> + <string>$\n?</string> + <key>name</key> + <string>markup.other.template.qmake</string> + <key>patterns</key> + <array> + <dict> + <key>match</key> + <string>\b(app|lib|subdirs|vcapp|vclib)\b</string> + <key>name</key> + <string>keyword.other.qmake</string> + </dict> + </array> + </dict> + <dict> + <key>begin</key> + <string>(CONFIG)\s*(\+|\-)?(=)</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>variable.language.qmake</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>punctuation.separator.key-value.qmake</string> + </dict> + </dict> + <key>end</key> + <string>$\n?</string> + <key>name</key> + <string>markup.other.config.qmake</string> + <key>patterns</key> + <array> + <dict> + <key>match</key> + <string>\b(release|debug|warn_(on|off)|qt|opengl|thread|x11|windows|console|dll|staticlib|plugin|designer|uic3|no_lflags_merge|exceptions|rtti|stl|flat|app_bundle|no_batch|qtestlib|ppc|x86)\b</string> + <key>name</key> + <string>keyword.other.qmake</string> + </dict> + </array> + </dict> + <dict> + <key>begin</key> + <string>(QT)\s*(\+|\-)?(=)</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>variable.language.qmake</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>punctuation.separator.key-value.qmake</string> + </dict> + </dict> + <key>end</key> + <string>$\n?</string> + <key>name</key> + <string>markup.other.qt.qmake</string> + <key>patterns</key> + <array> + <dict> + <key>match</key> + <string>\b(core|gui|network|opengl|sql|svg|xml|qt3support)\b</string> + <key>name</key> + <string>keyword.other.qmake</string> + </dict> + </array> + </dict> + <dict> + <key>match</key> + <string>\b(R(C(C_DIR|_FILE)|E(S_FILE|QUIRES))|M(OC_DIR|AKE(_MAKEFILE|FILE(_GENERATOR)?))|S(RCMOC|OURCES|UBDIRS)|HEADERS|YACC(SOURCES|IMPLS|OBJECTS)|CONFIG|T(RANSLATIONS|ARGET(_(EXT|\d+(\.\d+\.\d+)?))?)|INCLUDEPATH|OBJ(MOC|ECTS(_DIR)?)|D(SP_TEMPLATE|ISTFILES|E(STDIR(_TARGET)?|PENDPATH|F(_FILE|INES))|LLDESTDIR)|UI(C(IMPLS|OBJECTS)|_(SOURCES_DIR|HEADERS_DIR|DIR))|P(RE(COMPILED_HEADER|_TARGETDEPS)|OST_TARGETDEPS)|V(PATH|ER(SION|_(M(IN|AJ)|PAT)))|Q(MAKE(SPEC|_(RUN_C(XX(_IMP)?|C(_IMP)?)|MOC_SRC|C(XXFLAGS_(RELEASE|MT(_D(BG|LL(DBG)?))?|SHLIB|THREAD|DEBUG|WARN_O(N|FF))|FLAGS_(RELEASE|MT(_D(BG|LL(DBG)?))?|SHLIB|THREAD|DEBUG|WARN_O(N|FF))|LEAN)|TARGET|IN(CDIR(_(X|THREAD|OPENGL|QT))?|FO_PLIST)|UIC|P(RE_LINK|OST_LINK)|EXT(_(MOC|H|CPP|YACC|OBJ|UI|PRL|LEX)|ENSION_SHLIB)|Q(MAKE|T_DLL)|F(ILETAGS|AILED_REQUIREMENTS)|L(N_SHLIB|I(B(S(_(RT(MT)?|X|CONSOLE|THREAD|OPENGL(_QT)?|QT(_(OPENGL|DLL))?|WINDOWS))?|_FLAG|DIR(_(X|OPENGL|QT|FLAGS))?)|NK_SHLIB_CMD)|FLAGS(_(RELEASE|S(H(LIB|APP)|ONAME)|CONSOLE(_DLL)?|THREAD|DEBUG|PLUGIN|QT_DLL|WINDOWS(_DLL)?))?)|A(R_CMD|PP_(OR_DLL|FLAG))))?|T_THREAD)|FORMS|L(IBS|EX(SOURCES|IMPLS|OBJECTS)))\b</string> + <key>name</key> + <string>variable.language.qmake</string> + </dict> + <dict> + <key>begin</key> + <string>(\b([\w\d_]+\.[\w\d_]+|[A-Z_]+))?\s*(\+|\-)?(=)</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>variable.other.qmake</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>punctuation.separator.key-value.qmake</string> + </dict> + </dict> + <key>end</key> + <string>$\n?</string> + <key>name</key> + <string>markup.other.assignment.qmake</string> + <key>patterns</key> + <array> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>punctuation.definition.variable.qmake</string> + </dict> + </dict> + <key>match</key> + <string>(\$\$)([A-Z_]+|[\w\d_]+\.[\w\d_]+)|\$\([\w\d_]+\)</string> + <key>name</key> + <string>variable.other.qmake</string> + </dict> + <dict> + <key>match</key> + <string>[\w\d\/_\-\.\:]+</string> + <key>name</key> + <string>constant.other.filename.qmake</string> + </dict> + <dict> + <key>begin</key> + <string>"</string> + <key>beginCaptures</key> + <dict> + <key>0</key> + <dict> + <key>name</key> + <string>punctuation.definition.string.begin.qmake</string> + </dict> + </dict> + <key>end</key> + <string>"</string> + <key>endCaptures</key> + <dict> + <key>0</key> + <dict> + <key>name</key> + <string>punctuation.definition.string.end.qmake</string> + </dict> + </dict> + <key>name</key> + <string>string.quoted.double.qmake</string> + </dict> + <dict> + <key>begin</key> + <string>`</string> + <key>beginCaptures</key> + <dict> + <key>0</key> + <dict> + <key>name</key> + <string>punctuation.definition.string.begin.qmake</string> + </dict> + </dict> + <key>end</key> + <string>`</string> + <key>endCaptures</key> + <dict> + <key>0</key> + <dict> + <key>name</key> + <string>punctuation.definition.string.end.qmake</string> + </dict> + </dict> + <key>name</key> + <string>string.interpolated.qmake</string> + </dict> + <dict> + <key>begin</key> + <string>(\\)</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>string.regexp.qmake</string> + </dict> + </dict> + <key>end</key> + <string>^[^#]</string> + <key>name</key> + <string>markup.other.assignment.continuation.qmake</string> + <key>patterns</key> + <array> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>punctuation.definition.comment.qmake</string> + </dict> + </dict> + <key>match</key> + <string>(#).*$\n?</string> + <key>name</key> + <string>comment.line.number-sign.qmake</string> + </dict> + </array> + </dict> + <dict> + <key>begin</key> + <string>(^[ \t]+)?(?=#)</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>punctuation.whitespace.comment.leading.qmake</string> + </dict> + </dict> + <key>end</key> + <string>(?!\G)</string> + <key>patterns</key> + <array> + <dict> + <key>begin</key> + <string>#</string> + <key>beginCaptures</key> + <dict> + <key>0</key> + <dict> + <key>name</key> + <string>punctuation.definition.comment.qmake</string> + </dict> + </dict> + <key>end</key> + <string>\n</string> + <key>name</key> + <string>comment.line.number-sign.qmake</string> + </dict> + </array> + </dict> + </array> + </dict> + <dict> + <key>begin</key> + <string>\b(basename|CONFIG|contains|count|dirname|error|exists|find|for|include|infile|isEmpty|join|member|message|prompt|quote|sprintf|system|unique|warning)\s*(\()</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>entity.name.function.qmake</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>punctuation.definition.parameters.qmake</string> + </dict> + </dict> + <key>comment</key> + <string>entity.name.function.qmake</string> + <key>contentName</key> + <string>variable.parameter.qmake</string> + <key>end</key> + <string>(\))</string> + <key>endCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>punctuation.definition.parameters.qmake</string> + </dict> + </dict> + </dict> + <dict> + <key>match</key> + <string>\b(unix|win32|mac|debug|release)\b</string> + <key>name</key> + <string>keyword.other.scope.qmake</string> + </dict> + <dict> + <key>begin</key> + <string>(^[ \t]+)?(?=#)</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>punctuation.whitespace.comment.leading.qmake</string> + </dict> + </dict> + <key>end</key> + <string>(?!\G)</string> + <key>patterns</key> + <array> + <dict> + <key>begin</key> + <string>#</string> + <key>beginCaptures</key> + <dict> + <key>0</key> + <dict> + <key>name</key> + <string>punctuation.definition.comment.qmake</string> + </dict> + </dict> + <key>end</key> + <string>\n</string> + <key>name</key> + <string>comment.line.number-sign.qmake</string> + </dict> + </array> + </dict> + </array> + <key>scopeName</key> + <string>source.qmake</string> + <key>uuid</key> + <string>3D54A8F9-17CA-422A-A1D6-DE5F98B9DEF4</string> + </dict> +</plist> diff --git a/QtVsTest/Macro.cs b/QtVsTest/Macro.cs new file mode 100644 index 0000000..1c1004d --- /dev/null +++ b/QtVsTest/Macro.cs @@ -0,0 +1,1049 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Automation; +using Microsoft.CSharp; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Threading; + +using Task = System.Threading.Tasks.Task; + +namespace QtVsTest.Macros +{ + /// <summary> + /// Macros are snippets of C# code provided by a test client at runtime. They are compiled + /// on-the-fly and may run once after compilation or stored and reused later by other macros. + /// Macros may also include special statements in comment lines starting with '//#'. These will + /// be expanded into the corresponding code ahead of C# compilation. + /// </summary> + class Macro + { + /// <summary> + /// Global variable, shared between macros + /// </summary> + class GlobalVar + { + public string Name { get; set; } + public string Type { get; set; } + public string InitialValueExpr { get; set; } + public FieldInfo FieldInfo { get; set; } + public PropertyInfo InitInfo { get; set; } + public bool IsCallOutput { get; set; } + } + + /// <summary> + /// Reference to Visual Studio SDK service + /// </summary> + class VSServiceRef + { + public string Name { get; set; } + public string Interface { get; set; } + public string Type { get; set; } + public FieldInfo RefVar { get; set; } + public Type ServiceType { get; set; } + } + + /// <summary> + /// Name of reusable macro + /// </summary> + public string Name { get; private set; } + + /// <summary> + /// True if macro compilation was successful + /// </summary> + public bool Ok { get; private set; } + + /// <summary> + /// Result of macro compilation and execution + /// </summary> + public string Result { get; private set; } + + /// <summary> + /// True if macro will run immediately after compilation + /// </summary> + public bool AutoRun { get; private set; } + + /// <summary> + /// True if Visual Studio should be closed after macro execution + /// </summary> + public bool QuitWhenDone { get; private set; } + + AsyncPackage Package { get; set; } + EnvDTE80.DTE2 Dte { get; set; } + + AutomationElement UiRoot => AutomationElement.RootElement; + + AutomationElement _UiVsRoot; + AutomationElement UiVsRoot + { + get + { + if (_UiVsRoot == null) +#if VS2022 + _UiVsRoot = AutomationElement.FromHandle(Dte.MainWindow.HWnd); +#else + _UiVsRoot = AutomationElement.FromHandle(new IntPtr(Dte.MainWindow.HWnd)); +#endif + return _UiVsRoot; + } + } + + JoinableTaskFactory JoinableTaskFactory { get; set; } + CancellationToken ServerLoop { get; set; } + + string Message { get; set; } + + static MacroParser Parser { get; set; } + MacroLines MacroLines { get; set; } + + List<string> SelectedAssemblies { get { return _SelectedAssemblies; } } + List<string> _SelectedAssemblies = + new List<string> + { + "QtVsTest", + "System.Core", + }; + + IEnumerable<string> RefAssemblies { get; set; } + + List<string> Namespaces { get { return _Namespaces; } } + List<string> _Namespaces = + new List<string> + { + "System", + "System.Linq", + "System.Reflection", + "Task = System.Threading.Tasks.Task", + "System.Windows.Automation", + "EnvDTE", + "EnvDTE80", + }; + + Dictionary<string, VSServiceRef> ServiceRefs { get { return _ServiceRefs; } } + Dictionary<string, VSServiceRef> _ServiceRefs = + new Dictionary<string, VSServiceRef> + { + { + "Dte", new VSServiceRef + { Name = "Dte", Interface = "DTE2", Type = "DTE" } + }, + }; + + Dictionary<string, GlobalVar> GlobalVars { get { return _GlobalVars; } } + Dictionary<string, GlobalVar> _GlobalVars = + new Dictionary<string, GlobalVar> + { + { + "Result", new GlobalVar + { Type = "string", Name = "Result", InitialValueExpr = "string.Empty" } + }, + }; + + string CSharpMethodCode { get; set; } + string CSharpClassCode { get; set; } + + CompilerResults CompilerResults { get; set; } + Assembly MacroAssembly { get; set; } + Type MacroClass { get; set; } + FieldInfo ResultField { get; set; } + Func<Task> Run { get; set; } + + const BindingFlags PUBLIC_STATIC = BindingFlags.Public | BindingFlags.Static; + const StringComparison IGNORE_CASE = StringComparison.InvariantCultureIgnoreCase; + + static ConcurrentDictionary<string, Macro> Macros + = new ConcurrentDictionary<string, Macro>(); + + static ConcurrentDictionary<string, object> Globals + = new ConcurrentDictionary<string, object>(); + + /// <summary> + /// Macro constructor + /// </summary> + /// <param name="package">QtVSTest extension package</param> + /// <param name="joinableTaskFactory">Task factory, enables joining with UI thread</param> + /// <param name="serverLoop">Server loop cancellation token</param> + public Macro( + AsyncPackage package, + EnvDTE80.DTE2 dte, + JoinableTaskFactory joinableTaskFactory, + CancellationToken serverLoop) + { + Package = package; + JoinableTaskFactory = joinableTaskFactory; + ServerLoop = serverLoop; + Dte = dte; + ErrorMsg("Uninitialized"); + } + + /// <summary> + /// Compile macro code + /// </summary> + /// <param name="msg">Message from client containing macro code</param> + public async Task<bool> CompileAsync(string msg) + { + if (MacroLines != null) + return Warning("Macro already compiled"); + + try { + Message = msg; + + if (!ParseMessage()) + return false; + + if (!CompileMacro()) + return false; + + if (!CompileClass()) + return false; + + await GetServicesAsync(); + + return true; + } catch (Exception e) { + return ErrorException(e); + } + } + + /// <summary> + /// Run macro + /// </summary> + public async Task RunAsync() + { + if (!Ok) + return; + + try { + InitGlobals(); + await Run(); + await SwitchToWorkerThreadAsync(); + Result = ResultField.GetValue(null) as string; + if (string.IsNullOrEmpty(Result)) + Result = MACRO_OK; + } catch (Exception e) { + ErrorException(e); + } + UpdateGlobals(); + } + + /// <summary> + /// Parse message text into sequence of macro statements + /// </summary> + /// <returns></returns> + bool ParseMessage() + { + if (Parser == null) { + var parser = MacroParser.Get(); + if (parser == null) + return ErrorMsg("Parser error"); + Parser = parser; + } + + var macroLines = Parser.Parse(Message); + if (macroLines == null) + return ErrorMsg("Parse error"); + + MacroLines = macroLines; + + return NoError(); + } + + /// <summary> + /// Expand macro statements into C# code + /// </summary> + /// <returns></returns> + bool CompileMacro() + { + if (UiVsRoot == null) + return ErrorMsg("UI Automation not available"); + + var csharp = new StringBuilder(); + + foreach (var line in MacroLines) { + if (QuitWhenDone) + return ErrorMsg("No code allowed after #quit"); + + if (line is CodeLine) { + var codeLine = line as CodeLine; + csharp.Append(codeLine.Code + "\r\n"); + continue; + } + + if (!GenerateStatement(line as Statement, csharp)) + return false; + } + + if (csharp.Length > 0) + CSharpMethodCode = csharp.ToString(); + + AutoRun = string.IsNullOrEmpty(Name); + if (AutoRun) + Name = "Macro_" + Path.GetRandomFileName().Replace(".", ""); + else if (!SaveMacro(Name)) + return ErrorMsg("Macro already defined"); + + foreach (var sv in ServiceRefs.Values.Where(x => string.IsNullOrEmpty(x.Type))) + sv.Type = sv.Interface; + + var selectedAssemblyNames = SelectedAssemblies + .Select(x => new AssemblyName(x)) + .GroupBy(x => x.FullName) + .Select(x => x.First()); + + var allAssemblies = AppDomain.CurrentDomain.GetAssemblies() + .GroupBy(x => x.GetName().Name) + .ToDictionary(x => x.Key, x => x.AsEnumerable(), + StringComparer.InvariantCultureIgnoreCase); + + var refAssemblies = selectedAssemblyNames + .GroupBy(x => allAssemblies.ContainsKey(x.Name)) + .SelectMany(x => x.Key + ? x.SelectMany(y => allAssemblies[y.Name]) + : x.Select(y => + { + try { + return Assembly.Load(y); + } catch { + return null; + } + })); + + RefAssemblies = refAssemblies + .Where(x => x != null) + .Select(x => x.Location); + + return NoError(); + } + + bool GenerateStatement(Statement s, StringBuilder csharp) + { + switch (s.Type) { + + case StatementType.Quit: + QuitWhenDone = true; + break; + + case StatementType.Macro: + if (csharp.Length > 0) + return ErrorMsg("#macro must be first statement"); + if (!string.IsNullOrEmpty(Name)) + return ErrorMsg("Only one #macro statement allowed"); + if (s.Args.Count < 1) + return ErrorMsg("Missing macro name"); + Name = s.Args[0]; + break; + + case StatementType.Thread: + if (s.Args.Count < 1) + return ErrorMsg("Missing thread id"); + if (s.Args[0].Equals("ui", IGNORE_CASE)) { + + csharp.Append( +/** BEGIN generate code **/ +@" + await SwitchToUIThread();" +/** END generate code **/ ); + + } else if (s.Args[0].Equals("default", IGNORE_CASE)) { + + csharp.Append( +/** BEGIN generate code **/ +@" + await SwitchToWorkerThread();" +/** END generate code **/ ); + + } else { + return ErrorMsg("Unknown thread id"); + } + break; + + case StatementType.Reference: + if (!s.Args.Any()) + return ErrorMsg("Missing args for #reference"); + SelectedAssemblies.Add(s.Args.First()); + foreach (var ns in s.Args.Skip(1)) + Namespaces.Add(ns); + break; + + case StatementType.Using: + if (!s.Args.Any()) + return ErrorMsg("Missing args for #using"); + foreach (var ns in s.Args) + Namespaces.Add(ns); + break; + + case StatementType.Var: + if (s.Args.Count < 1) + return ErrorMsg("Missing args for #var"); + string typeName, varName; + if (s.Args.Count == 1) { + typeName = "object"; + varName = s.Args[0]; + } else { + typeName = s.Args[0]; + varName = s.Args[1]; + } + var initValue = s.Code; + if (varName.Where(c => char.IsWhiteSpace(c)).Any()) + return ErrorMsg("Wrong var name"); + GlobalVars[varName] = new GlobalVar + { + Type = typeName, + Name = varName, + InitialValueExpr = initValue + }; + break; + + case StatementType.Service: + if (s.Args.Count <= 1) + return ErrorMsg("Missing args for #service"); + var serviceVarName = s.Args[0]; + if (serviceVarName.Where(c => char.IsWhiteSpace(c)).Any()) + return ErrorMsg("Invalid service var name"); + if (ServiceRefs.ContainsKey(serviceVarName)) + return ErrorMsg("Duplicate service var name"); + ServiceRefs.Add(serviceVarName, new VSServiceRef + { + Name = serviceVarName, + Interface = s.Args[1], + Type = s.Args.Count > 2 ? s.Args[2] : s.Args[1] + }); + break; + + case StatementType.Call: + if (s.Args.Count < 1) + return ErrorMsg("Missing args for #call"); + var calleeName = s.Args[0]; + var callee = GetMacro(calleeName); + if (callee == null) + return ErrorMsg("Undefined macro"); + + csharp.AppendFormat( +/** BEGIN generate code **/ +@" + await CallMacro(""{0}"");" +/** END generate code **/ , calleeName); + + foreach (var globalVar in callee.GlobalVars.Values) { + if (GlobalVars.ContainsKey(globalVar.Name)) + continue; + GlobalVars[globalVar.Name] = new GlobalVar + { + Type = globalVar.Type, + Name = globalVar.Name, + IsCallOutput = true + }; + } + break; + + case StatementType.Wait: + if (string.IsNullOrEmpty(s.Code)) + return ErrorMsg("Missing args for #wait"); + var expr = s.Code; + uint timeout = uint.MaxValue; + if (s.Args.Count > 0 && !uint.TryParse(s.Args[0], out timeout)) + return ErrorMsg("Timeout format error in #wait"); + if (s.Args.Count > 2) { + var evalVarType = s.Args[1]; + var evalVarName = s.Args[2]; + + csharp.AppendFormat( +/** BEGIN generate code **/ +@" + {0} {1} = default({0}); + await WaitExpr({2}, () => {1} = {3});" +/** END generate code **/ , evalVarType, + evalVarName, + timeout, + expr); + + } else { + + csharp.AppendFormat( +/** BEGIN generate code **/ +@" + await WaitExpr({0}, () => {1});" +/** END generate code **/ , timeout, + expr); + + } + break; + + case StatementType.Ui: + if (!GenerateUiStatement(s, csharp)) + return false; + break; + } + return true; + } + + public AutomationElement UiFind(AutomationElement uiContext, object[] path) + { + var uiIterator = uiContext; + foreach (var item in path) { + var itemType = item.GetType(); + var scope = (uiIterator == UiRoot) ? TreeScope.Children : TreeScope.Subtree; + if (itemType.IsAssignableFrom(typeof(string))) { + // Find element by name + var name = (string)item; + uiIterator = uiIterator.FindFirst(scope, + new PropertyCondition(AutomationElement.NameProperty, name)); + } else if (itemType.IsAssignableFrom(typeof(string[]))) { + // Find element by name and type + var itemParams = (string[])item; + uiIterator = uiIterator.FindFirst(scope, + new AndCondition(itemParams.Select((x, i) => + (i == 0) ? new PropertyCondition( + AutomationElement.NameProperty, x) : + (i == 1) ? new PropertyCondition( + AutomationElement.LocalizedControlTypeProperty, x) : + (i == 2) ? new PropertyCondition( + AutomationElement.AutomationIdProperty, x) : + Condition.FalseCondition).ToArray())); + } + if (uiIterator == null) + throw new Exception( + string.Format("Could not find UI element \"{0}\"", item)); + } + return uiIterator; + } + + static readonly IEnumerable<string> UI_TYPES = new[] + { + "Dock", "ExpandCollapse", "GridItem", "Grid", "Invoke", "MultipleView", "RangeValue", + "Scroll", "ScrollItem", "Selection", "SelectionItem", "SynchronizedInput", "Text", + "Transform", "Toggle", "Value", "Window", "VirtualizedItem", "ItemContainer" + }; + + bool GenerateUiGlobals(StringBuilder csharp) + { + csharp.Append(@" + public static Func<AutomationElement, object[], AutomationElement> UiFind; + public static AutomationElement UiRoot; + public static AutomationElement UiVsRoot; + public static AutomationElement UiContext;"); + return true; + } + + bool InitializeUiGlobals() + { + if (MacroClass == null) + return false; + + MacroClass.GetField("UiFind", PUBLIC_STATIC) + .SetValue(null, new Func<AutomationElement, object[], AutomationElement>(UiFind)); + + MacroClass.GetField("UiRoot", PUBLIC_STATIC) + .SetValue(null, UiRoot); + + MacroClass.GetField("UiVsRoot", PUBLIC_STATIC) + .SetValue(null, UiVsRoot); + + MacroClass.GetField("UiContext", PUBLIC_STATIC) + .SetValue(null, UiVsRoot); + + return true; + } + + bool GenerateUiStatement(Statement s, StringBuilder csharp) + { + if (s.Args.Count == 0) + return ErrorMsg("Invalid #ui statement"); + + if (s.Args[0].Equals("context", IGNORE_CASE)) { + //# ui context [ VSROOT | DESKTOP ] [_int_] => _string_ [, _string_, ... ] + //# ui context HWND [_int_] => _int_ + + if (s.Args.Count > 3 || string.IsNullOrEmpty(s.Code)) + return ErrorMsg("Invalid #ui statement"); + + bool uiVsRoot = (s.Args.Count > 1 && s.Args[1] == "VSROOT"); + bool uiDesktop = (s.Args.Count > 1 && s.Args[1] == "DESKTOP"); + bool uiHwnd = (s.Args.Count > 1 && s.Args[1] == "HWND"); + + string context; + if (uiVsRoot) + context = string.Format("UiFind(UiVsRoot, new object[] {{ {0} }})", s.Code); + else if (uiDesktop) + context = string.Format("UiFind(UiRoot, new object[] {{ {0} }})", s.Code); + else if (uiHwnd) + context = string.Format("AutomationElement.FromHandle((IntPtr)({0}))", s.Code); + else + context = string.Format("UiFind(UiContext, new object[] {{ {0} }})", s.Code); + + int timeout = 3000; + if (s.Args.Count > 1 && !uiVsRoot && !uiDesktop && !uiHwnd) + timeout = int.Parse(s.Args[1]); + else if (s.Args.Count > 2) + timeout = int.Parse(s.Args[2]); + + csharp.AppendFormat(@" + await WaitExpr({0}, () => UiContext = {1});", timeout, context); + + } else if (s.Args[0].Equals("pattern", IGNORE_CASE)) { + //# ui pattern <_TypeName_> <_VarName_> [ => _string_ [, _string_, ... ] ] + //# ui pattern Invoke [ => _string_ [, _string_, ... ] ] + //# ui pattern Toggle [ => _string_ [, _string_, ... ] ] + + if (s.Args.Count < 2) + return ErrorMsg("Invalid #ui statement"); + + string typeName = s.Args[1]; + string varName = (s.Args.Count > 2) ? s.Args[2] : string.Empty; + if (!UI_TYPES.Contains(typeName)) + return ErrorMsg("Invalid #ui statement"); + + string uiElement; + if (!string.IsNullOrEmpty(s.Code)) + uiElement = string.Format("UiFind(UiContext, new object[] {{ {0} }})", s.Code); + else + uiElement = "UiContext"; + + string patternTypeId = string.Format("{0}PatternIdentifiers.Pattern", typeName); + string patternType = string.Format("{0}Pattern", typeName); + + if (!string.IsNullOrEmpty(varName)) { + + csharp.AppendFormat(@" + var {0} = {1}.GetCurrentPattern({2}) as {3};", + varName, + uiElement, + patternTypeId, + patternType); + + } else if (typeName == "Invoke" || typeName == "Toggle") { + + csharp.AppendFormat(@" + ({0}.GetCurrentPattern({1}) as {2}).{3}();", + uiElement, + patternTypeId, + patternType, + typeName); + + } else { + return ErrorMsg("Invalid #ui statement"); + } + + } else { + return ErrorMsg("Invalid #ui statement"); + } + + return true; + } + + const string SERVICETYPE_PREFIX = "_ServiceType_"; + const string INIT_PREFIX = "_Init_"; + string MethodName { get { return string.Format("_Run_{0}_Async", Name); } } + + bool GenerateClass() + { + var csharp = new StringBuilder(); + foreach (var ns in Namespaces) { + csharp.AppendFormat( +/** BEGIN generate code **/ +@" +using {0};" +/** END generate code **/ , ns); + } + + csharp.AppendFormat( +/** BEGIN generate code **/ +@" +namespace QtVsTest.Macros +{{ + public class {0} + {{" +/** END generate code **/ , Name); + + foreach (var serviceRef in ServiceRefs.Values) { + csharp.AppendFormat( +/** BEGIN generate code **/ +@" + public static {2} {1}; + public static readonly Type {0}{1} = typeof({3});" +/** END generate code **/ , SERVICETYPE_PREFIX, + serviceRef.Name, + serviceRef.Interface, + serviceRef.Type); + } + + foreach (var globalVar in GlobalVars.Values) { + csharp.AppendFormat( +/** BEGIN generate code **/ +@" + public static {1} {2}; + public static {1} {0}{2} {{ get {{ return ({3}); }} }}" +/** END generate code **/ , INIT_PREFIX, + globalVar.Type, + globalVar.Name, + !string.IsNullOrEmpty(globalVar.InitialValueExpr) + ? globalVar.InitialValueExpr + : string.Format("default({0})", globalVar.Type)); + } + + csharp.Append( +/** BEGIN generate code **/ +@" + static string MACRO_OK { get { return ""(ok)""; } } + static string MACRO_ERROR { get { return ""(error)""; } } + static string MACRO_WARN { get { return ""(warn)""; } } + static string MACRO_ERROR_MSG(string msg) + { return string.Format(""{0}\r\n{1}"", MACRO_ERROR, msg); } + static string MACRO_WARN_MSG(string msg) + { return string.Format(""{0}\r\n{1}"", MACRO_WARN, msg); } + public static Func<string, Assembly> GetAssembly; + public static Func<Task> SwitchToUIThread; + public static Func<Task> SwitchToWorkerThread; + public static Func<string, Task> CallMacro; + public static Func<int, Func<object>, Task> WaitExpr;" +/** END generate code **/ ); + + if (!GenerateResultFuncs(csharp)) + return false; + + if (!GenerateUiGlobals(csharp)) + return false; + + csharp.AppendFormat( +/** BEGIN generate code **/ +@" + public static async Task {0}() + {{ +{1} + }} + + }} /*class*/ + +}} /*namespace*/" +/** END generate code **/ , MethodName, + CSharpMethodCode); + + CSharpClassCode = csharp.ToString(); + + return true; + } + + /// <summary> + /// Generate and compile C# class for macro + /// </summary> + /// <returns></returns> + bool CompileClass() + { + if (!GenerateClass()) + return false; + + var dllUri = new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase); + var dllPath = Uri.UnescapeDataString(dllUri.AbsolutePath); + var macroDllPath = Path.Combine(Path.GetDirectoryName(dllPath), Name + ".dll"); + + if (File.Exists(macroDllPath)) + File.Delete(macroDllPath); + + var cscParams = new CompilerParameters() + { + GenerateInMemory = false, + OutputAssembly = macroDllPath + }; + cscParams.ReferencedAssemblies.AddRange(RefAssemblies.ToArray()); + + var cSharpProvider = new CSharpCodeProvider(); + + CompilerResults = cSharpProvider.CompileAssemblyFromSource(cscParams, CSharpClassCode); + if (CompilerResults.Errors.Count > 0) { + if (File.Exists(macroDllPath)) + File.Delete(macroDllPath); + return ErrorMsg(string.Join("\r\n", + CompilerResults.Errors.Cast<CompilerError>() + .Select(x => x.ErrorText))); + } + + MacroAssembly = AppDomain.CurrentDomain.Load(File.ReadAllBytes(macroDllPath)); + MacroClass = MacroAssembly.GetType(string.Format("QtVsTest.Macros.{0}", Name)); + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + + if (File.Exists(macroDllPath)) + File.Delete(macroDllPath); + + foreach (var serviceVar in ServiceRefs.Values) { + serviceVar.RefVar = MacroClass.GetField(serviceVar.Name, PUBLIC_STATIC); + var serviceType = MacroClass.GetField(SERVICETYPE_PREFIX + serviceVar.Name, PUBLIC_STATIC); + serviceVar.ServiceType = (Type)serviceType.GetValue(null); + } + + ResultField = MacroClass.GetField("Result", PUBLIC_STATIC); + foreach (var globalVar in GlobalVars.Values) { + globalVar.FieldInfo = MacroClass.GetField(globalVar.Name, PUBLIC_STATIC); + if (!globalVar.IsCallOutput) { + globalVar.InitInfo = MacroClass + .GetProperty(INIT_PREFIX + globalVar.Name, PUBLIC_STATIC); + } + } + + Run = (Func<Task>)Delegate.CreateDelegate(typeof(Func<Task>), + MacroClass.GetMethod(MethodName, PUBLIC_STATIC)); + + MacroClass.GetField("GetAssembly", PUBLIC_STATIC) + .SetValue(null, new Func<string, Assembly>(GetAssembly)); + + MacroClass.GetField("SwitchToUIThread", PUBLIC_STATIC) + .SetValue(null, new Func<Task>(SwitchToUIThreadAsync)); + + MacroClass.GetField("SwitchToWorkerThread", PUBLIC_STATIC) + .SetValue(null, new Func<Task>(SwitchToWorkerThreadAsync)); + + MacroClass.GetField("CallMacro", PUBLIC_STATIC) + .SetValue(null, new Func<string, Task>(CallMacroAsync)); + + MacroClass.GetField("WaitExpr", PUBLIC_STATIC) + .SetValue(null, new Func<int, Func<object>, Task>(WaitExprAsync)); + + if (!InitializeUiGlobals()) + return false; + + return NoError(); + } + + Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + if (args.RequestingAssembly == null || args.RequestingAssembly != MacroAssembly) + return null; + var fullName = new AssemblyName(args.Name); + var assemblyPath = RefAssemblies + .Where(x => Path.GetFileNameWithoutExtension(x).Equals(fullName.Name, IGNORE_CASE)) + .FirstOrDefault(); + if (string.IsNullOrEmpty(assemblyPath)) + return null; + if (!File.Exists(assemblyPath)) + return null; + return Assembly.LoadFrom(assemblyPath); + } + + public static Assembly GetAssembly(string name) + { + return AppDomain.CurrentDomain.GetAssemblies() + .Where(x => x.GetName().Name == name) + .FirstOrDefault(); + } + + public async Task SwitchToUIThreadAsync() + { + await JoinableTaskFactory.SwitchToMainThreadAsync(ServerLoop); + } + + public async Task SwitchToWorkerThreadAsync() + { + await TaskScheduler.Default; + } + + public async Task CallMacroAsync(string macroName) + { + var callee = GetMacro(macroName); + if (callee == null) + throw new FileNotFoundException("Unknown macro"); + + callee.InitGlobals(); + await callee.Run(); + callee.UpdateGlobals(); + + // Refresh caller local copies of globals + InitGlobals(); + } + + public async Task WaitExprAsync(int timeout, Func<object> expr) + { + var tMax = TimeSpan.FromMilliseconds(timeout); + var tRemaining = tMax; + var t = Stopwatch.StartNew(); + object value; + try { + value = await Task.Run(() => expr()).WithTimeout(tRemaining); + } catch { + value = null; + } + bool ok = !IsDefaultValue(value); + + while (!ok && (tRemaining = (tMax - t.Elapsed)) > TimeSpan.Zero) { + await Task.Delay(10); + try { + value = await Task.Run(() => expr()).WithTimeout(tRemaining); + } catch { + value = null; + } + ok = !IsDefaultValue(value); + } + + if (!ok) + throw new TimeoutException(); + } + + bool IsDefaultValue(object obj) + { + if (obj == null) + return true; + else if (obj.GetType().IsValueType) + return obj.Equals(Activator.CreateInstance(obj.GetType())); + else + return false; + } + + void InitGlobals() + { + foreach (var globalVar in GlobalVars.Values) { + string varName = globalVar.Name; + Type varType = globalVar.FieldInfo.FieldType; + object value; + if (Globals.TryGetValue(varName, out value)) { + Type valueType = value.GetType(); + if (!varType.IsAssignableFrom(valueType)) { + throw new InvalidCastException(string.Format( + "Global variable '{0}': cannot assign '{1}' from '{2}'", + varName, varType.Name, valueType.Name)); + } + globalVar.FieldInfo.SetValue(null, value); + } else { + globalVar.FieldInfo.SetValue(null, globalVar.InitInfo.GetValue(null)); + } + } + } + + void UpdateGlobals() + { + foreach (var globalVar in GlobalVars.Values) { + object value = globalVar.FieldInfo.GetValue(null); + Globals.AddOrUpdate(globalVar.Name, value, (key, oldValue) => value); + } + } + + async Task<bool> GetServicesAsync() + { + foreach (var serviceRef in ServiceRefs.Values.Where(x => x.RefVar != null)) { + serviceRef.RefVar.SetValue(null, + await Package.GetServiceAsync(serviceRef.ServiceType)); + } + return await Task.FromResult(NoError()); + } + + bool SaveMacro(string name) + { + if (Macros.ContainsKey(name)) + return false; + return Macros.TryAdd(Name = name, this); + } + + static Macro GetMacro(string name) + { + Macro macro; + if (!Macros.TryGetValue(name, out macro)) + return null; + return macro; + } + + public static void Reset() + { + Macros.Clear(); + Globals.Clear(); + } + + bool GenerateResultFuncs(StringBuilder csharp) + { + csharp.Append( +/** BEGIN generate code **/ +@" + public static string Ok; + public static string Error; + public static Func<string, string> ErrorMsg;" +/** END generate code **/ ); + return true; + } + + bool InitializeResultFuncs() + { + if (MacroClass == null) + return false; + + MacroClass.GetField("Ok", PUBLIC_STATIC) + .SetValue(null, MACRO_OK); + + MacroClass.GetField("Error", PUBLIC_STATIC) + .SetValue(null, MACRO_ERROR); + + MacroClass.GetField("ErrorMsg", PUBLIC_STATIC) + .SetValue(null, new Func<string, string>(MACRO_ERROR_MSG)); + + return true; + } + + string MACRO_OK { get { return "(ok)"; } } + string MACRO_ERROR { get { return "(error)"; } } + string MACRO_WARN { get { return "(warn)"; } } + string MACRO_ERROR_MSG(string msg) { return string.Format("{0}\r\n{1}", MACRO_ERROR, msg); } + string MACRO_WARN_MSG(string msg) { return string.Format("{0}\r\n{1}", MACRO_WARN, msg); } + + bool NoError() + { + Result = MACRO_OK; + return (Ok = true); + } + + bool Error() + { + Result = MACRO_ERROR; + return (Ok = false); + } + + bool ErrorMsg(string errorMsg) + { + Result = MACRO_ERROR_MSG(errorMsg); + return (Ok = false); + } + + bool ErrorException(Exception e) + { + Result = MACRO_ERROR_MSG(string.Format("{0}\r\n\"{1}\"\r\n{2}", + e.GetType().Name, e.Message, e.StackTrace)); + return (Ok = false); + } + + bool Warning(string warnMsg) + { + Result = MACRO_WARN_MSG(warnMsg); + return (Ok = true); + } + } +} diff --git a/QtVsTest/MacroClient.h b/QtVsTest/MacroClient.h new file mode 100644 index 0000000..c9ab713 --- /dev/null +++ b/QtVsTest/MacroClient.h @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MACROCLIENT_H +#define MACROCLIENT_H + +#include <QDir> +#include <QFile> +#include <QString> + +#include <QElapsedTimer> +#include <QLocalSocket> +#include <QProcess> +#include <QThread> + +#include <process.h> + +#define MACRO_OK QStringLiteral("(ok)") +#define MACRO_ERROR QStringLiteral("(error)") +#define MACRO_ERROR_MSG(msg) QStringLiteral("(error)\r\n" msg) +#define MACRO_WARN QStringLiteral("(warn)") +#define MACRO_WARN_MSG(msg) QStringLiteral("(warn)\r\n" msg) + +class MacroClient +{ + +public: + MacroClient() + {} + + ~MacroClient() + { + disconnect(false); + } + + bool connect(qint64 *refPid = 0) + { + qint64 pid = 0; + if (!refPid) + refPid = &pid; + + if (*refPid == 0) { + vsProcess.setProgram("devenv.exe"); + if (vsProcess.state() != QProcess::Running) { + vsProcess.start(); + if (!vsProcess.waitForStarted()) + return false; + } + *refPid = vsProcess.processId(); + } else if (!vsProcess.setProcessId(*refPid)) { + return false; + } + + QString pipeName = QStringLiteral("QtVSTest_%1").arg(*refPid); + + QElapsedTimer timer; + timer.start(); + + while (!timer.hasExpired(30000) && socket.state() != QLocalSocket::ConnectedState) { + socket.connectToServer(pipeName, QIODevice::ReadWrite); + if (socket.state() != QLocalSocket::ConnectedState) { + socket.abort(); + QThread::usleep(100); + } + } + + return (socket.state() == QLocalSocket::ConnectedState); + } + + void disconnect(bool closeVs) + { + if (socket.state() == QLocalSocket::ConnectedState) + socket.disconnectFromServer(); + + if (vsProcess.isRunning()) { + if (closeVs) + vsProcess.kill(); + else + vsProcess.detach(); + } + } + + QString runMacro(QString macroCode) + { + if (socket.state() != QLocalSocket::ConnectedState && !connect()) + return MACRO_ERROR_MSG("Disconnected"); + + QByteArray data = macroCode.toUtf8(); + int size = data.size(); + + socket.write(reinterpret_cast<const char *>(&size), sizeof(int)); + socket.write(data); + socket.flush(); + + if (socket.state() != QLocalSocket::ConnectedState) + return MACRO_ERROR_MSG("Disconnected"); + + while (socket.state() == QLocalSocket::ConnectedState && socket.bytesToWrite()) + socket.waitForBytesWritten(15000); + if (socket.state() != QLocalSocket::ConnectedState) + return MACRO_ERROR_MSG("Disconnected"); + + while (socket.state() == QLocalSocket::ConnectedState && socket.bytesAvailable() < 4) + socket.waitForReadyRead(15000); + if (socket.state() != QLocalSocket::ConnectedState) + return MACRO_ERROR_MSG("Disconnected"); + + size = *reinterpret_cast<int *>(socket.read(4).data()); + + while (socket.state() == QLocalSocket::ConnectedState && socket.bytesAvailable() < size) + socket.waitForReadyRead(15000); + if (socket.state() != QLocalSocket::ConnectedState) + return MACRO_ERROR_MSG("Disconnected"); + + data = socket.read(size); + return QString::fromUtf8(data); + } + + QString runMacro(QFile ¯oFile) + { + return loadAndRunMacro(macroFile); + } + + QString storeMacro(QString macroName, QString macroCode) + { + return runMacro(QString() % "//#macro " % macroName % "\r\n" % macroCode); + } + + QString storeMacro(QString macroName, QFile ¯oFile) + { + if (macroName.isNull() || macroName.isEmpty()) + return MACRO_ERROR_MSG("Invalid macro name"); + return loadAndRunMacro(macroFile, QString("//#macro %1").arg(macroName)); + } + +private: + QString loadAndRunMacro(QFile ¯oFile, QString macroHeader = QString()) + { + if (!macroFile.open(QIODevice::ReadOnly | QIODevice::Text)) + return MACRO_ERROR_MSG("Macro load failed"); + QString macroCode = QString::fromUtf8(macroFile.readAll()); + macroFile.close(); + if (macroCode.isEmpty()) + return MACRO_ERROR_MSG("Macro load failed"); + if (!macroHeader.isNull()) + return runMacro(macroHeader % "\r\n" % macroCode); + else + return runMacro(macroCode); + } + + class QDetachableProcess : public QProcess + { + public: + QDetachableProcess(QObject *parent = 0) : QProcess(parent), detachedPid(0) + { } + void detach() + { + if (isAttached()) { + detachedPid = QProcess::processId(); + waitForStarted(); + setProcessState(QProcess::NotRunning); + } + } + qint64 processId() + { + if (isAttached()) + return QProcess::processId(); + return detachedPid; + } + bool setProcessId(qint64 pid) + { + if (isAttached()) + return false; + else if (!detachedIsRunning(pid)) + return false; + detachedPid = pid; + return true; + } + void kill() + { + if (isAttached()) { + terminate(); + if (!waitForFinished(3000)) + QProcess::kill(); + } else { + killDetached(); + } + } + bool isRunning() + { + return (isAttached() || detachedIsRunning(detachedPid)); + } + private: + qint64 detachedPid; + bool isAttached() + { + return (state() == QProcess::Running); + } + bool detachedIsRunning(qint64 pid) + { + if (pid == 0) + return false; + int errorLevel = system(qPrintable(QString( + "tasklist /FI \"PID eq %1\" /FO LIST | find /I /N \"%1\" > NUL 2>&1") + .arg(pid))); + return (errorLevel == 0); + } + void killDetached() + { + if (detachedPid == 0) + return; + system(qPrintable(QString( + "taskkill /PID %1 > NUL 2>&1") + .arg(detachedPid))); + detachedPid = 0; + } + }; + + QDetachableProcess vsProcess; + QLocalSocket socket; + +}; // class MacroClient + +#endif // MACROCLIENT_H diff --git a/QtVsTest/MacroParser.cs b/QtVsTest/MacroParser.cs new file mode 100644 index 0000000..4f8d240 --- /dev/null +++ b/QtVsTest/MacroParser.cs @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace QtVsTest.Macros +{ + using System.Collections; + using static QtVsTools.SyntaxAnalysis.RegExpr; + + class MacroLines : IEnumerable<MacroLine> + { + List<MacroLine> Lines = new List<MacroLine>(); + + public void Add(MacroLine line) { Lines.Add(line); } + + public IEnumerator<MacroLine> GetEnumerator() { return Lines.GetEnumerator(); } + + IEnumerator IEnumerable.GetEnumerator() { return Lines.GetEnumerator(); } + } + + public class MacroLine + { } + + public enum StatementType + { + Unknown, + + // Define reusable macro + //# macro <macro name> + Macro, + + // Switch to thread + //# thread <ui | default> + Thread, + + // Add reference to assembly and (opt.) namespaces in that assembly + //# ref <assembly name> [namespace] [namespace] ... + Reference, + Ref = Reference, + + // Add reference to namespace + //# using <namespace> + Using, + + // Declare global variable, shared by called/calling macros + //# var <type> <name> [ => <initial value> ] + Var, + + // Get Visual Studio SDK service and assign it to local variable + //# service <var name> <service interface> [service type] + Service, + + // Call macro + //# call <macro> + Call, + + // Wait until expression evaluation returns non-default value + // Optionally assign evaluated value to variable + //# wait [timeout] [ <var type> <var name> ] => <expr> + Wait, + + // UI automation command + // + // Set context based on UI element name path + //# ui context [ VS ] => _string_ [, _string_, ... ] + // + // Set context based on window handle + //# ui context HWND => _int_ + // + // Get reference to UI element pattern. By default, the current context is used as source. + // A name path relative to the current context allows using a child element as source. + //# ui pattern <_TypeName_> <_VarName_> [ => _string_ [, _string_, ... ] ] + // + // Get reference to UI element Invoke pattern and immediately call the Invoke() method. + //# ui pattern Invoke [ => _string_ [, _string_, ... ] ] + // + // Get reference to UI element Toggle pattern and immediately call the Toggle() method. + //# ui pattern Toggle [ => _string_ [, _string_, ... ] ] + Ui, + + // Close Visual Studio + //# quit + Quit + } + + public class Statement : MacroLine + { + public StatementType Type { get; set; } + public List<string> Args { get; set; } + public string Code { get; set; } + } + + public class CodeLine : MacroLine + { + public string Code; + public CodeLine(string code) + { + Code = code; + } + } + + class MacroParser + { + Parser MacroTextParser; + Token TokenMacro; + + public static MacroParser Get() + { + var _this = new MacroParser(); + return _this.Initialize() ? _this : null; + } + + enum TokenId + { + Macro, + Code, + Statement, + StatementType, + StatementArg, + StatementArgValue, + StatementCode, + StatementCodeValue, + }; + + bool Initialize() + { + var charEsc = Char['\\']; + var charQuot = Char['\"']; + var stmtBegin = new Token("//#"); + var codeBegin = new Token("=>"); + + var stmtType = new Token(TokenId.StatementType, + CharWord.Repeat(atLeast: 1)); + + var quotedArgValue = new Token(TokenId.StatementArgValue, SkipWs_Disable, + (charEsc & charQuot | CharSet[~(charQuot + CharCr + CharLf)]).Repeat(atLeast: 1)); + + var quotedArg = new Token(TokenId.StatementArg, + charQuot & quotedArgValue & charQuot.Optional()) + { + new Rule<string> + { + Create(TokenId.StatementArgValue, (string value) => value ) + } + }; + + var unquotedArg = new Token(TokenId.StatementArg, + !LookAhead[charQuot] & CharSet[~CharSpace].Repeat(atLeast: 1)); + + var stmtArg = !LookAhead[codeBegin] & (quotedArg | unquotedArg); + + var stmtCodeValue = new Token(TokenId.StatementCodeValue, + CharSet[~(CharCr + CharLf)].Repeat()); + + var stmtCode = new Token(TokenId.StatementCode, + codeBegin & stmtCodeValue) + { + new Rule<string> + { + Create(TokenId.StatementCodeValue, (string value) => value ) + } + }; + + var stmtLine = StartOfLine & + new Token(TokenId.Statement, SkipWs_Disable, + // '//#' <type> [ <arg>... ] [ '=>' <code> ] + stmtBegin & stmtType & stmtArg.Repeat() & stmtCode.Optional()) + { + new Rule<Statement> + { + Capture(value => new Statement { Args = new List<string>() }), + Update(TokenId.StatementType, (Statement s, string typeStr) => + { + StatementType type; + if (Enum.TryParse(typeStr, ignoreCase: true, result: out type)) + s.Type = type; + else + s.Type = StatementType.Unknown; + }), + Update(TokenId.StatementArg, (Statement s, string arg) => s.Args.Add(arg)), + Update(TokenId.StatementCode, (Statement s, string code) => s.Code = code) + } + } + & SkipWs & (LineBreak | EndOfFile); + + var codeLine = StartOfLine & + new Token(TokenId.Code, SkipWs_Disable, + !LookAhead[stmtBegin & stmtType] & CharSet[~(CharCr + CharLf)].Repeat()) + { + new Rule<CodeLine> + { + Capture((string value) => new CodeLine(value)) + } + } + & (LineBreak | EndOfFile); + + TokenMacro = new Token(TokenId.Macro, SkipWs_Disable, + (stmtLine | codeLine).Repeat() & EndOfFile) + { + new Rule<MacroLines> + { + Update(TokenId.Statement, (MacroLines m, Statement s) => m.Add(s)), + Update(TokenId.Code, (MacroLines m, CodeLine l) => m.Add(l)) + } + }; + + var parser = TokenMacro.Render(defaultTokenWs: CharHorizSpace.Repeat()); + if (parser == null) + return false; + + MacroTextParser = parser; + return true; + } + + public MacroLines Parse(string macroText) + { + return MacroTextParser.Parse(macroText) + .GetValues<MacroLines>(TokenMacro) + .FirstOrDefault(); + } + } +} diff --git a/QtVsTest/MacroServer.cs b/QtVsTest/MacroServer.cs new file mode 100644 index 0000000..24f0033 --- /dev/null +++ b/QtVsTest/MacroServer.cs @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.IO.Pipes; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Threading; +using EnvDTE; +using EnvDTE80; + +using Task = System.Threading.Tasks.Task; +using Process = System.Diagnostics.Process; + +namespace QtVsTest.Macros +{ + /// <summary> + /// Provides test clients with macro compilation and execution services + /// </summary> + class MacroServer + { + public CancellationTokenSource Loop { get; private set; } + + AsyncPackage Package { get; set; } + JoinableTaskFactory JoinableTaskFactory { get; set; } + + /// <summary> + /// Macro server constructor + /// </summary> + /// <param name="package">QtVSTest extension package</param> + /// <param name="joinableTaskFactory">Task factory, enables joining with UI thread</param> + public MacroServer(AsyncPackage package, JoinableTaskFactory joinableTaskFactory) + { + Package = package; + JoinableTaskFactory = joinableTaskFactory; + Loop = new CancellationTokenSource(); + } + + /// <summary> + /// Server loop + /// </summary> + public async Task LoopAsync() + { + await JoinableTaskFactory.SwitchToMainThreadAsync(Loop.Token); + var DTE = await Package.GetServiceAsync(typeof(DTE)) as DTE2; + await TaskScheduler.Default; + + var pipeName = string.Format("QtVSTest_{0}", Process.GetCurrentProcess().Id); + while (!Loop.Token.IsCancellationRequested) { + using (var pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut)) { + + // Clean-up previous macro session + Macro.Reset(); + + await pipe.WaitForConnectionAsync(Loop.Token); + if (Loop.Token.IsCancellationRequested) + break; + + while (!Loop.Token.IsCancellationRequested && pipe.IsConnected) { + byte[] data = new byte[4]; + await pipe.ReadAsync(data, 0, 4, Loop.Token); + if (Loop.Token.IsCancellationRequested) + break; + + if (pipe.IsConnected) { + int size = BitConverter.ToInt32(data, 0); + data = new byte[size]; + + await pipe.ReadAsync(data, 0, size, Loop.Token); + if (Loop.Token.IsCancellationRequested) + break; + + var macro = new Macro(Package, DTE, JoinableTaskFactory, Loop.Token); + await macro.CompileAsync(Encoding.UTF8.GetString(data)); + if (macro.AutoRun) + await macro.RunAsync(); + + data = Encoding.UTF8.GetBytes(macro.Result); + size = data.Length; + + await pipe.WriteAsync(BitConverter.GetBytes(size), 0, 4, Loop.Token); + if (Loop.Token.IsCancellationRequested) + break; + + await pipe.WriteAsync(data, 0, size, Loop.Token); + if (Loop.Token.IsCancellationRequested) + break; + + await pipe.FlushAsync(Loop.Token); + if (Loop.Token.IsCancellationRequested) + break; + + pipe.WaitForPipeDrain(); + + if (macro != null && macro.Ok && macro.AutoRun && macro.QuitWhenDone) { + await JoinableTaskFactory.SwitchToMainThreadAsync(Loop.Token); + if (DTE != null) { + DTE.Solution.Close(false); + DTE.Quit(); + } + await TaskScheduler.Default; + Loop.Cancel(); + } + } + } + } + } + } + } +} diff --git a/QtVsTest/Properties/AssemblyInfo.cs b/QtVsTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e221ea2 --- /dev/null +++ b/QtVsTest/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("QtVsTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("QtVsTest")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/QtVsTest/QtVsTest.cs b/QtVsTest/QtVsTest.cs new file mode 100644 index 0000000..74a7939 --- /dev/null +++ b/QtVsTest/QtVsTest.cs @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Threading; + +using Task = System.Threading.Tasks.Task; + +namespace QtVsTest +{ + using Macros; + using System.IO; + + [Guid(PackageGuidString)] + [InstalledProductRegistration( + productName: "Qt Visual Studio Test", + productDetails: "Auto-test framework for Qt Visual Studio Tools.", + productId: "1.0", + IconResourceID = 400)] + [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] + [ProvideAutoLoad(UIContextGuids.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)] + [ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)] + + public sealed class QtVsTest : AsyncPackage + { + public const string PackageGuidString = "0e258dce-fc8a-49a2-81c5-c9e138bfe500"; + MacroServer MacroServer { get; set; } + + public QtVsTest() + { + MacroServer = new MacroServer(this, JoinableTaskFactory); + } + + protected override async Task InitializeAsync( + CancellationToken cancellationToken, + IProgress<ServiceProgressData> progress) + { + // Get package install path + var uri = new Uri(System.Reflection.Assembly + .GetExecutingAssembly().EscapedCodeBase); + var pkgInstallPath = Path.GetDirectoryName( + Uri.UnescapeDataString(uri.AbsolutePath)) + @"\"; + + // Install client interface + var qtVsTestFiles = Environment. + ExpandEnvironmentVariables(@"%LOCALAPPDATA%\qtvstest"); + Directory.CreateDirectory(qtVsTestFiles); + File.Copy( + Path.Combine(pkgInstallPath, "MacroClient.h"), + Path.Combine(qtVsTestFiles, "MacroClient.h"), + overwrite: true); + + // Install .csmacro syntax highlighting + var grammarFilesPath = Environment. + ExpandEnvironmentVariables(@"%USERPROFILE%\.vs\Extensions\qtcsmacro"); + Directory.CreateDirectory(grammarFilesPath); + File.Copy( + Path.Combine(pkgInstallPath, "csmacro.tmLanguage"), + Path.Combine(grammarFilesPath, "csmacro.tmLanguage"), + overwrite: true); + File.Copy( + Path.Combine(pkgInstallPath, "csmacro.tmTheme"), + Path.Combine(grammarFilesPath, "csmacro.tmTheme"), + overwrite: true); + + // Start macro server loop as background task + await Task.Run(() => MacroServer.LoopAsync().Forget()); + } + + protected override int QueryClose(out bool canClose) + { + // Shutdown macro server when closing Visual Studio + MacroServer.Loop.Cancel(); + + return base.QueryClose(out canClose); + } + } +} diff --git a/QtVsTest/QtVsTest.csproj b/QtVsTest/QtVsTest.csproj new file mode 100644 index 0000000..c1c2aa4 --- /dev/null +++ b/QtVsTest/QtVsTest.csproj @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(SolutionDir)\version.targets" /> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + <UseCodebase>true</UseCodebase> + </PropertyGroup> + <PropertyGroup> + <SignAssembly>false</SignAssembly> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <RootNamespace>QtVsTest</RootNamespace> + <AssemblyName>QtVsTest</AssemblyName> + <ProjectGuid>{48A50432-6BDF-4DE2-A3AD-3A237D31E49D}</ProjectGuid> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <GeneratePkgDefFile>true</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>true</IncludeDebugSymbolsInLocalVSIXDeployment> + <CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + <StartAction>Program</StartAction> + <StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram> + <StartArguments>/rootsuffix Exp</StartArguments> + <ApplicationIcon>Resources\QtVsTest.ico</ApplicationIcon> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + <Reference Include="System" /> + <Reference Include="System.Design" /> + <Reference Include="System.Drawing" /> + <Reference Include="Microsoft.VisualStudio.ProjectSystem"> + <HintPath>$(VsInstallRoot)\Common7\IDE\CommonExtensions\Microsoft\Project\Microsoft.VisualStudio.ProjectSystem.dll</HintPath> + </Reference> + <Reference Include="Microsoft.VisualStudio.Composition"> + <HintPath>$(VsInstallRoot)\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll</HintPath> + </Reference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="System.ComponentModel.Composition" + Version="$(Version_System_ComponentModel_Composition)" /> + <PackageReference Include="Stub.System.Data.SQLite.Core.NetFramework" + Version="$(Version_Stub_System_Data_SQLite_Core_NetFramework)" /> + <PackageReference Include="Newtonsoft.Json" + Version="$(Version_Newtonsoft_Json)" /> + <PackageReference Include="Microsoft.Build" + Version="$(Version_Microsoft_Build)" /> + <PackageReference Include="Microsoft.Build.Framework" + Version="$(Version_Microsoft_Build_Framework)" /> + <PackageReference Include="Microsoft.Build.Tasks.Core" + Version="$(Version_Microsoft_Build_Tasks_Core)" /> + <PackageReference Include="Microsoft.VisualStudio.SDK" + Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" /> + <PackageReference Include="Microsoft.VSSDK.BuildTools" + Version="$(Version_Microsoft_VSSDK_BuildTools)" /> + <PackageReference Include="Microsoft.VisualStudio.Shell.Framework" + Version="$(Version_Microsoft_VisualStudio_Shell_Framework)" /> + <PackageReference Include="Microsoft.VisualStudio.Validation" + Version="$(Version_Microsoft_VisualStudio_Validation)" /> + <PackageReference Include="$(Name_Microsoft_VisualStudio_Threading)" + Version="$(Version_Microsoft_VisualStudio_Threading)" /> + <PackageReference Include="System.Collections.Immutable" + Version="$(Version_System_Collections_Immutable)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'"> + <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" + Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'"> + <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" + Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'"> + <Reference Include="Microsoft.VisualStudio.VCProjectEngine" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="..\QtVsTools.RegExpr\QtVsTools.RegExpr.csproj"> + <Project>{a2831b9b-4d3b-46cb-85df-1b5c277c17db}</Project> + <Name>QtVsTools.RegExpr</Name> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <Compile Include="Macro.cs" /> + <Compile Include="MacroParser.cs" /> + <Compile Include="MacroServer.cs" /> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + <Compile Include="QtVsTest.cs" /> + <None Include="source.extension.vsixmanifest"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>source.extension.vsixmanifest_TT</DependentUpon> + </None> + <T4Template Include="source.extension.vsixmanifest_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>source.extension.vsixmanifest</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>source.extension.vsixmanifest</LastGenOutput> + </T4Template> + <Content Include="MacroClient.h"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <Content Include="Resources\QtVsTest.ico" /> + <T4Template Include="csmacro.tmLanguage_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>csmacro.tmLanguage</OutputFile> + <DependsOn>$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>csmacro.tmLanguage</LastGenOutput> + </T4Template> + <Content Include="csmacro.tmLanguage"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>csmacro.tmLanguage_TT</DependentUpon> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <T4Template Include="csmacro.tmTheme_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>csmacro.tmTheme</OutputFile> + <DependsOn>$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>csmacro.tmTheme</LastGenOutput> + </T4Template> + <Content Include="csmacro.tmTheme"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>csmacro.tmTheme_TT</DependentUpon> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + </ItemGroup> + <Choose> + <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.17763.0')"> + <PropertyGroup> + <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.17763.0</Win10SDKPath> + </PropertyGroup> + </When> + <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.17134.0')"> + <PropertyGroup> + <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.17134.0</Win10SDKPath> + </PropertyGroup> + </When> + <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.16299.15')"> + <PropertyGroup> + <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.16299.15</Win10SDKPath> + </PropertyGroup> + </When> + <When Condition="Exists('$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.15063.0')"> + <PropertyGroup> + <Win10SDKPath>$(MSBuildProgramFiles32)\Windows Kits\10\bin\10.0.15063.0</Win10SDKPath> + </PropertyGroup> + </When> + </Choose> + <PropertyGroup Condition="'$(Win10SDKPath)' != ''"> + <UIAVerifyPath>$(Win10SDKPath)\x86\UIAVerify</UIAVerifyPath> + </PropertyGroup> + <ItemGroup Condition="'$(UIAVerifyPath)' != ''"> + <Reference Include="Interop.UIAutomationClient"> + <HintPath>$(UIAVerifyPath)\Interop.UIAutomationClient.dll</HintPath> + <SpecificVersion>False</SpecificVersion> + <EmbedInteropTypes>False</EmbedInteropTypes> + </Reference> + <Reference Include="UIAComWrapper"> + <HintPath>$(UIAVerifyPath)\UIAComWrapper.dll</HintPath> + <Private>True</Private> + <Aliases>global</Aliases> + <EmbedInteropTypes>False</EmbedInteropTypes> + </Reference> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> +</Project> \ No newline at end of file diff --git a/QtVsTest/Resources/QtVsTest.ico b/QtVsTest/Resources/QtVsTest.ico new file mode 100644 index 0000000..c501ecc --- /dev/null +++ b/QtVsTest/Resources/QtVsTest.ico Binary files differ diff --git a/QtVsTest/csmacro.tmLanguage_TT b/QtVsTest/csmacro.tmLanguage_TT new file mode 100644 index 0000000..e06f4d5 --- /dev/null +++ b/QtVsTest/csmacro.tmLanguage_TT @@ -0,0 +1,529 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="tmLanguage" #> +<#@include file="$(SolutionDir)\common.tt" #> +<# + var csGrammarFile = Path.Combine(DevEnvDir, + @"CommonExtensions\Microsoft\TextMate\Starterkit\Extensions\csharp\Syntaxes", + "csharp.tmLanguage"); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>name</key> + <string>C#</string> + <key>scopeName</key> + <string>source.cs</string> + <key>fileTypes</key> + <array> + <string>csmacro</string> + </array> + <key>patterns</key> + <array> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# using "<namespace>" + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*using)\s+(\"[^\"\r\n]+")([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>string.quoted.double.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# using <namespace> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*using)\s+([\w\.]*)([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# macro <macro name> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*macro)\s+([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# call <macro> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*call)\s+([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# thread <ui | default> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*thread)\s+(ui|default)([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>keyword.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# ref <assembly name> [namespace] [namespace] ... + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*ref(?:erence)?)\s+((?:\"[^\"\r\n]*\")|(?:[\w\.]+))((?:\s+[\w\.]+)*)([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>string.quoted.double.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# var <type> <name> => <initial value> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>begin</key> + <string>(//#\s*var)\s+([\w\.]+)\s+(\w+)\s+(=>)</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.type.class.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.variable.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + </dict> + <key>end</key> + <string>^</string> + <key>patterns</key> + <array> + <dict> + <key>include</key> + <string>#expression</string> + </dict> + </array> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# var <type> <name> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*var)\s+([\w\.]+)\s+(\w+)([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.type.class.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.variable.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# service <var name> <service interface> [service type] + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*service)\s+(\w+)\s+([\w\.]+)(?:\s+([\w\.]+))?([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.variable.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.type.interface.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>entity.name.type.class.cs</string> + </dict> + <key>5</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# wait [timeout] [ <var type> <var name> ] => <expr> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>begin</key> + <string>(//#\s*wait)\s+(?:(\d+)\s+)?(?:([\w\.]+)\s+(\w+)\s+)?(=>)</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>constant.numeric.decimal.cs</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.type.class.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>entity.name.variable.cs</string> + </dict> + <key>5</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + </dict> + <key>end</key> + <string>^</string> + <key>patterns</key> + <array> + <dict> + <key>include</key> + <string>#expression</string> + </dict> + </array> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# ui context [ VSROOT | DESKTOP [timeout] ] => _string_ [, _string_, ... ] + //# ui context HWND [timeout] => _int_ + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>begin</key> + <string>(//#\s*ui)\s+(context)\s+(?:(?:(?:(VSROOT|DESKTOP)(?:\s+(\d+))?)|(HWND))\s+)?(=>)</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>keyword.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>constant.numeric.decimal.cs</string> + </dict> + <key>5</key> + <dict> + <key>name</key> + <string>keyword.cs</string> + </dict> + <key>6</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + </dict> + <key>end</key> + <string>^</string> + <key>patterns</key> + <array> + <dict> + <key>include</key> + <string>#expression</string> + </dict> + <dict> + <key>include</key> + <string>#punctuation-comma</string> + </dict> + </array> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# ui pattern <_TypeName_> <_VarName_> [ => _string_ [, _string_, ... ] ] + //# ui pattern Invoke [ => _string_ [, _string_, ... ] ] + //# ui pattern Toggle [ => _string_ [, _string_, ... ] ] + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>begin</key> + <string>(//#\s*ui)\s+(pattern)\s+([\w\.]+)\s+(?:(\w+)\s+)?(=>)</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.type.class.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>entity.name.variable.cs</string> + </dict> + <key>5</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + </dict> + <key>end</key> + <string>^</string> + <key>patterns</key> + <array> + <dict> + <key>include</key> + <string>#expression</string> + </dict> + <dict> + <key>include</key> + <string>#punctuation-comma</string> + </dict> + </array> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# ui pattern <_TypeName_> <_VarName_> + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*ui)\s+(pattern)\s+([\w\.]+)\s+(\w+)([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.type.class.cs</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>entity.name.variable.cs</string> + </dict> + <key>5</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////// + //# quit + ///////////////////////////////////////////////////////////////////////////////////////// --> + <dict> + <key>match</key> + <string>(//#\s*quit)([^\r\n]*)\r?\n</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.control.inline.csmacro</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>comment.line.double-slash.cs</string> + </dict> + </dict> + </dict> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#=string.Join("\r\n", File.ReadAllLines(csGrammarFile) + .SkipWhile(x => !x.Contains("<key>patterns</key>")) + .Skip(2) + .TakeWhile(x => !x.Contains("</plist>")))#> +<#=XML_COMMENT_BEGIN#> END Generated Text --> + <!-- <#/* --> + </array> + </dict> + <!-- */#> --> +</plist> diff --git a/QtVsTest/csmacro.tmTheme_TT b/QtVsTest/csmacro.tmTheme_TT new file mode 100644 index 0000000..81ce315 --- /dev/null +++ b/QtVsTest/csmacro.tmTheme_TT @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="tmTheme" #> +<#@include file="$(SolutionDir)\common.tt" #> +<# + var themeFile = Path.Combine(DevEnvDir, + @"CommonExtensions\Microsoft\TextMate\Starterkit\Themes", + "vs.tmTheme"); +#> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>name</key> + <string>csmacro Theme</string> + <key>settings</key> + <array> + <dict> + <key>name</key> + <string>csmacro-stmt</string> + <key>scope</key> + <string>keyword.control.inline.csmacro</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#66ff33</string> + </dict> + </dict> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> +<#=string.Join("\r\n", File.ReadAllLines(themeFile) + .SkipWhile(x => !x.Contains("<key>settings</key>")) + .Skip(2) + .TakeWhile(x => !x.Contains("</plist>")))#> +<#=XML_COMMENT_BEGIN#> END Generated Text --> + <!-- <#/* --> + </array> + </dict> + <!-- */#> --> +</plist> diff --git a/QtVsTest/source.extension.vsixmanifest_TT b/QtVsTest/source.extension.vsixmanifest_TT new file mode 100644 index 0000000..e204fd7 --- /dev/null +++ b/QtVsTest/source.extension.vsixmanifest_TT @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vsixmanifest" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +<# + string[] VsProducts = new string[0]; + string VersionRange = ""; + switch (VS_NAME) { + case "2022": + VsProducts = new[] + { + "Microsoft.VisualStudio.Pro", + "Microsoft.VisualStudio.Premium", + "Microsoft.VisualStudio.Ultimate", + "Microsoft.VisualStudio.Community", + "Microsoft.VisualStudio.Enterprise" + }; + VersionRange = "[17.0, 18.0)"; + break; + case "2019": + VsProducts = new[] + { + "Microsoft.VisualStudio.Pro", + "Microsoft.VisualStudio.Premium", + "Microsoft.VisualStudio.Ultimate", + "Microsoft.VisualStudio.Community", + "Microsoft.VisualStudio.Enterprise" + }; + VersionRange = "[16.0, 17.0)"; + break; + case "2017": + VsProducts = new[] + { + "Microsoft.VisualStudio.Pro", + "Microsoft.VisualStudio.Premium", + "Microsoft.VisualStudio.Ultimate", + "Microsoft.VisualStudio.Community", + "Microsoft.VisualStudio.Enterprise" + }; + VersionRange = "[15.0.28010.0,16.0)"; + break; + } +#> +--> +<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" + xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011"> + <Metadata> + <!--<#/*#>--><Identity/><!--<#*/#>--> + <!--<#=XML_COMMENT_END#> + <Identity Id="QtVsTest.7026c025-6dd2-44bf-9913-0b058e239616" + Version="<#=QT_VS_TOOLS_VERSION_MANIFEST#>" + Language="en-US" + Publisher="The Qt Company Ltd." /> + <#=XML_COMMENT_BEGIN#>--> + <DisplayName>Qt Visual Studio Test</DisplayName> + <Description xml:space="preserve">Auto-test framework for Qt Visual Studio Tools.</Description> + </Metadata> + <Installation> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <#foreach (var VsProduct in VsProducts) {#> + <InstallationTarget Id="<#=VsProduct#>" Version="<#=VersionRange#>"> + <#if (VS_NAME == "2022") {#> + <ProductArchitecture>amd64</ProductArchitecture> + <#}#> + </InstallationTarget> + <#}#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Installation> + <Dependencies> + <Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" /> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <#if (VS_NAME == "2022") {#> + <Dependency Id="Microsoft.VisualStudio.MPF.16.0" DisplayName="Visual Studio MPF 16.0" d:Source="Installed" Version="[16.0,17.0)" /> + <#}#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Dependencies> + <Prerequisites> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" + Version="<#=VersionRange#>" + DisplayName="Visual Studio core editor" /> + <Prerequisite Id="Microsoft.VisualStudio.Component.VC.CoreIde" + Version="<#=VersionRange#>" + DisplayName="Visual Studio C++ core features" /> + <Prerequisite Id="Microsoft.VisualStudio.Component.VC.Tools.x86.x64" + Version="<#=VersionRange#>" + DisplayName="MSVC C++ x64/x86 build tools" /> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Prerequisites> + <Assets> + <Asset Type="Microsoft.VisualStudio.VsPackage" d:Source="Project" d:ProjectName="%CurrentProject%" Path="|%CurrentProject%;PkgdefProjectOutputGroup|" /> + </Assets> +</PackageManifest> diff --git a/QtVsTools.Core/BuildConfig.cs b/QtVsTools.Core/BuildConfig.cs new file mode 100644 index 0000000..713e9f8 --- /dev/null +++ b/QtVsTools.Core/BuildConfig.cs @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + public struct BuildConfig + { + public const uint Both = 0x03; + public const uint Release = 0x01; + public const uint Debug = 0x02; + + public static string PlatformToolset => + // TODO: Find a proper way to return the PlatformToolset version. +#if VS2017 + "141"; +#elif VS2019 + "142"; +#elif VS2022 + "143"; +#else +#error Unknown Visual Studio version! +#endif + } +} diff --git a/QtVsTools.Core/CommandLineParser.cs b/QtVsTools.Core/CommandLineParser.cs new file mode 100644 index 0000000..7380cc8 --- /dev/null +++ b/QtVsTools.Core/CommandLineParser.cs @@ -0,0 +1,589 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.IO; + +namespace QtVsTools.Core.CommandLine +{ + using IVSMacroExpander = QtMsBuild.IVSMacroExpander; + + public class Parser + { + + List<Option> commandLineOptionList = new List<Option>(); + Dictionary<string, int> nameHash = new Dictionary<string, int>(); + Dictionary<int, List<string>> optionValuesHash = new Dictionary<int, List<string>>(); + List<string> optionNames = new List<string>(); + List<string> positionalArgumentList = new List<string>(); + List<string> unknownOptionNames = new List<string>(); + bool needsParsing = true; + + public enum SingleDashWordOptionMode + { + ParseAsCompactedShortOptions = 0, + ParseAsLongOptions = 1 + } + SingleDashWordOptionMode singleDashWordOptionMode = 0; + + public enum OptionsAfterPositionalArgumentsMode + { + ParseAsOptions = 0, + ParseAsPositionalArguments = 1 + } + OptionsAfterPositionalArgumentsMode optionsAfterPositionalArgumentsMode = 0; + + public Parser() + { + } + + public string ApplicationDescription + { + get; + set; + } + + public string ErrorText + { + get + { + throw new NotImplementedException(); + } + } + + public string HelpText + { + get + { + throw new NotImplementedException(); + } + } + + public IEnumerable<string> PositionalArguments + { + get + { + CheckParsed("PositionalArguments"); + return positionalArgumentList; + } + } + + public IEnumerable<string> OptionNames + { + get + { + CheckParsed("OptionNames"); + return optionNames; + } + } + + public IEnumerable<string> UnknownOptionNames + { + get + { + CheckParsed("UnknownOptionNames"); + return unknownOptionNames; + } + } + + IEnumerable<string> Aliases(string optionName) + { + int optionIndex; + if (!nameHash.TryGetValue(optionName, out optionIndex)) { + return new List<string>(); + } + return commandLineOptionList[optionIndex].Names; + } + + public void SetSingleDashWordOptionMode(SingleDashWordOptionMode singleDashWordOptionMode) + { + this.singleDashWordOptionMode = singleDashWordOptionMode; + } + + public void SetOptionsAfterPositionalArgumentsMode( + OptionsAfterPositionalArgumentsMode parsingMode) + { + this.optionsAfterPositionalArgumentsMode = parsingMode; + } + + public bool AddOption(Option option) + { + if (option.Names.Any()) { + foreach (var name in option.Names) { + if (nameHash.ContainsKey(name)) + return false; + } + + commandLineOptionList.Add(option); + + int offset = commandLineOptionList.Count() - 1; + foreach (var name in option.Names) + nameHash.Add(name, offset); + + return true; + } + + return false; + } + + public bool AddOptions(IEnumerable<Option> options) + { + bool result = true; + foreach (var option in options) + result &= AddOption(option); + + return result; + } + + public Option AddVersionOption() + { + Option opt = new Option(new[] { "v", "version" }); + AddOption(opt); + return opt; + } + + public Option AddHelpOption() + { + Option opt = new Option(new[] { "?", "h", "help" }); + AddOption(opt); + return opt; + } + + public void AddPositionalArgument(string name, string description, string syntax) + { + throw new NotImplementedException(); + } + + public void ClearPositionalArguments() + { + throw new NotImplementedException(); + } + + bool RegisterFoundOption(string optionName) + { + if (nameHash.ContainsKey(optionName)) { + optionNames.Add(optionName); + return true; + } else { + unknownOptionNames.Add(optionName); + return false; + } + } + + bool ParseOptionValue(string optionName, string argument, + IEnumerator<string> argumentEnumerator, ref bool atEnd) + { + const char assignChar = '='; + int optionOffset; + if (nameHash.TryGetValue(optionName, out optionOffset)) { + int assignPos = argument.IndexOf(assignChar); + bool withValue = !string.IsNullOrEmpty( + commandLineOptionList[optionOffset].ValueName); + if (withValue) { + if (assignPos == -1) { + if (atEnd = (!argumentEnumerator.MoveNext())) { + return false; + } + if (!optionValuesHash.ContainsKey(optionOffset)) + optionValuesHash.Add(optionOffset, new List<string>()); + optionValuesHash[optionOffset].Add(argumentEnumerator.Current); + } else { + if (!optionValuesHash.ContainsKey(optionOffset)) + optionValuesHash.Add(optionOffset, new List<string>()); + optionValuesHash[optionOffset].Add(argument.Substring(assignPos + 1)); + } + } else { + if (assignPos != -1) { + return false; + } + } + } + return true; + } + + void CheckParsed(string method) + { + if (needsParsing) + Trace.TraceWarning("CommandLineParser: Parse() before {0}", method); + } + + List<string> TokenizeArgs(string commandLine, IVSMacroExpander macros, string execName = "") + { + List<string> arguments = new List<string>(); + StringBuilder arg = new StringBuilder(); + bool foundExec = string.IsNullOrEmpty(execName); + foreach (Match token in Lexer.Tokenize(commandLine + " ")) { + // Additional " " ensures loop will always end with whitespace processing + + if (!foundExec) { + if (!token.TokenText() + .EndsWith(execName, + StringComparison.InvariantCultureIgnoreCase)) { + continue; + } + + foundExec = true; + } + + var tokenType = token.TokenType(); + if (tokenType == Token.Whitespace || tokenType == Token.Newline) { + // This will always run at the end of the loop + + if (arg.Length > 0) { + var argData = arg.ToString(); + arg.Clear(); + if (argData.StartsWith("@")) { + var workingDir = macros.ExpandString("$(MSBuildProjectDirectory)"); + var optFilePath = macros.ExpandString(argData.Substring(1)); + string[] additionalArgs = File.ReadAllLines( + Path.Combine(workingDir, optFilePath)); + if (additionalArgs != null) { + var additionalArgsString = string.Join(" ", additionalArgs + .Select(x => "\"" + x.Replace("\"", "\\\"") + "\"")); + arguments.AddRange(TokenizeArgs(additionalArgsString, macros)); + } + } else { + arguments.Add(argData); + } + } + if (tokenType == Token.Newline) + break; + + } else { + arg.Append(token.TokenText()); + } + } + return arguments; + } + + public bool Parse(string commandLine, IVSMacroExpander macros, string execName) + { + List<string> args = null; + try { + args = TokenizeArgs(commandLine, macros, execName); + } catch { + return false; + } + return Parse(args); + } + + public bool Parse(IEnumerable<string> args) + { + needsParsing = false; + + bool error = false; + + const string doubleDashString = "--"; + const char dashChar = '-'; + const char assignChar = '='; + + bool forcePositional = false; + positionalArgumentList.Clear(); + optionNames.Clear(); + unknownOptionNames.Clear(); + optionValuesHash.Clear(); + + if (args == null || !args.Any()) { + return false; + } + + var argumentIterator = args.GetEnumerator(); + bool atEnd = false; + + while (!atEnd && argumentIterator.MoveNext()) { + var argument = argumentIterator.Current; + if (forcePositional) { + positionalArgumentList.Add(argument); + } else if (argument.StartsWith(doubleDashString)) { + if (argument.Length > 2) { + var optionName = argument.Substring(2).Split(new char[] { assignChar })[0]; + if (RegisterFoundOption(optionName)) { + if (!ParseOptionValue( + optionName, + argument, + argumentIterator, + ref atEnd)) { + error = true; + } + + } else { + error = true; + } + } else { + forcePositional = true; + } + } else if (argument.StartsWith(dashChar.ToString())) { + if (argument.Length == 1) { // single dash ("stdin") + positionalArgumentList.Add(argument); + continue; + } + string optionName = ""; + switch (singleDashWordOptionMode) { + case SingleDashWordOptionMode.ParseAsCompactedShortOptions: + bool valueFound = false; + for (int pos = 1; pos < argument.Length; ++pos) { + optionName = argument.Substring(pos, 1); + if (!RegisterFoundOption(optionName)) { + error = true; + } else { + int optionOffset; + Trace.Assert(nameHash.TryGetValue( + optionName, + out optionOffset)); + bool withValue = !string.IsNullOrEmpty( + commandLineOptionList[optionOffset].ValueName); + if (withValue) { + if (pos + 1 < argument.Length) { + if (argument[pos + 1] == assignChar) + ++pos; + if (!optionValuesHash.ContainsKey(optionOffset)) { + optionValuesHash.Add( + optionOffset, + new List<string>()); + } + optionValuesHash[optionOffset].Add( + argument.Substring(pos + 1)); + valueFound = true; + } + break; + } + if (pos + 1 < argument.Length + && argument[pos + 1] == assignChar) { + break; + } + } + } + if (!valueFound + && !ParseOptionValue( + optionName, + argument, + argumentIterator, + ref atEnd)) { + error = true; + } + + break; + case SingleDashWordOptionMode.ParseAsLongOptions: + if (argument.Length > 2) { + string possibleShortOptionStyleName = argument.Substring(1, 1); + + int shortOptionIdx; + if (nameHash.TryGetValue( + possibleShortOptionStyleName, + out shortOptionIdx)) { + var arg = commandLineOptionList[shortOptionIdx]; + if ((arg.Flags & Option.Flag.ShortOptionStyle) != 0) { + RegisterFoundOption(possibleShortOptionStyleName); + if (!optionValuesHash.ContainsKey(shortOptionIdx)) { + optionValuesHash.Add( + shortOptionIdx, + new List<string>()); + } + optionValuesHash[shortOptionIdx].Add( + argument.Substring(2)); + break; + } + } + } + optionName = argument.Substring(1).Split(new char[] { assignChar })[0]; + if (RegisterFoundOption(optionName)) { + if (!ParseOptionValue( + optionName, + argument, + argumentIterator, + ref atEnd)) { + error = true; + } + + } else { + error = true; + } + break; + } + } else { + positionalArgumentList.Add(argument); + if (optionsAfterPositionalArgumentsMode + == OptionsAfterPositionalArgumentsMode.ParseAsPositionalArguments) { + forcePositional = true; + } + } + } + return !error; + } + + public bool IsSet(string name) + { + CheckParsed("IsSet"); + if (optionNames.Contains(name)) + return true; + var aliases = Aliases(name); + foreach (var optionName in optionNames) { + if (aliases.Contains(optionName)) + return true; + } + return false; + } + + public string Value(string optionName) + { + CheckParsed("Value"); + var valueList = Values(optionName); + if (valueList.Any()) + return valueList.Last(); + return ""; + } + + public IEnumerable<string> Values(string optionName) + { + CheckParsed("Values"); + int optionOffset; + if (nameHash.TryGetValue(optionName, out optionOffset)) { + var values = optionValuesHash[optionOffset]; + return values; + } + + Trace.TraceWarning("QCommandLineParser: option not defined: \"{0}\"", optionName); + return new List<string>(); + } + + public bool IsSet(Option option) + { + return option.Names.Any() && IsSet(option.Names.First()); + } + + public string Value(Option option) + { + return Value(option.Names.FirstOrDefault()); + } + + public IEnumerable<string> Values(Option option) + { + return Values(option.Names.FirstOrDefault()); + } + } + + public class Option + { + [Flags] + public enum Flag + { + HiddenFromHelp = 0x1, + ShortOptionStyle = 0x2 + } + + public Option(string name, string valueName = "") + { + Names = new[] { name }; + ValueName = valueName; + Flags = 0; + } + + public Option(IEnumerable<string> names, string valueName = "") + { + Names = names; + ValueName = valueName; + Flags = 0; + } + + public Option(Option other) + { + Names = other.Names; + ValueName = other.ValueName; + Flags = other.Flags; + } + + public IEnumerable<string> Names + { + get; + private set; + } + + public string ValueName + { + get; + set; + } + + public Flag Flags + { + get; + set; + } + + public override string ToString() + { + return Names.Last(); + } + + } + + enum Token + { + Unknown = 0, + Newline = 1, + Unquoted = 2, + Quoted = 3, + Whitespace = 4 + }; + + static class Lexer + { + static Regex lexer = new Regex( + /* Newline */ @"(\n)" + + /* Unquoted */ @"|((?:(?:[^\s\""])|(?:(?<=\\)\""))+)" + + /* Quoted */ @"|(?:\""((?:(?:[^\""])|(?:(?<=\\)\""))+)\"")" + + /* Whitespace */ @"|(\s+)"); + + public static Token TokenType(this Match token) + { + for (int i = 1; i < token.Groups.Count; i++) { + if (!string.IsNullOrEmpty(token.Groups[i].Value)) + return (Token)i; + } + return Token.Unknown; + } + + public static string TokenText(this Match token) + { + Token t = TokenType(token); + if (t != Token.Unknown) + return token.Groups[(int)t].Value.Replace("\\\"", "\""); + return ""; + } + + public static MatchCollection Tokenize(string commandLine) + { + return lexer.Matches(commandLine); + } + } +} diff --git a/QtVsTools.Core/Common/EnumExt.cs b/QtVsTools.Core/Common/EnumExt.cs new file mode 100644 index 0000000..0406e2c --- /dev/null +++ b/QtVsTools.Core/Common/EnumExt.cs @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace QtVsTools.Common +{ + /// <summary> + /// Extended enum support: + /// * Customized cast of enum values to arbitrary types + /// </summary> + public static class EnumExt + { + /// <summary> + /// Wrapper for enum cast values. + /// </summary> + /// <typeparam name="T">Type of cast output</typeparam> + /// <remarks> + /// Cast attributes associated with enum values must implement this interface. + /// </remarks> + public interface ICast<T> + { + T Value { get; } + } + + /// <summary> + /// String cast attribute associated to an enum value. + /// </summary> + /// <example> + /// enum Foobar { + /// Foo, + /// [EnumExt.String("Bahr")] Bar + /// } + /// </example> + [AttributeUsage(AttributeTargets.All)] + public sealed class StringAttribute : Attribute, ICast<string> + { + public string Value { get; private set; } + public StringAttribute(string str) { Value = str; } + } + + /// <summary> + /// Cast enum value to type T. + /// </summary> + /// <typeparam name="T">Cast output type.</typeparam> + /// <param name="value">Input enum value.</param> + /// <returns> + /// Value of type T associated to the enum value by an Attribute implementing + /// ICast<typeparamref name="T"/>. If no attribute is found, returns a default value. + /// </returns> + /// <example> + /// enum Foobar + /// { + /// Foo, + /// [EnumExt.String("Bahr")] Bar + /// } + /// Foobar foo = Foobar.Foo; + /// Foobar bar = Foobar.Bar; + /// string fooCastString = foo.Cast<string>(); // "Foo" + /// string barCastString = bar.Cast<string>(); // "Bahr" + /// string fooToString = foo.ToString(); // "Foo" + /// string barToString = bar.ToString(); // "Bar" + /// </example> + public static T Cast<T>(this Enum value) + { + if (FindCastAttrib<T>(value) is ICast<T> cast) + return cast.Value; + else + return Default<T>(value); + } + + /// <summary> + /// Compare enum value with instance/value of type T. + /// </summary> + /// <typeparam name="T">Cast/comparison type.</typeparam> + /// <param name="valueT">Instance/value of type T to compare with.</param> + /// <param name="valueEnum">Enum value to compare with.</param> + /// <returns>true if cast of valueEnum is equal to valueT, false otherwise</returns> + public static bool EqualTo<T>(this T valueT, Enum valueEnum) + { + return valueT.Equals(valueEnum.Cast<T>()); + } + + /// <summary> + /// Convert type T to enum + /// </summary> + public static bool TryCast<T, TEnum>(this T valueT, out TEnum value) where TEnum : struct + { + value = default(TEnum); + IEnumerable<Enum> enumValues = Enum.GetValues(typeof(TEnum)).OfType<Enum>() + .Where((Enum valueEnum) => valueEnum.Cast<T>().Equals(valueT)); + if (enumValues.Any()) + value = (TEnum)Enum.ToObject(typeof(TEnum), enumValues.FirstOrDefault()); + return enumValues.Any(); + } + + /// <summary> + /// Convert type T to enum + /// </summary> + public static TEnum Cast<T, TEnum>(this T valueT, TEnum defaultValue) where TEnum : struct + { + TEnum value; + return TryCast(valueT, out value) ? value : defaultValue; + } + + /// <summary> + /// Get list of values of enum type + /// </summary> + public static IEnumerable<TEnum> GetValues<TEnum>() where TEnum : struct + { + Debug.Assert(typeof(TEnum).IsEnum); + return Enum.GetValues(typeof(TEnum)).OfType<TEnum>(); + } + + /// <summary> + /// Get list of values of enum type converted to type T + /// </summary> + public static IEnumerable<T> GetValues<T>(Type enumType) + { + return Enum.GetValues(enumType).OfType<Enum>() + .Select((Enum value) => value.Cast<T>()); + } + + /// <summary> + /// Default cast of enum value to type T. + /// </summary> + /// <typeparam name="T">Cast output type.</typeparam> + /// <param name="value">Input enum value.</param> + /// <returns> + /// Default value of type T associated with the enum value: + /// * if T is string: returns the enum value name as string; + /// * if T is an integer type: returns the underlying enum integer value; + /// * otherwise: default value for type T (e.g. null for reference types). + /// </returns> + static T Default<T>(Enum value) + { + Type enumType = value.GetType(); + Type baseType = Enum.GetUnderlyingType(enumType); + Type outputType = typeof(T); + if (outputType.IsAssignableFrom(enumType) || outputType.IsAssignableFrom(baseType)) + return (T)(object)value; + else if (outputType == typeof(string)) + return (T)(object)Enum.GetName(value.GetType(), value); + else + return default(T); + } + + /// <summary> + /// Find cast attribute. + /// </summary> + /// <typeparam name="T">Cast output type.</typeparam> + /// <param name="value">Input enum value.</param> + /// <returns> + /// First cast attribute of type T found associated with the enum value, or null in case a + /// suitable attribute is not found. + /// </returns> + static ICast<T> FindCastAttrib<T>(Enum value) + { + Type enumType = value.GetType(); + + string valueName = Enum.GetName(enumType, value); + if (string.IsNullOrEmpty(valueName)) + return null; + + FieldInfo enumField = enumType.GetField(valueName); + if (enumField == null) + return null; + + return CastAttribTypes + .Where(type => typeof(ICast<T>).IsAssignableFrom(type)) + .Select(type => Attribute.GetCustomAttribute(enumField, type) as ICast<T>) + .FirstOrDefault(); + } + + static IEnumerable<Type> _CastAttribTypes; + /// <summary> + /// List of cast attribute types. + /// </summary> + /// <remarks> + /// Future cast attribute types need to be added to this list. + /// </remarks> + static IEnumerable<Type> CastAttribTypes => _CastAttribTypes + ?? (_CastAttribTypes = new[] + { + typeof(StringAttribute) + }); + + } +} diff --git a/QtVsTools.Core/CompilerToolWrapper.cs b/QtVsTools.Core/CompilerToolWrapper.cs new file mode 100644 index 0000000..b9d95fc --- /dev/null +++ b/QtVsTools.Core/CompilerToolWrapper.cs @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.VCProjectEngine; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace QtVsTools.Core +{ + /// <summary> + /// Wrapper for the VCCLCompilerTool class. + /// </summary> + /// For platforms other than Win32 the type VCCLCompilerTool is not available. + /// See http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=220646&SiteID=1 + /// So we have to use the reflection system to get and set the desired properties. + /// This class should be the only place where VCCLCompilerTool is used. + /// Using VCCLCompilerTool directly will break the VS integration for Win CE. + class CompilerToolWrapper + { + private VCCLCompilerTool compilerTool; + private readonly Object compilerObj; + private readonly Type compilerType; + + public static CompilerToolWrapper Create(VCConfiguration config) + { + CompilerToolWrapper wrapper = null; + try { + wrapper = new CompilerToolWrapper(((IVCCollection)config.Tools) + .Item("VCCLCompilerTool")); + } catch { + } + + return wrapper.IsNull() ? null : wrapper; + } + + public static CompilerToolWrapper Create(VCFileConfiguration config) + { + CompilerToolWrapper wrapper = null; + try { + wrapper = new CompilerToolWrapper(config.Tool); + } catch { + } + + return wrapper.IsNull() ? null : wrapper; + } + + public static CompilerToolWrapper Create(VCPropertySheet sheet) + { + CompilerToolWrapper wrapper = null; + try { + wrapper = new CompilerToolWrapper(((IVCCollection)sheet.Tools) + .Item("VCCLCompilerTool")); + } catch { + } + + return wrapper.IsNull() ? null : wrapper; + } + + protected CompilerToolWrapper(object tool) + { + if (tool == null) + return; + + compilerTool = tool as VCCLCompilerTool; + if (compilerTool == null) { + compilerObj = tool; + compilerType = compilerObj.GetType(); + } + } + + protected bool IsNull() + { + return compilerTool == null && compilerObj == null; + } + + public List<string> AdditionalIncludeDirectories + { + get + { + var directories = GetAdditionalIncludeDirectories(); + if (directories == null) + return new List<string>(); + // double quotes are escaped + directories = directories.Replace("\\\"", "\""); + var dirArray = directories.Split(new[] { ';', ',' }, StringSplitOptions + .RemoveEmptyEntries); + var lst = new List<string>(dirArray); + var i = 0; + while (i < lst.Count) { + var item = lst[i]; + if (item.StartsWith("\"", StringComparison.Ordinal) && item.EndsWith("\"", StringComparison.Ordinal)) { + item = item.Remove(0, 1); + item = item.Remove(item.Length - 1, 1); + lst[i] = item; + } + + if (lst[i].Length > 0) + ++i; + else + lst.RemoveAt(i); + } + return lst; + } + + set + { + if (value == null) { + SetAdditionalIncludeDirectories(null); + return; + } + var newDirectories = string.Empty; + var firstLoop = true; + foreach (var dir in value) { + if (firstLoop) + firstLoop = false; + else + newDirectories += ";"; + + if (dir.IndexOfAny(new[] { ' ', '\t' }) > 0 || !Path.IsPathRooted(dir)) + newDirectories += "\"" + dir + "\""; + else + newDirectories += dir; + } + if (newDirectories != GetAdditionalIncludeDirectories()) + SetAdditionalIncludeDirectories(newDirectories); + } + } + + public void SetAdditionalIncludeDirectories(string value) + { + // Prevent setting of empty substring, as they break the build + value = value.Replace("\"\",", string.Empty); + if (compilerTool != null) + compilerTool.AdditionalIncludeDirectories = value; + else + SetStringProperty("AdditionalIncludeDirectories", value); + } + + public List<string> PreprocessorDefinitions + { + get + { + var ppdefsstr = GetPreprocessorDefinitions(); + if (string.IsNullOrEmpty(ppdefsstr)) + return new List<string>(); + + var ppdefs = ppdefsstr.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); + return new List<string>(ppdefs); + } + } + + /// <summary> + /// Adds a single preprocessor definition. + /// </summary> + /// <param name="value"></param> + public void AddPreprocessorDefinition(string value) + { + var preprocessorDefs = GetPreprocessorDefinitions(); + if (preprocessorDefs != null) { + var definesArray = preprocessorDefs.Split(new[] { ';', ',' }, + StringSplitOptions.RemoveEmptyEntries); + var definesList = new List<string>(definesArray); + if (definesList.Contains(value)) + return; + if (preprocessorDefs.Length > 0 + && !preprocessorDefs.EndsWith(";", StringComparison.Ordinal) + && !value.StartsWith(";", StringComparison.Ordinal)) { + preprocessorDefs += ";"; + } + } + preprocessorDefs += value; + SetPreprocessorDefinitions(preprocessorDefs); + } + + /// <summary> + /// Removes a single preprocessor definition. + /// </summary> + /// <param name="value"></param> + public void RemovePreprocessorDefinition(string value) + { + var preprocessorDefs = GetPreprocessorDefinitions(); + if (preprocessorDefs == null) + return; + + var definesArray = preprocessorDefs.Split(new[] { ';', ',' }, + StringSplitOptions.RemoveEmptyEntries); + var definesList = new List<string>(definesArray); + if (!definesList.Remove(value)) + return; + preprocessorDefs = ""; + var firstIteration = true; + foreach (var define in definesList) { + if (firstIteration) + firstIteration = false; + else + preprocessorDefs += ';'; + preprocessorDefs += define; + } + NormalizePreprocessorDefinitions(ref preprocessorDefs); + SetPreprocessorDefinitions(preprocessorDefs); + } + + private static void NormalizePreprocessorDefinitions(ref string preprocessorDefs) + { + var idx = 0; + while ((idx = preprocessorDefs.IndexOf(' ', idx)) != -1) + preprocessorDefs = preprocessorDefs.Remove(idx, 1); + + preprocessorDefs = preprocessorDefs.Replace(',', ';'); + + idx = 0; + while ((idx = preprocessorDefs.IndexOf(";;", idx, StringComparison.Ordinal)) != -1) + preprocessorDefs = preprocessorDefs.Remove(idx, 1); + + if (preprocessorDefs.EndsWith(";", StringComparison.Ordinal)) + preprocessorDefs = preprocessorDefs.Remove(preprocessorDefs.Length - 1); + } + + public void SetPreprocessorDefinitions(string value) + { + if (compilerTool != null) + compilerTool.PreprocessorDefinitions = value; + else + SetStringProperty("PreprocessorDefinitions", value); + } + + public string GetPreprocessorDefinitions() + { + if (compilerTool != null) + return compilerTool.PreprocessorDefinitions; + return GetStringProperty("PreprocessorDefinitions"); + } + + public void AddAdditionalIncludeDirectories(string value) + { + if (string.IsNullOrEmpty(value)) + return; + + var directoryAdded = false; + var directories = value.Split(new[] { ';', ',' }, StringSplitOptions + .RemoveEmptyEntries); + var lst = AdditionalIncludeDirectories; + foreach (var directory in directories) { + if (lst.Contains(directory)) + continue; + + lst.Add(directory); + directoryAdded = true; + } + + if (directoryAdded) + AdditionalIncludeDirectories = lst; + } + + public string[] GetAdditionalIncludeDirectoriesList() + { + var includes = GetAdditionalIncludeDirectories().Split(',', ';'); + var fixedincludes = new string[includes.Length]; + var i = 0; + foreach (var include in includes) { + var incl = include; + if (incl.StartsWith("\\\"", StringComparison.Ordinal) && incl.EndsWith("\\\"", StringComparison.Ordinal)) { + incl = incl.Remove(0, 2); + incl = incl.Remove(incl.Length - 2, 2); + } + fixedincludes[i++] = incl; + } + return fixedincludes; + } + + public string GetAdditionalIncludeDirectories() + { + if (compilerTool != null) + return compilerTool.AdditionalIncludeDirectories; + return GetStringProperty("AdditionalIncludeDirectories"); + } + + public string GetPrecompiledHeaderFile() + { + if (compilerTool != null) + return compilerTool.PrecompiledHeaderFile; + return GetStringProperty("PrecompiledHeaderFile"); + } + + public string GetPrecompiledHeaderThrough() + { + if (compilerTool != null) + return compilerTool.PrecompiledHeaderThrough; + return GetStringProperty("PrecompiledHeaderThrough"); + } + + public pchOption GetUsePrecompiledHeader() + { + if (compilerTool != null) + return compilerTool.UsePrecompiledHeader; + + var obj = compilerType.InvokeMember("UsePrecompiledHeader", + BindingFlags.GetProperty, null, compilerObj, null); + if (obj == null) + return pchOption.pchNone; + return (pchOption)obj; + } + + public void SetDebugInformationFormat(debugOption value) + { + if (compilerTool != null) { + compilerTool.DebugInformationFormat = value; + } else { + compilerType.InvokeMember( + "DebugInformationFormat", + BindingFlags.SetProperty, + null, + compilerObj, + new object[] { value }); + } + } + + public runtimeLibraryOption RuntimeLibrary + { + get + { + if (compilerTool != null) + return compilerTool.RuntimeLibrary; + + var obj = compilerType.InvokeMember("RuntimeLibrary", BindingFlags.GetProperty, + null, compilerObj, null); + if (obj == null) + return runtimeLibraryOption.rtMultiThreaded; + return (runtimeLibraryOption)obj; + } + + set + { + if (compilerTool == null) { + compilerType.InvokeMember("RuntimeLibrary", BindingFlags.SetProperty, + null, compilerObj, new object[] { value }); + } else { + compilerTool.RuntimeLibrary = value; + } + } + } + + public void SetOptimization(optimizeOption value) + { + if (compilerTool != null) { + compilerTool.Optimization = value; + } else { + compilerType.InvokeMember( + "Optimization", + BindingFlags.SetProperty, + null, + compilerObj, + new object[] { value }); + } + } + + public void SetTreatWChar_tAsBuiltInType(bool value) + { + if (compilerTool != null) { + compilerTool.TreatWChar_tAsBuiltInType = value; + } else { + compilerType.InvokeMember( + "TreatWChar_tAsBuiltInType", + BindingFlags.SetProperty, + null, + compilerObj, + new object[] { value }); + } + } + + public void SetPrecompiledHeaderFile(string file) + { + if (compilerTool != null) { + compilerTool.PrecompiledHeaderFile = file; + } else { + compilerType.InvokeMember( + "PrecompiledHeaderFile", + BindingFlags.SetProperty, + null, + compilerObj, + new object[] { file }); + } + } + + public void SetPrecompiledHeaderThrough(string value) + { + if (compilerTool != null) { + compilerTool.PrecompiledHeaderThrough = value; + } else { + compilerType.InvokeMember( + "PrecompiledHeaderThrough", + BindingFlags.SetProperty, + null, + compilerObj, + new object[] { value }); + } + } + + public void SetUsePrecompiledHeader(pchOption value) + { + if (compilerTool != null) { + compilerTool.UsePrecompiledHeader = value; + } else { + compilerType.InvokeMember( + "UsePrecompiledHeader", + BindingFlags.SetProperty, + null, + compilerObj, + new object[] { value }); + } + } + + private void SetStringProperty(string name, string value) + { + compilerType.InvokeMember( + name, + BindingFlags.SetProperty, + null, + compilerObj, + new object[] { value }); + } + + private string GetStringProperty(string name) + { + object obj; + try { + obj = compilerType.InvokeMember(name, BindingFlags.GetProperty, + null, compilerObj, null); + } catch { + obj = null; + } + if (obj == null) + return string.Empty; + return (string)obj; + } + + } +} diff --git a/QtVsTools.Core/CxxStreamReader.cs b/QtVsTools.Core/CxxStreamReader.cs new file mode 100644 index 0000000..eb758eb --- /dev/null +++ b/QtVsTools.Core/CxxStreamReader.cs @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.IO; + +namespace QtVsTools.Core +{ + /// <summary> + /// StreamReader for C++ files. + /// Removes comments, takes care of strings and skips empty lines. + /// </summary> + class CxxStreamReader : IDisposable + { + private enum State + { + Normal, Comment, String + } + private State state = State.Normal; + private StreamReader sr; + private string partialLine; + bool disposed; + + int _lineNum; + string[] _lines; + + public CxxStreamReader(string[] lines) + { + _lines = lines; + } + + public CxxStreamReader(string fileName) + { + sr = new StreamReader(fileName); + } + + ~CxxStreamReader() + { + Dispose(false); + } + + public void Close() + { + Dispose(true); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (disposing && sr != null) + sr.Dispose(); + + disposed = true; + } + + public string ReadLine() + { + return ReadLine(false); + } + + public string ReadLine(bool removeStrings) + { + var line = string.Empty; + if (sr != null) { + do { + line = sr.ReadLine(); + if (line == null) + return null; + line = ProcessString(line, removeStrings); + } while (line.Length == 0); + } else { + do { + if (_lineNum >= _lines.Length) + return null; + line = ProcessString(_lines[_lineNum++], removeStrings); + } while (line.Length == 0); + } + return line; + } + + private string ProcessString(string line, bool removeStrings) + { + switch (state) { + case State.Normal: { + var lineCopy = line; + line = string.Empty; + for (int i = 0, j = 1; i < lineCopy.Length; ++i, ++j) { + if (lineCopy[i] == '/' && j < lineCopy.Length) { + if (lineCopy[j] == '*') { + // C style comment detected + var endIdx = lineCopy.IndexOf("*/", j + 1, StringComparison.Ordinal); + if (endIdx >= 0) { + i = endIdx + 1; + j = i + 1; + continue; + } + state = State.Comment; + break; + } + if (lineCopy[j] == '/') { + // C++ style comment detected + break; + } + } else if (lineCopy[i] == '"') { + // start of a string detected + var endIdx = j - 1; + do { + endIdx = lineCopy.IndexOf('"', endIdx + 1); + } while (endIdx >= 0 && lineCopy[endIdx - 1] == '\\'); + + if (endIdx < 0) { + if (lineCopy.EndsWith("\\", StringComparison.Ordinal)) { + partialLine = line; + if (!removeStrings) + partialLine += lineCopy.Substring(i); + state = State.String; + } else { + state = State.Normal; + } + line = string.Empty; + break; + } + if (!removeStrings) + line += lineCopy.Substring(i, endIdx - i + 1); + i = endIdx; + j = i + 1; + continue; + } + line += lineCopy[i]; + } + } + break; + case State.Comment: { + var idx = line.IndexOf("*/", StringComparison.Ordinal); + if (idx >= 0) { + state = State.Normal; + line = line.Substring(idx + 2); + } else { + line = string.Empty; // skip line + } + } + break; + case State.String: { + var lineCopy = line; + line = string.Empty; + var endIdx = -1; + do { + endIdx = lineCopy.IndexOf('"', endIdx + 1); + } while (endIdx >= 0 && lineCopy[endIdx - 1] == '\\'); + if (endIdx < 0) { + if (!removeStrings) + partialLine += lineCopy; + } else { + state = State.Normal; + line = partialLine; + if (!removeStrings) + line += lineCopy.Substring(0, endIdx + 1); + line += ProcessString(lineCopy.Substring(endIdx + 1), removeStrings); + } + } + break; + } + return line; + } + } +} diff --git a/QtVsTools.Core/ExportProjectDialog.cs b/QtVsTools.Core/ExportProjectDialog.cs new file mode 100644 index 0000000..13aea35 --- /dev/null +++ b/QtVsTools.Core/ExportProjectDialog.cs @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace QtVsTools.Core +{ + /// <summary> + /// Summary description for ExportProjectDialog. + /// </summary> + internal class ExportProjectDialog : Form + { + private Button cancelButton; + private Button okButton; + private Label projLabel; + private ListBox optionListBox; + private Label optionLabel; + private ComboBox optionComboBox; + private Label commentLabel; + private TextBox optionTextBox; + private CheckBox openCheckBox; + private CheckedListBox projListBox; + private GroupBox lineBox; + private CheckBox createPriFileCheckBox; + private Panel panel1; + private Panel panel2; + private Button newButton; + private Button delButton; + + public ExportProjectDialog() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + cancelButton.Text = SR.GetString("ExportProjectDialog_Cancel"); + okButton.Text = SR.GetString("ExportProjectDialog_OK"); + projLabel.Text = SR.GetString("ExportProjectDialog_CreatePro"); + optionLabel.Text = SR.GetString("ExportProjectDialog_Project"); + optionTextBox.Text = ""; + openCheckBox.Text = SR.GetString("ExportProjectDialog_Open"); + createPriFileCheckBox.Text = SR.GetString("ExportProjectDialog_CreatePri"); + + if (SR.LanguageName == "de") + Size = new Size(470, 300); + else + Size = new Size(400, 300); + + ShowInTaskbar = false; + Shown += ExportProjectDialog_Shown; + } + + private void ExportProjectDialog_Shown(object sender, EventArgs e) + { + Text = SR.GetString("ExportProjectDialog_Title"); + } + + #region Windows Form Designer generated code + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + cancelButton = new System.Windows.Forms.Button(); + okButton = new System.Windows.Forms.Button(); + projLabel = new System.Windows.Forms.Label(); + optionListBox = new System.Windows.Forms.ListBox(); + optionLabel = new System.Windows.Forms.Label(); + optionComboBox = new System.Windows.Forms.ComboBox(); + commentLabel = new System.Windows.Forms.Label(); + optionTextBox = new System.Windows.Forms.TextBox(); + projListBox = new System.Windows.Forms.CheckedListBox(); + openCheckBox = new System.Windows.Forms.CheckBox(); + lineBox = new System.Windows.Forms.GroupBox(); + createPriFileCheckBox = new System.Windows.Forms.CheckBox(); + panel1 = new System.Windows.Forms.Panel(); + panel2 = new System.Windows.Forms.Panel(); + + newButton = new ImageButton(HelperFunctions.GetSharedImage("QtVsTools.Core.Resources.newitem.png"), + HelperFunctions.GetSharedImage("QtVsTools.Core.Resources.newitem_d.png")); + delButton = new ImageButton(HelperFunctions.GetSharedImage("QtVsTools.Core.Resources.delete.png"), + HelperFunctions.GetSharedImage("QtVsTools.Core.Resources.delete_d.png")); + panel1.SuspendLayout(); + panel2.SuspendLayout(); + SuspendLayout(); + // + // cancelButton + // + cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + cancelButton.Location = new System.Drawing.Point(352, 232); + cancelButton.Name = "cancelButton"; + cancelButton.Size = new System.Drawing.Size(72, 24); + cancelButton.TabIndex = 5; + cancelButton.Text = "Cancel"; + // + // okButton + // + okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + okButton.Location = new System.Drawing.Point(272, 232); + okButton.Name = "okButton"; + okButton.Size = new System.Drawing.Size(72, 24); + okButton.TabIndex = 4; + okButton.Text = "OK"; + // + // projLabel + // + projLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + projLabel.Location = new System.Drawing.Point(0, 8); + projLabel.Name = "projLabel"; + projLabel.Size = new System.Drawing.Size(200, 16); + projLabel.TabIndex = 3; + projLabel.Text = "Create .pro files for:"; + // + // optionListBox + // + optionListBox.Location = new System.Drawing.Point(0, 72); + optionListBox.Name = "optionListBox"; + optionListBox.Size = new System.Drawing.Size(200, 82); + optionListBox.TabIndex = 3; + optionListBox.SelectedIndexChanged += optionListBox_SelectedIndexChanged; + // + // optionLabel + // + optionLabel.Location = new System.Drawing.Point(0, 8); + optionLabel.Name = "optionLabel"; + optionLabel.Size = new System.Drawing.Size(200, 16); + optionLabel.TabIndex = 5; + optionLabel.Text = "Project &tag:"; + // + // optionComboBox + // + optionComboBox.Location = new System.Drawing.Point(0, 24); + optionComboBox.Name = "optionComboBox"; + optionComboBox.Size = new System.Drawing.Size(200, 21); + optionComboBox.TabIndex = 2; + optionComboBox.SelectedIndexChanged += optionComboBox_SelectedIndexChanged; + // + // commentLabel + // + commentLabel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + commentLabel.Location = new System.Drawing.Point(0, 160); + commentLabel.Name = "commentLabel"; + commentLabel.Size = new System.Drawing.Size(200, 48); + commentLabel.TabIndex = 6; + // + // optionTextBox + // + optionTextBox.Enabled = false; + optionTextBox.Location = new System.Drawing.Point(0, 48); + optionTextBox.Name = "optionTextBox"; + optionTextBox.Size = new System.Drawing.Size(136, 20); + optionTextBox.TabIndex = 7; + optionTextBox.Text = ""; + optionTextBox.TextChanged += optionTextBox_TextChanged; + // + // projListBox + // + projListBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + projListBox.Location = new System.Drawing.Point(0, 24); + projListBox.Name = "projListBox"; + projListBox.Size = new System.Drawing.Size(200, 124); + projListBox.TabIndex = 10; + projListBox.SelectedIndexChanged += projListBox_SelectedIndexChanged; + projListBox.ItemCheck += projListBox_ItemCheck; + // + // openCheckBox + // + openCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + openCheckBox.Checked = true; + openCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + openCheckBox.Location = new System.Drawing.Point(0, 184); + openCheckBox.Name = "openCheckBox"; + openCheckBox.Size = new System.Drawing.Size(208, 24); + openCheckBox.TabIndex = 11; + openCheckBox.Text = "Open Created Files"; + // + // lineBox + // + lineBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + lineBox.Location = new System.Drawing.Point(-8, 216); + lineBox.Name = "lineBox"; + lineBox.Size = new System.Drawing.Size(536, 8); + lineBox.TabIndex = 12; + lineBox.TabStop = false; + // + // createPriFileCheckBox + // + createPriFileCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + createPriFileCheckBox.Checked = true; + createPriFileCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + createPriFileCheckBox.Location = new System.Drawing.Point(0, 160); + createPriFileCheckBox.Name = "createPriFileCheckBox"; + createPriFileCheckBox.Size = new System.Drawing.Size(200, 24); + createPriFileCheckBox.TabIndex = 13; + createPriFileCheckBox.Text = "Create .pri File"; + // + // panel1 + // + panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + panel1.Controls.Add(projLabel); + panel1.Controls.Add(projListBox); + panel1.Controls.Add(createPriFileCheckBox); + panel1.Controls.Add(openCheckBox); + panel1.Location = new System.Drawing.Point(8, 0); + panel1.Name = "panel1"; + panel1.Size = new System.Drawing.Size(200, 208); + panel1.TabIndex = 14; + // + // panel2 + // + panel2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + panel2.Controls.Add(newButton); + panel2.Controls.Add(delButton); + panel2.Controls.Add(optionLabel); + panel2.Controls.Add(optionComboBox); + panel2.Controls.Add(optionTextBox); + panel2.Controls.Add(optionListBox); + panel2.Controls.Add(commentLabel); + panel2.Location = new System.Drawing.Point(216, 0); + panel2.Name = "panel2"; + panel2.Size = new System.Drawing.Size(208, 208); + panel2.TabIndex = 15; + // + // newButton + // + newButton.Location = new System.Drawing.Point(144, 48); + newButton.Name = "button1"; + newButton.Size = new System.Drawing.Size(24, 23); + newButton.TabIndex = 8; + newButton.Click += newButton_Click; + + // + // delButton + // + delButton.Location = new System.Drawing.Point(176, 48); + delButton.Name = "button2"; + delButton.Size = new System.Drawing.Size(24, 23); + delButton.TabIndex = 9; + delButton.Click += delButton_Click; + + // + // ExportProjectDialog + // + AcceptButton = okButton; + CancelButton = cancelButton; + AutoScaleBaseSize = new System.Drawing.Size(5, 13); + ClientSize = new System.Drawing.Size(432, 262); + Controls.Add(panel2); + Controls.Add(panel1); + Controls.Add(lineBox); + Controls.Add(okButton); + Controls.Add(cancelButton); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + MaximizeBox = false; + MinimizeBox = false; + Name = "ExportProjectDialog"; + SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + Text = "Export Project"; + Load += ExportProjectDialog_Load; + panel1.ResumeLayout(false); + panel2.ResumeLayout(false); + ResumeLayout(false); + + } + #endregion + + public ProSolution ProFileSolution + { + set + { + proSln = value; + InitProSolution(); + } + } + + private void InitProSolution() + { + projListBox.DataSource = proSln.ProFiles; + projListBox.SelectedIndex = 0; + } + + private ProSolution proSln; + private ProFileContent currentPro; + private ProFileOption currentOpt; + + private void optionComboBox_SelectedIndexChanged(object sender, System.EventArgs e) + { + currentOpt = currentPro.Options[optionComboBox.SelectedIndex]; + UpdateCurrentListItem(); + + optionTextBox.Text = string.Empty; + + // update comment field + commentLabel.Text = currentOpt.Comment; + UpdateButtons(); + } + + private void optionListBox_SelectedIndexChanged(object sender, System.EventArgs e) + { + var idx = optionListBox.SelectedIndex; + if (idx < 0) + return; + optionTextBox.Text = currentOpt.List[optionListBox.SelectedIndex]; + optionTextBox.Focus(); + UpdateButtons(); + } + + private void optionTextBox_TextChanged(object sender, System.EventArgs e) + { + if (optionListBox.SelectedIndex < 0) { + optionTextBox.Enabled = false; + } else { + optionTextBox.Enabled = true; + currentOpt.List[optionListBox.SelectedIndex] = optionTextBox.Text; + var index = optionListBox.SelectedIndex; + UpdateCurrentListItem(); + optionListBox.SelectedIndex = index; + } + } + + private void UpdateCurrentListItem() + { + optionListBox.BeginUpdate(); + optionListBox.Items.Clear(); + foreach (var tag in currentOpt.List) + optionListBox.Items.Add(tag); + optionListBox.EndUpdate(); + } + + private void newButton_Click(object sender, System.EventArgs e) + { + currentOpt.List.Add("{New}"); + var index = currentOpt.List.Count - 1; + UpdateCurrentListItem(); + optionListBox.SelectedIndex = index; + optionTextBox.SelectAll(); + } + + private void UpdateButtons() + { + var delEnabled = true; + var addEnabled = true; + + if (optionListBox.SelectedIndex < 0) + delEnabled = false; + + if (optionListBox.Items.Count <= 0) + delEnabled = false; + + if ((optionListBox.Items.Count > 0) && (currentOpt.NewOption == null)) + addEnabled = false; + + delButton.Enabled = delEnabled; + newButton.Enabled = addEnabled; + } + + private void delButton_Click(object sender, System.EventArgs e) + { + var index = optionListBox.SelectedIndex; + currentOpt.List.RemoveAt(optionListBox.SelectedIndex); + UpdateCurrentListItem(); + + if (index > (optionListBox.Items.Count - 1)) + index--; + + optionListBox.SelectedIndex = index; + + if (index < 0) { + optionTextBox.Text = string.Empty; + UpdateButtons(); + } + } + + private void projListBox_SelectedIndexChanged(object sender, System.EventArgs e) + { + currentPro = proSln.ProFiles[projListBox.SelectedIndex]; + optionComboBox.DataSource = currentPro.Options; + } + + private void projListBox_ItemCheck(object sender, ItemCheckEventArgs e) + { + if (e.NewValue == CheckState.Checked) + proSln.ProFiles[e.Index].Export = true; + else + proSln.ProFiles[e.Index].Export = false; + } + + private void ExportProjectDialog_Load(object sender, System.EventArgs e) + { + for (var i = 0; i < projListBox.Items.Count; i++) + projListBox.SetItemChecked(i, true); + } + + public bool OpenFiles + { + get { return openCheckBox.Checked; } + } + + public bool CreatePriFile + { + get { return createPriFileCheckBox.Checked; } + } + } +} diff --git a/QtVsTools.Core/ExportProjectDialog.resx b/QtVsTools.Core/ExportProjectDialog.resx new file mode 100644 index 0000000..3ddca09 --- /dev/null +++ b/QtVsTools.Core/ExportProjectDialog.resx @@ -0,0 +1,301 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 1.3 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">1.3</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1">this is my long string</data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + [base64 mime encoded serialized .NET Framework object] + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + [base64 mime encoded string representing a byte array form of the .NET Framework object] + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used forserialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>1.3</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="cancelButton.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="cancelButton.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="cancelButton.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="okButton.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="okButton.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="okButton.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="projLabel.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="projLabel.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="projLabel.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionListBox.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionListBox.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="optionListBox.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionLabel.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="optionLabel.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionLabel.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionComboBox.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionComboBox.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="optionComboBox.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="commentLabel.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="commentLabel.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="commentLabel.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionTextBox.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="optionTextBox.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="optionTextBox.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="projListBox.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="projListBox.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="projListBox.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="openCheckBox.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="openCheckBox.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="openCheckBox.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="lineBox.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="lineBox.GridSize" type="System.Drawing.Size, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>8, 8</value> + </data> + <data name="lineBox.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="lineBox.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="lineBox.DrawGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="lineBox.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="createPriFileCheckBox.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="createPriFileCheckBox.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="createPriFileCheckBox.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="panel1.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="panel1.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="panel1.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="panel1.GridSize" type="System.Drawing.Size, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>8, 8</value> + </data> + <data name="panel1.DrawGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="panel1.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="panel2.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="panel2.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="panel2.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="panel2.GridSize" type="System.Drawing.Size, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>8, 8</value> + </data> + <data name="panel2.DrawGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="panel2.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="button1.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="button1.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="button1.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="button2.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="button2.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="button2.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> + <data name="$this.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="$this.Language" type="System.Globalization.CultureInfo, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>(Default)</value> + </data> + <data name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="$this.Localizable" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </data> + <data name="$this.GridSize" type="System.Drawing.Size, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>8, 8</value> + </data> + <data name="$this.DrawGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="$this.TrayHeight" type="System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>80</value> + </data> + <data name="$this.Name"> + <value>ExportProjectDialog</value> + </data> + <data name="$this.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>True</value> + </data> + <data name="$this.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>Private</value> + </data> +</root> \ No newline at end of file diff --git a/QtVsTools.Core/Extensions.cs b/QtVsTools.Core/Extensions.cs new file mode 100644 index 0000000..4f14aa6 --- /dev/null +++ b/QtVsTools.Core/Extensions.cs @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; + +namespace QtVsTools.Core +{ + public static class Extensions + { + public static string Quoute(this string input) + { + if (!input.StartsWith("\"", StringComparison.Ordinal)) + input = "\"" + input; + if (!input.EndsWith("\"", StringComparison.Ordinal)) + input += "\""; + return input; + } + + public static string Replace(this string original, string oldValue, string newValue, + StringComparison comparison) + { + newValue = newValue ?? string.Empty; + if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(oldValue) + || string.Equals(oldValue, newValue, comparison)) { + return original; + } + + int pos = 0, index = 0; + var result = new System.Text.StringBuilder(); + while ((index = original.IndexOf(oldValue, pos, comparison)) >= 0) { + result.Append(original, pos, index - pos).Append(newValue); + pos = index + oldValue.Length; + } + return result.Append(original, pos, original.Length - pos).ToString(); + } + } +} diff --git a/QtVsTools.Core/FakeFilter.cs b/QtVsTools.Core/FakeFilter.cs new file mode 100644 index 0000000..8851174 --- /dev/null +++ b/QtVsTools.Core/FakeFilter.cs @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + public class FakeFilter + { + public string Name { get; set; } + public string Filter { get; set; } + public string UniqueIdentifier { get; set; } + + private bool parseFiles = true; + public bool ParseFiles + { + get { return parseFiles; } + set { parseFiles = value; } + } + } +} diff --git a/QtVsTools.Core/FilesToList.cs b/QtVsTools.Core/FilesToList.cs new file mode 100644 index 0000000..b290b73 --- /dev/null +++ b/QtVsTools.Core/FilesToList.cs @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + public enum FilesToList + { + FL_Resources = 1, + FL_CppFiles = 2, + FL_HFiles = 3, + FL_UiFiles = 4, + FL_Generated = 5, + FL_Translation = 6, + FL_WinResource = 7, + FL_QmlFiles = 8 + } +} diff --git a/QtVsTools.Core/Filters.cs b/QtVsTools.Core/Filters.cs new file mode 100644 index 0000000..ab55d55 --- /dev/null +++ b/QtVsTools.Core/Filters.cs @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + public static class Filters + { + public static FakeFilter SourceFiles() + { + return new FakeFilter + { + UniqueIdentifier = "{4FC737F1-C7A5-4376-A066-2A32D752A2FF}", + Name = SR.GetString("Resources_SourceFiles"), + Filter = "cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + }; + } + + public static FakeFilter HeaderFiles() + { + return new FakeFilter + { + UniqueIdentifier = "{93995380-89BD-4b04-88EB-625FBE52EBFB}", + Name = SR.GetString("Resources_HeaderFiles"), + Filter = "h;hh;hpp;hxx;hm;inl;inc;xsd" + }; + } + + public static FakeFilter FormFiles() + { + return new FakeFilter + { + UniqueIdentifier = "{99349809-55BA-4b9d-BF79-8FDBB0286EB3}", + Name = SR.GetString("Resources_FormFiles"), + Filter = "ui" + }; + } + + public static FakeFilter ResourceFiles() + { + return new FakeFilter + { + UniqueIdentifier = "{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}", + Name = SR.GetString("Resources_ResourceFiles"), + ParseFiles = false, + Filter = "qrc;*" + }; + } + + public static FakeFilter TranslationFiles() + { + return new FakeFilter + { + UniqueIdentifier = "{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}", + Name = SR.GetString("Resources_TranslationFiles"), + Filter = "ts", + ParseFiles = false + }; + } + + public static FakeFilter GeneratedFiles() + { + return new FakeFilter + { + UniqueIdentifier = "{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}", + Name = SR.GetString("Resources_GeneratedFiles"), + Filter = "moc;h;cpp", + }; + } + + public static FakeFilter OtherFiles() + { + return new FakeFilter + { + UniqueIdentifier = "{B67473BF-9FA1-4674-831E-CB28F72D4791}", + Name = SR.GetString("Resources_OtherFiles"), + Filter = "*", + }; + } + } +} diff --git a/QtVsTools.Core/HelperFunctions.cs b/QtVsTools.Core/HelperFunctions.cs new file mode 100644 index 0000000..b6fe0ff --- /dev/null +++ b/QtVsTools.Core/HelperFunctions.cs @@ -0,0 +1,1918 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.VCProjectEngine; +using Microsoft.Win32; +using QtVsTools.Core.QtMsBuild; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; + +using Process = System.Diagnostics.Process; + +namespace QtVsTools.Core +{ + public static class HelperFunctions + { + public static string FindQtDirWithTools(Project project) + { + var versionManager = QtVersionManager.The(); + string projectQtVersion = null; + if (IsQtProject(project)) + projectQtVersion = versionManager.GetProjectQtVersion(project); + return FindQtDirWithTools(projectQtVersion); + } + + public static string FindQtDirWithTools(string projectQtVersion) + { + string tool = null; + return FindQtDirWithTools(tool, projectQtVersion); + } + + public static string FindQtDirWithTools(string tool, string projectQtVersion) + { + if (!string.IsNullOrEmpty(tool)) { + if (!tool.StartsWith("\\bin\\", StringComparison.OrdinalIgnoreCase)) + tool = "\\bin\\" + tool; + if (!tool.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) + tool += ".exe"; + } + + var versionManager = QtVersionManager.The(); + string qtDir = null; + if (projectQtVersion != null) + qtDir = versionManager.GetInstallPath(projectQtVersion); + + if (qtDir == null) + qtDir = Environment.GetEnvironmentVariable("QTDIR"); + + var found = false; + if (tool == null) { + found = File.Exists(qtDir + "\\bin\\designer.exe") + && File.Exists(qtDir + "\\bin\\linguist.exe"); + } else { + found = File.Exists(qtDir + tool); + } + if (!found) { + VersionInformation exactlyMatchingVersion = null; + VersionInformation matchingVersion = null; + VersionInformation somehowMatchingVersion = null; + var viProjectQtVersion = versionManager.GetVersionInfo(projectQtVersion); + foreach (var qtVersion in versionManager.GetVersions()) { + var vi = versionManager.GetVersionInfo(qtVersion); + if (tool == null) { + found = File.Exists(vi.qtDir + "\\bin\\designer.exe") + && File.Exists(vi.qtDir + "\\bin\\linguist.exe"); + } else { + found = File.Exists(vi.qtDir + tool); + } + if (!found) + continue; + + if (viProjectQtVersion != null + && vi.qtMajor == viProjectQtVersion.qtMajor + && vi.qtMinor == viProjectQtVersion.qtMinor) { + exactlyMatchingVersion = vi; + break; + } + if (matchingVersion == null + && viProjectQtVersion != null + && vi.qtMajor == viProjectQtVersion.qtMajor) { + matchingVersion = vi; + } + if (somehowMatchingVersion == null) + somehowMatchingVersion = vi; + } + + if (exactlyMatchingVersion != null) + qtDir = exactlyMatchingVersion.qtDir; + else if (matchingVersion != null) + qtDir = matchingVersion.qtDir; + else if (somehowMatchingVersion != null) + qtDir = somehowMatchingVersion.qtDir; + else + qtDir = null; + } + return qtDir; + } + + static readonly HashSet<string> _sources = new HashSet<string>(new[] { ".c", ".cpp", ".cxx" }, + StringComparer.OrdinalIgnoreCase); + static public bool IsSourceFile(string fileName) + { + return _sources.Contains(Path.GetExtension(fileName)); + } + + static readonly HashSet<string> _headers = new HashSet<string>(new[] { ".h", ".hpp", ".hxx" }, + StringComparer.OrdinalIgnoreCase); + static public bool IsHeaderFile(string fileName) + { + return _headers.Contains(Path.GetExtension(fileName)); + } + + public static bool IsUicFile(string fileName) + { + return ".ui".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsMocFile(string fileName) + { + return ".moc".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsQrcFile(string fileName) + { + return ".qrc".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsWinRCFile(string fileName) + { + return ".rc".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsTranslationFile(string fileName) + { + return ".ts".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase); + } + + public static bool IsQmlFile(string fileName) + { + return ".qml".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase); + } + + static public void SetDebuggingEnvironment(Project prj) + { + SetDebuggingEnvironment(prj, string.Empty); + } + + static public void SetDebuggingEnvironment(Project prj, string solutionConfig) + { + SetDebuggingEnvironment(prj, "PATH=$(QTDIR)\\bin;$(PATH)", false, solutionConfig); + } + + static public void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite) + { + SetDebuggingEnvironment(prj, envpath, overwrite, string.Empty); + } + + static public void SetDebuggingEnvironment(Project prj, string envpath, bool overwrite, string solutionConfig) + { + if (QtProject.GetFormatVersion(prj) >= Resources.qtMinFormatVersion_Settings) + return; + + // Get platform name from given solution configuration + // or if not available take the active configuration + var activePlatformName = string.Empty; + if (string.IsNullOrEmpty(solutionConfig)) { + // First get active configuration cause not given as parameter + try { + var activeConf = prj.ConfigurationManager.ActiveConfiguration; + activePlatformName = activeConf.PlatformName; + } catch { + Messages.Print("Could not get the active platform name."); + } + } else { + activePlatformName = solutionConfig.Split('|')[1]; + } + + var vcprj = prj.Object as VCProject; + foreach (VCConfiguration conf in vcprj.Configurations as IVCCollection) { + // Set environment only for active (or given) platform + var currentPlatform = conf.Platform as VCPlatform; + if (currentPlatform == null || currentPlatform.Name != activePlatformName) + continue; + + var de = conf.DebugSettings as VCDebugSettings; + if (de == null) + continue; + + // See: https://connect.microsoft.com/VisualStudio/feedback/details/619702 + // Project | Properties | Configuration Properties | Debugging | Environment + // + // Issue: Substitution of ";" to "%3b" + // Answer: This behavior currently is by design as ';' is a special MSBuild + // character and needs to be escaped. In the Project Properties we show this + // escaped value, but it should be the original when we use it. + envpath = envpath.Replace("%3b", ";"); + de.Environment = de.Environment.Replace("%3b", ";"); + + var index = envpath.LastIndexOf(";$(PATH)", StringComparison.Ordinal); + var withoutPath = (index >= 0 ? envpath.Remove(index) : envpath); + + if (overwrite || string.IsNullOrEmpty(de.Environment)) + de.Environment = envpath; + else if (!de.Environment.Contains(envpath) && !de.Environment.Contains(withoutPath)) { + var m = Regex.Match(de.Environment, "PATH\\s*=\\s*"); + if (m.Success) { + de.Environment = Regex.Replace(de.Environment, "PATH\\s*=\\s*", withoutPath + ";"); + if (!de.Environment.Contains("$(PATH)") && !de.Environment.Contains("%PATH%")) { + if (!de.Environment.EndsWith(";", StringComparison.Ordinal)) + de.Environment = de.Environment + ";"; + de.Environment += "$(PATH)"; + } + } else { + if (!string.IsNullOrEmpty(de.Environment)) + de.Environment += "\n"; + de.Environment += envpath; + } + } + } + } + + public static bool IsProjectInSolution(DTE dteObject, string fullName) + { + var fi = new FileInfo(fullName); + + foreach (var p in ProjectsInSolution(dteObject)) { + if (p.FullName.ToLower() == fi.FullName.ToLower()) + return true; + } + return false; + } + + /// <summary> + /// Returns the normalized file path of a given file. + /// </summary> + /// <param name="name">file name</param> + static public string NormalizeFilePath(string name) + { + var fi = new FileInfo(name); + return fi.FullName; + } + + static public string NormalizeRelativeFilePath(string path) + { + if (path == null) + return ".\\"; + + path = path.Trim(); + path = path.Replace("/", "\\"); + + var tmp = string.Empty; + while (tmp != path) { + tmp = path; + path = path.Replace("\\\\", "\\"); + } + + path = path.Replace("\"", ""); + + if (path != "." && !IsAbsoluteFilePath(path) && !path.StartsWith(".\\", StringComparison.Ordinal) + && !path.StartsWith("$", StringComparison.Ordinal)) + path = ".\\" + path; + + if (path.EndsWith("\\", StringComparison.Ordinal)) + path = path.Substring(0, path.Length - 1); + + return path; + } + + static public bool IsAbsoluteFilePath(string path) + { + path = path.Trim(); + if (path.Length >= 2 && path[1] == ':') + return true; + if (path.StartsWith("\\", StringComparison.Ordinal) || path.StartsWith("/", StringComparison.Ordinal)) + return true; + + return false; + } + + /// <summary> + /// Reads lines from a .pro file that is opened with a StreamReader + /// and concatenates strings that end with a backslash. + /// </summary> + /// <param name="streamReader"></param> + /// <returns>the composite string</returns> + static private string ReadProFileLine(StreamReader streamReader) + { + var line = streamReader.ReadLine(); + if (line == null) + return null; + + line = line.TrimEnd(' ', '\t'); + while (line.EndsWith("\\", StringComparison.Ordinal)) { + line = line.Remove(line.Length - 1); + var appendix = streamReader.ReadLine(); + if (appendix != null) + line += appendix.TrimEnd(' ', '\t'); + } + return line; + } + + /// <summary> + /// Reads a .pro file and returns true if it is a subdirs template. + /// </summary> + /// <param name="profile">full name of .pro file to read</param> + /// <returns>true if this is a subdirs file</returns> + static public bool IsSubDirsFile(string profile) + { + StreamReader sr = null; + try { + sr = new StreamReader(profile); + + var line = string.Empty; + while ((line = ReadProFileLine(sr)) != null) { + line = line.Replace(" ", string.Empty).Replace("\t", string.Empty); + if (line.StartsWith("TEMPLATE", StringComparison.Ordinal)) + return line.StartsWith("TEMPLATE=subdirs", StringComparison.Ordinal); + } + } catch (Exception e) { + Messages.DisplayErrorMessage(e); + } finally { + if (sr != null) + sr.Dispose(); + } + return false; + } + + /// <summary> + /// Returns the relative path between a given file and a path. + /// </summary> + /// <param name="path">absolute path</param> + /// <param name="file">absolute file name</param> + public static string GetRelativePath(string path, string file) + { + if (file == null || path == null) + return ""; + var fi = new FileInfo(file); + var di = new DirectoryInfo(path); + + char[] separator = { '\\' }; + var fiArray = fi.FullName.Split(separator); + var dir = di.FullName; + while (dir.EndsWith("\\", StringComparison.Ordinal)) + dir = dir.Remove(dir.Length - 1, 1); + var diArray = dir.Split(separator); + + var minLen = fiArray.Length < diArray.Length ? fiArray.Length : diArray.Length; + int i = 0, j = 0, commonParts = 0; + + while (i < minLen && fiArray[i].ToLower() == diArray[i].ToLower()) { + commonParts++; + i++; + } + + if (commonParts < 1) + return fi.FullName; + + var result = string.Empty; + + for (j = i; j < fiArray.Length; j++) { + if (j == i) + result = fiArray[j]; + else + result += "\\" + fiArray[j]; + } + while (i < diArray.Length) { + result = "..\\" + result; + i++; + } + //MessageBox.Show(path + "\n" + file + "\n" + result); + if (result.StartsWith("..\\", StringComparison.Ordinal)) + return result; + return ".\\" + result; + } + + /// <summary> + /// Replaces a string in the commandLine, description, outputs and additional dependencies + /// in all Custom build tools of the project + /// </summary> + /// <param name="project">Project</param> + /// <param name="oldString">String, which is going to be replaced</param> + /// <param name="oldString">String, which is going to replace the other one</param> + /// <returns></returns> + public static void ReplaceInCustomBuildTools(Project project, string oldString, string replaceString) + { + var vcPro = (VCProject)project.Object; + if (vcPro == null) + return; + + var qtMsBuild = new QtMsBuildContainer(new VCPropertyStorageProvider()); + qtMsBuild.BeginSetItemProperties(); + foreach (VCFile vcfile in (IVCCollection)vcPro.Files) { + foreach (VCFileConfiguration config in (IVCCollection)vcfile.FileConfigurations) { + try { + if (vcfile.ItemType == "CustomBuild") { + var tool = GetCustomBuildTool(config); + if (tool == null) + continue; + + tool.CommandLine = tool.CommandLine + .Replace(oldString, replaceString, + StringComparison.OrdinalIgnoreCase); + tool.Description = tool.Description + .Replace(oldString, replaceString, + StringComparison.OrdinalIgnoreCase); + tool.Outputs = tool.Outputs + .Replace(oldString, replaceString, + StringComparison.OrdinalIgnoreCase); + tool.AdditionalDependencies = tool.AdditionalDependencies + .Replace(oldString, replaceString, + StringComparison.OrdinalIgnoreCase); + } else { + var tool = new QtCustomBuildTool(config, qtMsBuild); + tool.CommandLine = tool.CommandLine + .Replace(oldString, replaceString, + StringComparison.OrdinalIgnoreCase); + } + } catch (Exception) { + } + } + } + qtMsBuild.EndSetItemProperties(); + } + + /// <summary> + /// Since VS2010 it is possible to have VCCustomBuildTools without commandlines + /// for certain filetypes. We are not interested in them and thus try to read the + /// tool's commandline. If this causes an exception, we ignore it. + /// There does not seem to be another way for checking which kind of tool it is. + /// </summary> + /// <param name="config">File configuration</param> + /// <returns></returns> + static public VCCustomBuildTool GetCustomBuildTool(VCFileConfiguration config) + { + var file = config.File as VCFile; + if (file == null || file.ItemType != "CustomBuild") + return null; + + var tool = config.Tool as VCCustomBuildTool; + if (tool == null) + return null; + + try { + // TODO: The return value is not used at all? + var cmdLine = tool.CommandLine; + } catch { + return null; + } + return tool; + } + + /// <summary> + /// Since VS2010 we have to ensure, that a custom build tool is present + /// if we want to use it. In order to do so, the ProjectItem's ItemType + /// has to be "CustomBuild" + /// </summary> + /// <param name="projectItem">Project Item which needs to have custom build tool</param> + static public void EnsureCustomBuildToolAvailable(ProjectItem projectItem) + { + foreach (Property prop in projectItem.Properties) { + if (prop.Name == "ItemType") { + if ((string)prop.Value != "CustomBuild") + prop.Value = "CustomBuild"; + break; + } + } + } + + /// <summary> + /// As Qmake -tp vc Adds the full path to the additional dependencies + /// we need to do the same when toggling project kind to qmake generated. + /// </summary> + /// <returns></returns> + private static string AddFullPathToAdditionalDependencies(string qtDir, string additionalDependencies) + { + var returnString = additionalDependencies; + returnString = + Regex.Replace(returnString, "Qt(\\S+5?)\\.lib", qtDir + "\\lib\\Qt${1}.lib"); + returnString = + Regex.Replace(returnString, "(qtmaind?5?)\\.lib", qtDir + "\\lib\\${1}.lib"); + returnString = + Regex.Replace(returnString, "(enginiod?5?)\\.lib", qtDir + "\\lib\\${1}.lib"); + return returnString; + } + + /// <summary> + /// Toggles the kind of a project. If the project is a QMake generated project (qmake -tp vc) + /// it is transformed to an Qt VS Tools project and vice versa. + /// </summary> + /// <param name="project">Project</param> + /// <returns></returns> + public static void ToggleProjectKind(Project project) + { + if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) + return; + + string qtDir = null; + var vcPro = (VCProject)project.Object; + if (!IsQMakeProject(project)) + return; + if (IsQtProject(project)) { + // TODO: qtPro is never used. + var qtPro = QtProject.Create(project); + var vm = QtVersionManager.The(); + qtDir = vm.GetInstallPath(project); + + foreach (var global in (string[])project.Globals.VariableNames) { + if (global.StartsWith("Qt5Version", StringComparison.Ordinal)) + project.Globals.set_VariablePersists(global, false); + } + + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + var librarian = (VCLibrarianTool)((IVCCollection)config.Tools).Item("VCLibrarianTool"); + if (compiler != null) { + var additionalIncludes = compiler.GetAdditionalIncludeDirectories(); + additionalIncludes = additionalIncludes.Replace("$(QTDIR)", qtDir, + StringComparison.OrdinalIgnoreCase); + compiler.SetAdditionalIncludeDirectories(additionalIncludes); + } + if (linker != null) { + linker.AdditionalLibraryDirectories = linker.AdditionalLibraryDirectories. + Replace("$(QTDIR)", qtDir, StringComparison.OrdinalIgnoreCase); + linker.AdditionalDependencies = AddFullPathToAdditionalDependencies(qtDir, linker.AdditionalDependencies); + } else { + librarian.AdditionalLibraryDirectories = librarian.AdditionalLibraryDirectories + .Replace("$(QTDIR)", qtDir, StringComparison.OrdinalIgnoreCase); + librarian.AdditionalDependencies = AddFullPathToAdditionalDependencies(qtDir, librarian.AdditionalDependencies); + } + } + + ReplaceInCustomBuildTools(project, "$(QTDIR)", qtDir); + } else { + qtDir = GetQtDirFromQMakeProject(project); + + var vm = QtVersionManager.The(); + var qtVersion = vm.GetQtVersionFromInstallDir(qtDir); + if (qtVersion == null) + qtVersion = vm.GetDefaultVersion(); + if (qtDir == null) + qtDir = vm.GetInstallPath(qtVersion); + var vi = vm.GetVersionInfo(qtVersion); + var platformName = vi.GetVSPlatformName(); + vm.SaveProjectQtVersion(project, qtVersion, platformName); + var qtPro = QtProject.Create(project); + if (!qtPro.SelectSolutionPlatform(platformName) || !qtPro.HasPlatform(platformName)) { + var newProject = false; + qtPro.CreatePlatform("Win32", platformName, null, vi, ref newProject); + if (!qtPro.SelectSolutionPlatform(platformName)) + Messages.Print("Can't select the platform " + platformName + "."); + } + + var activeConfig = project.ConfigurationManager.ActiveConfiguration.ConfigurationName; + var activeVCConfig = (VCConfiguration)((IVCCollection)qtPro.VCProject.Configurations).Item(activeConfig); + if (activeVCConfig.ConfigurationType == ConfigurationTypes.typeDynamicLibrary) { + var compiler = CompilerToolWrapper.Create(activeVCConfig); + var linker = (VCLinkerTool)((IVCCollection)activeVCConfig.Tools).Item("VCLinkerTool"); + var ppdefs = compiler.GetPreprocessorDefinitions(); + if (ppdefs != null + && ppdefs.IndexOf("QT_PLUGIN", StringComparison.Ordinal) > -1 + && ppdefs.IndexOf("QDESIGNER_EXPORT_WIDGETS", StringComparison.Ordinal) > -1 + && ppdefs.IndexOf("QtDesigner", StringComparison.Ordinal) > -1 + && linker.AdditionalDependencies != null + && linker.AdditionalDependencies.IndexOf("QtDesigner", StringComparison.Ordinal) > -1) { + qtPro.MarkAsDesignerPluginProject(); + } + } + + CleanupQMakeDependencies(project); + + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + + if (compiler != null) { + var additionalIncludes = compiler.AdditionalIncludeDirectories; + if (additionalIncludes != null) { + ReplaceDirectory(ref additionalIncludes, qtDir, "$(QTDIR)", project); + compiler.AdditionalIncludeDirectories = additionalIncludes; + } + } + if (linker != null) { + var linkerToolWrapper = new LinkerToolWrapper(linker); + var paths = linkerToolWrapper.AdditionalLibraryDirectories; + if (paths != null) { + ReplaceDirectory(ref paths, qtDir, "$(QTDIR)", project); + linkerToolWrapper.AdditionalLibraryDirectories = paths; + } + } + } + + ReplaceInCustomBuildTools(project, qtDir, "$(QTDIR)"); + qtPro.TranslateFilterNames(); + } + project.Save(project.FullName); + } + + /// <summary> + /// Replaces every occurrence of oldDirectory with replacement in the array of strings. + /// Parameter oldDirectory must be an absolute path. + /// This function converts relative directories to absolute paths internally + /// and replaces them, if necessary. If no replacement is done, the path isn't altered. + /// </summary> + /// <param name="project">The project is needed to convert relative paths to absolute paths.</param> + private static void ReplaceDirectory(ref List<string> paths, string oldDirectory, string replacement, Project project) + { + for (var i = 0; i < paths.Count; ++i) { + var dirName = paths[i]; + if (dirName.StartsWith("\"", StringComparison.Ordinal) && dirName.EndsWith("\"", StringComparison.Ordinal)) { + dirName = dirName.Substring(1, dirName.Length - 2); + } + if (!Path.IsPathRooted(dirName)) { + // convert to absolute path + dirName = Path.Combine(Path.GetDirectoryName(project.FullName), dirName); + dirName = Path.GetFullPath(dirName); + var alteredDirName = dirName.Replace(oldDirectory, replacement, StringComparison + .OrdinalIgnoreCase); + if (alteredDirName == dirName) + continue; + dirName = alteredDirName; + } else { + dirName = dirName.Replace(oldDirectory, replacement, StringComparison + .OrdinalIgnoreCase); + } + paths[i] = dirName; + } + } + + public static string GetQtDirFromQMakeProject(Project project) + { + var vcProject = project.Object as VCProject; + if (vcProject == null) + return null; + + try { + foreach (VCConfiguration projectConfig in vcProject.Configurations as IVCCollection) { + var compiler = CompilerToolWrapper.Create(projectConfig); + if (compiler != null) { + var additionalIncludeDirectories = compiler.AdditionalIncludeDirectories; + if (additionalIncludeDirectories != null) { + foreach (var dir in additionalIncludeDirectories) { + var subdir = Path.GetFileName(dir); + if (subdir != "QtCore" && subdir != "QtGui") // looking for Qt include directories + continue; + var dirName = Path.GetDirectoryName(dir); // cd .. + dirName = Path.GetDirectoryName(dirName); // cd .. + if (!Path.IsPathRooted(dirName)) { + var projectDir = Path.GetDirectoryName(project.FullName); + dirName = Path.Combine(projectDir, dirName); + dirName = Path.GetFullPath(dirName); + } + return dirName; + } + } + } + + var linker = (VCLinkerTool)((IVCCollection)projectConfig.Tools).Item("VCLinkerTool"); + if (linker != null) { + var linkerWrapper = new LinkerToolWrapper(linker); + var linkerPaths = linkerWrapper.AdditionalDependencies; + if (linkerPaths != null) { + foreach (var library in linkerPaths) { + var idx = library.IndexOf("\\lib\\qtmain.lib", StringComparison.OrdinalIgnoreCase); + if (idx == -1) + idx = library.IndexOf("\\lib\\qtmaind.lib", StringComparison.OrdinalIgnoreCase); + if (idx == -1) + idx = library.IndexOf("\\lib\\qtcore5.lib", StringComparison.OrdinalIgnoreCase); + if (idx == -1) + idx = library.IndexOf("\\lib\\qtcored5.lib", StringComparison.OrdinalIgnoreCase); + if (idx == -1) + continue; + + var dirName = Path.GetDirectoryName(library); + dirName = Path.GetDirectoryName(dirName); // cd .. + if (!Path.IsPathRooted(dirName)) { + var projectDir = Path.GetDirectoryName(project.FullName); + dirName = Path.Combine(projectDir, dirName); + dirName = Path.GetFullPath(dirName); + } + + return dirName; + } + } + + linkerPaths = linkerWrapper.AdditionalLibraryDirectories; + if (linkerPaths != null) { + foreach (var libDir in linkerPaths) { + var dirName = libDir; + if (!Path.IsPathRooted(dirName)) { + var projectDir = Path.GetDirectoryName(project.FullName); + dirName = Path.Combine(projectDir, dirName); + dirName = Path.GetFullPath(dirName); + } + + if (File.Exists(dirName + "\\qtmain.lib") || + File.Exists(dirName + "\\qtmaind.lib") || + File.Exists(dirName + "\\QtCore5.lib") || + File.Exists(dirName + "\\QtCored5.lib")) { + return Path.GetDirectoryName(dirName); + } + } + } + } + } + } catch { } + + return null; + } + + /// <summary> + /// Return true if the project is a Qt project, otherwise false. + /// </summary> + /// <param name="proj">project</param> + /// <returns></returns> + public static bool IsQtProject(VCProject proj) + { + if (!IsQMakeProject(proj)) + return false; + + if (QtProject.GetFormatVersion(proj) >= Resources.qtMinFormatVersion_Settings) + return true; + + var envPro = proj.Object as Project; + if (envPro.Globals == null || envPro.Globals.VariableNames == null) + return false; + + foreach (var global in envPro.Globals.VariableNames as string[]) { + if (global.StartsWith("Qt5Version", StringComparison.Ordinal) && envPro.Globals.get_VariablePersists(global)) + return true; + } + return false; + } + + /// <summary> + /// Returns true if the specified project is a Qt Project. + /// </summary> + /// <param name="proj">project</param> + public static bool IsQtProject(Project proj) + { + try { + if (proj != null && proj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") + return IsQtProject(proj.Object as VCProject); + } catch { } + return false; + } + + /// <summary> + /// Return true if the project is a QMake -tp vc project, otherwise false. + /// </summary> + /// <param name="proj">project</param> + /// <returns></returns> + public static bool IsQMakeProject(VCProject proj) + { + if (proj == null) + return false; + var keyword = proj.keyword; + if (keyword == null || + (!keyword.StartsWith(Resources.qtProjectV2Keyword, StringComparison.Ordinal) + && !keyword.StartsWith(Resources.qtProjectKeyword, StringComparison.Ordinal))) { + return false; + } + + return true; + } + + /// <summary> + /// Returns true if the specified project is a QMake -tp vc Project. + /// </summary> + /// <param name="proj">project</param> + public static bool IsQMakeProject(Project proj) + { + try { + if (proj != null && proj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") + return IsQMakeProject(proj.Object as VCProject); + } catch { } + return false; + } + + public static void CleanupQMakeDependencies(Project project) + { + var vcPro = (VCProject)project.Object; + // clean up qmake mess + var rxp1 = new Regex("\\bQt\\w+d?5?\\.lib\\b"); + var rxp2 = new Regex("\\bQAx\\w+\\.lib\\b"); + var rxp3 = new Regex("\\bqtmaind?.lib\\b"); + var rxp4 = new Regex("\\benginiod?.lib\\b"); + foreach (VCConfiguration cfg in (IVCCollection)vcPro.Configurations) { + var linker = (VCLinkerTool)((IVCCollection)cfg.Tools).Item("VCLinkerTool"); + if (linker == null || linker.AdditionalDependencies == null) + continue; + var linkerWrapper = new LinkerToolWrapper(linker); + var deps = linkerWrapper.AdditionalDependencies; + var newDeps = new List<string>(); + foreach (var lib in deps) { + var m1 = rxp1.Match(lib); + var m2 = rxp2.Match(lib); + var m3 = rxp3.Match(lib); + var m4 = rxp4.Match(lib); + if (m1.Success) + newDeps.Add(m1.ToString()); + else if (m2.Success) + newDeps.Add(m2.ToString()); + else if (m3.Success) + newDeps.Add(m3.ToString()); + else if (m4.Success) + newDeps.Add(m4.ToString()); + else + newDeps.Add(lib); + } + // Remove Duplicates + var uniques = new Dictionary<string, int>(); + foreach (var dep in newDeps) + uniques[dep] = 1; + var uniqueList = new List<string>(uniques.Keys); + linkerWrapper.AdditionalDependencies = uniqueList; + } + } + + /// <summary> + /// Deletes the file's directory if it is empty (not deleting the file itself so it must + /// have been deleted before) and every empty parent directory until the first, non-empty + /// directory is found. + /// </summary> + /// <param term='file'>Start point of the deletion</param> + public static void DeleteEmptyParentDirs(VCFile file) + { + var dir = file.FullPath.Remove(file.FullPath.LastIndexOf(Path.DirectorySeparatorChar)); + DeleteEmptyParentDirs(dir); + } + + /// <summary> + /// Deletes the directory if it is empty and every empty parent directory until the first, + /// non-empty directory is found. + /// </summary> + /// <param term='file'>Start point of the deletion</param> + public static void DeleteEmptyParentDirs(string directory) + { + var dirInfo = new DirectoryInfo(directory); + while (dirInfo.Exists && dirInfo.GetFileSystemInfos().Length == 0) { + var tmp = dirInfo; + dirInfo = dirInfo.Parent; + tmp.Delete(); + } + } + + public static bool HasQObjectDeclaration(VCFile file) + { + return CxxFileContainsNotCommented(file, + new[] + { + "Q_OBJECT", + "Q_GADGET", + "Q_NAMESPACE" + }, + StringComparison.Ordinal, true); + } + + public static bool CxxFileContainsNotCommented(VCFile file, string str, + StringComparison comparisonType, bool suppressStrings) + { + return CxxFileContainsNotCommented(file, new[] { str }, comparisonType, suppressStrings); + } + + public static bool CxxFileContainsNotCommented(VCFile file, string[] searchStrings, + StringComparison comparisonType, bool suppressStrings) + { + // Small optimization, we first read the whole content as a string and look for the + // search strings. Once we found at least one, ... + bool found = false; + var content = string.Empty; + try { + using (StreamReader sr = new StreamReader(file.FullPath)) + content = sr.ReadToEnd(); + + foreach (var key in searchStrings) { + if (content.IndexOf(key, comparisonType) >= 0) { + found = true; + break; + } + } + } catch { } + + if (!found) + return false; + + // ... we will start parsing the file again to see if the actual string is commented + // or not. The combination of string.IndexOf(...) and string.Split(...) seems to be + // way faster then reading the file line by line. + found = false; + CxxStreamReader cxxSr = null; + try { + cxxSr = new CxxStreamReader(content.Split(new[] { "\n", "\r\n" }, + StringSplitOptions.RemoveEmptyEntries)); + string strLine; + while (!found && (strLine = cxxSr.ReadLine(suppressStrings)) != null) { + foreach (var str in searchStrings) { + if (strLine.IndexOf(str, comparisonType) != -1) { + found = true; + break; + } + } + } + cxxSr.Close(); + } catch (Exception) { + if (cxxSr != null) + cxxSr.Close(); + } + return found; + } + + public static void SetEnvironmentVariableEx(string environmentVariable, string variableValue) + { + try { + Environment.SetEnvironmentVariable(environmentVariable, variableValue); + } catch { + throw new QtVSException(SR.GetString("HelperFunctions_CannotWriteEnvQTDIR")); + } + } + + public static string ChangePathFormat(string path) + { + return path.Replace('\\', '/'); + } + + public static string RemoveFileNameExtension(FileInfo fi) + { + var lastIndex = fi.Name.LastIndexOf(fi.Extension, StringComparison.Ordinal); + return fi.Name.Remove(lastIndex, fi.Extension.Length); + } + + public static bool IsInFilter(VCFile vcfile, FakeFilter filter) + { + var item = (VCProjectItem)vcfile; + + while ((item.Parent != null) && (item.Kind != "VCProject")) { + item = (VCProjectItem)item.Parent; + + if (item.Kind == "VCFilter") { + var f = (VCFilter)item; + if (f.UniqueIdentifier != null + && f.UniqueIdentifier.ToLower() == filter.UniqueIdentifier.ToLower()) + return true; + } + } + return false; + } + + public static void CollapseFilter(UIHierarchyItem item, UIHierarchy hierarchy, string nodeToCollapseFilter) + { + if (string.IsNullOrEmpty(nodeToCollapseFilter)) + return; + + foreach (UIHierarchyItem innerItem in item.UIHierarchyItems) { + if (innerItem.Name == nodeToCollapseFilter) + CollapseFilter(innerItem, hierarchy); + else if (innerItem.UIHierarchyItems.Count > 0) + CollapseFilter(innerItem, hierarchy, nodeToCollapseFilter); + } + } + + public static void CollapseFilter(UIHierarchyItem item, UIHierarchy hierarchy) + { + var subItems = item.UIHierarchyItems; + if (subItems != null) { + foreach (UIHierarchyItem innerItem in subItems) { + if (innerItem.UIHierarchyItems.Count > 0) { + CollapseFilter(innerItem, hierarchy); + + if (innerItem.UIHierarchyItems.Expanded) { + innerItem.UIHierarchyItems.Expanded = false; + if (innerItem.UIHierarchyItems.Expanded) { + innerItem.Select(vsUISelectionType.vsUISelectionTypeSelect); + hierarchy.DoDefaultAction(); + } + } + } + } + } + if (item.UIHierarchyItems.Expanded) { + item.UIHierarchyItems.Expanded = false; + if (item.UIHierarchyItems.Expanded) { + item.Select(vsUISelectionType.vsUISelectionTypeSelect); + hierarchy.DoDefaultAction(); + } + } + } + + // returns true if some exception occurs + public static bool IsGenerated(VCFile vcfile) + { + try { + return IsInFilter(vcfile, Filters.GeneratedFiles()); + } catch (Exception e) { + MessageBox.Show(e.ToString()); + return true; + } + } + + // returns false if some exception occurs + public static bool IsResource(VCFile vcfile) + { + try { + return IsInFilter(vcfile, Filters.ResourceFiles()); + } catch (Exception) { + return false; + } + } + + public static List<string> GetProjectFiles(Project pro, FilesToList filter) + { + var fileList = new List<string>(); + + VCProject vcpro; + try { + vcpro = (VCProject)pro.Object; + } catch (Exception e) { + Messages.DisplayErrorMessage(e); + return null; + } + + var configurationName = pro.ConfigurationManager.ActiveConfiguration.ConfigurationName; + + foreach (VCFile vcfile in (IVCCollection)vcpro.Files) { + // Why project files are also returned? + if (vcfile.ItemName.EndsWith(".vcxproj.filters", StringComparison.Ordinal)) + continue; + var excluded = false; + var fileConfigurations = (IVCCollection)vcfile.FileConfigurations; + foreach (VCFileConfiguration config in fileConfigurations) { + if (config.ExcludedFromBuild && config.MatchName(configurationName, false)) { + excluded = true; + break; + } + } + + if (excluded) + continue; + + // can be in any filter + if (IsTranslationFile(vcfile.Name) && (filter == FilesToList.FL_Translation)) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + + // can also be in any filter + if (IsWinRCFile(vcfile.Name) && (filter == FilesToList.FL_WinResource)) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + + if (IsGenerated(vcfile)) { + if (filter == FilesToList.FL_Generated) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + continue; + } + + if (IsResource(vcfile)) { + if (filter == FilesToList.FL_Resources) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + continue; + } + + switch (filter) { + case FilesToList.FL_UiFiles: // form files + if (IsUicFile(vcfile.Name)) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + break; + case FilesToList.FL_HFiles: + if (IsHeaderFile(vcfile.Name)) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + break; + case FilesToList.FL_CppFiles: + if (IsSourceFile(vcfile.Name)) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + break; + case FilesToList.FL_QmlFiles: + if (IsQmlFile(vcfile.Name)) + fileList.Add(ChangePathFormat(vcfile.RelativePath)); + break; + } + } + + return fileList; + } + + /// <summary> + /// Removes a file reference from the project and moves the file to the "Deleted" folder. + /// </summary> + /// <param name="vcpro"></param> + /// <param name="fileName"></param> + public static void RemoveFileInProject(VCProject vcpro, string fileName) + { + var qtProj = QtProject.Create(vcpro); + var fi = new FileInfo(fileName); + + foreach (VCFile vcfile in (IVCCollection)vcpro.Files) { + if (vcfile.FullPath.ToLower() == fi.FullName.ToLower()) { + vcpro.RemoveFile(vcfile); + qtProj.MoveFileToDeletedFolder(vcfile); + } + } + } + + public static Project GetSelectedProject(DTE dteObject) + { + if (dteObject == null) + return null; + Array prjs = null; + try { + prjs = (Array)dteObject.ActiveSolutionProjects; + } catch { + // When VS2010 is started from the command line, + // we may catch a "Unspecified error" here. + } + if (prjs == null || prjs.Length < 1) + return null; + + // don't handle multiple selection... use the first one + if (prjs.GetValue(0) is Project) + return (Project)prjs.GetValue(0); + return null; + } + + public static Project GetActiveDocumentProject(DTE dteObject) + { + if (dteObject == null) + return null; + var doc = dteObject.ActiveDocument; + if (doc == null) + return null; + + if (doc.ProjectItem == null) + return null; + + return doc.ProjectItem.ContainingProject; + } + + public static Project GetSingleProjectInSolution(DTE dteObject) + { + var projectList = ProjectsInSolution(dteObject); + if (dteObject == null || dteObject.Solution == null || + projectList.Count != 1) + return null; // no way to know which one to select + + return projectList[0]; + } + + /// <summary> + /// Returns the the current selected Qt Project. If not project + /// is selected or if the selected project is not a Qt project + /// this function returns null. + /// </summary> + public static Project GetSelectedQtProject(DTE dteObject) + { + // can happen sometimes shortly after starting VS + if (dteObject == null || dteObject.Solution == null + || ProjectsInSolution(dteObject).Count == 0) + return null; + + Project pro; + + if ((pro = GetSelectedProject(dteObject)) == null) { + if ((pro = GetSingleProjectInSolution(dteObject)) == null) + pro = GetActiveDocumentProject(dteObject); + } + return IsQtProject(pro) ? pro : null; + } + + public static VCFile[] GetSelectedFiles(DTE dteObject) + { + if (GetSelectedQtProject(dteObject) == null) + return null; + + if (dteObject.SelectedItems.Count <= 0) + return null; + + var items = dteObject.SelectedItems; + + var files = new VCFile[items.Count + 1]; + for (var i = 1; i <= items.Count; ++i) { + var item = items.Item(i); + if (item.ProjectItem == null) + continue; + + VCProjectItem vcitem; + try { + vcitem = (VCProjectItem)item.ProjectItem.Object; + } catch (Exception) { + return null; + } + + if (vcitem.Kind == "VCFile") + files[i - 1] = (VCFile)vcitem; + } + files[items.Count] = null; + return files; + } + + public static Image GetSharedImage(string name) + { + Image image = null; + var a = Assembly.GetExecutingAssembly(); + using (var imgStream = a.GetManifestResourceStream(name)) { + if (imgStream != null) + image = Image.FromStream(imgStream); + } + return image; + } + + public static RccOptions ParseRccOptions(string cmdLine, VCFile qrcFile) + { + var pro = VCProjectToProject((VCProject)qrcFile.project); + + var rccOpts = new RccOptions(pro, qrcFile); + + if (cmdLine.Length > 0) { + var cmdSplit = cmdLine.Split(' ', '\t'); + for (var i = 0; i < cmdSplit.Length; ++i) { + var lowercmdSplit = cmdSplit[i].ToLower(); + if (lowercmdSplit.Equals("-threshold")) { + rccOpts.CompressFiles = true; + rccOpts.CompressThreshold = int.Parse(cmdSplit[i + 1]); + } else if (lowercmdSplit.Equals("-compress")) { + rccOpts.CompressFiles = true; + rccOpts.CompressLevel = int.Parse(cmdSplit[i + 1]); + } + } + } + return rccOpts; + } + + public static Project VCProjectToProject(VCProject vcproj) + { + return (Project)vcproj.Object; + } + + public static List<Project> ProjectsInSolution(DTE dteObject) + { + var projects = new List<Project>(); + var solution = dteObject.Solution; + if (solution != null) { + var c = solution.Count; + for (var i = 1; i <= c; ++i) { + try { + var prj = solution.Projects.Item(i); + if (prj == null) + continue; + addSubProjects(prj, ref projects); + } catch { + // Ignore this exception... maybe the next project is ok. + // This happens for example for Intel VTune projects. + } + } + } + return projects; + } + + private static void addSubProjects(Project prj, ref List<Project> projects) + { + // If the actual object of the project is null then the project was probably unloaded. + if (prj.Object == null) + return; + + if (prj.ConfigurationManager != null && + // Is this a Visual C++ project? + prj.Kind == "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") { + projects.Add(prj); + } else { + // In this case, prj is a solution folder + addSubProjects(prj.ProjectItems, ref projects); + } + } + + private static void addSubProjects(ProjectItems subItems, ref List<Project> projects) + { + if (subItems == null) + return; + + foreach (ProjectItem item in subItems) { + Project subprj = null; + try { + subprj = item.SubProject; + } catch { + // The property "SubProject" might not be implemented. + // This is the case for Intel Fortran projects. (QTBUG-11567) + } + if (subprj != null) + addSubProjects(subprj, ref projects); + } + } + + public static int GetMaximumCommandLineLength() + { + var epsilon = 10; // just to be sure :) + var os = Environment.OSVersion; + if (os.Version.Major >= 6 || + (os.Version.Major == 5 && os.Version.Minor >= 1)) + return 8191 - epsilon; // Windows XP and above + return 2047 - epsilon; + } + + /// <summary> + /// Translates the machine type given as command line argument to the linker + /// to the internal enum type VCProjectEngine.machineTypeOption. + /// </summary> + public static machineTypeOption TranslateMachineType(string cmdLineMachine) + { + switch (cmdLineMachine.ToUpper()) { + case "AM33": + return machineTypeOption.machineAM33; + case "X64": + return machineTypeOption.machineAMD64; + case "ARM": + return machineTypeOption.machineARM; + case "EBC": + return machineTypeOption.machineEBC; + case "IA-64": + return machineTypeOption.machineIA64; + case "M32R": + return machineTypeOption.machineM32R; + case "MIPS": + return machineTypeOption.machineMIPS; + case "MIPS16": + return machineTypeOption.machineMIPS16; + case "MIPSFPU": + return machineTypeOption.machineMIPSFPU; + case "MIPSFPU16": + return machineTypeOption.machineMIPSFPU16; + case "MIPS41XX": + return machineTypeOption.machineMIPSR41XX; + case "SH3": + return machineTypeOption.machineSH3; + case "SH3DSP": + return machineTypeOption.machineSH3DSP; + case "SH4": + return machineTypeOption.machineSH4; + case "SH5": + return machineTypeOption.machineSH5; + case "THUMB": + return machineTypeOption.machineTHUMB; + case "X86": + return machineTypeOption.machineX86; + default: + return machineTypeOption.machineNotSet; + } + } + + public static bool ArraysEqual(Array array1, Array array2) + { + if (array1 == array2) + return true; + + if (array1 == null || array2 == null) + return false; + + if (array1.Length != array2.Length) + return false; + + for (var i = 0; i < array1.Length; i++) { + if (!Equals(array1.GetValue(i), array2.GetValue(i))) + return false; + } + return true; + } + + public static string FindFileInPATH(string fileName) + { + var envPATH = Environment.ExpandEnvironmentVariables("%PATH%"); + var directories = envPATH.Split(';'); + foreach (var directory in directories) { + var fullFilePath = directory; + if (!fullFilePath.EndsWith("\\", StringComparison.Ordinal)) + fullFilePath += '\\'; + fullFilePath += fileName; + if (File.Exists(fullFilePath)) + return fullFilePath; + } + return null; + } + + /// <summary> + /// This method copies the specified directory and all its child directories and files to + /// the specified destination. The destination directory is created if it does not exist. + /// </summary> + public static void CopyDirectory(string directory, string targetPath) + { + var sourceDir = new DirectoryInfo(directory); + if (!sourceDir.Exists) + return; + + try { + if (!Directory.Exists(targetPath)) + Directory.CreateDirectory(targetPath); + + var files = sourceDir.GetFiles(); + foreach (var file in files) { + try { + file.CopyTo(Path.Combine(targetPath, file.Name), true); + } catch { } + } + } catch { } + + var subDirs = sourceDir.GetDirectories(); + foreach (var subDir in subDirs) + CopyDirectory(subDir.FullName, Path.Combine(targetPath, subDir.Name)); + } + + /// <summary> + /// Performs an in-place expansion of MS Build properties in the form $(PropertyName) + /// and project item metadata in the form %(MetadataName).<para/> + /// Returns: 'true' if expansion was successful, 'false' otherwise<para/> + /// <paramref name="stringToExpand"/>: The string containing properties and/or metadata to + /// expand. This string is passed by ref and expansion is performed in-place.<para/> + /// <paramref name="project"/>: Current project.<para/> + /// <paramref name="configName"/>: Name of selected configuration (e.g. "Debug").<para/> + /// <paramref name="platformName"/>: Name of selected platform (e.g. "x64").<para/> + /// <paramref name="filePath"/>(optional): Evaluation context.<para/> + /// </summary> + public static bool ExpandString( + ref string stringToExpand, + EnvDTE.Project project, + string configName, + string platformName, + string filePath = null) + { + if (project == null + || string.IsNullOrEmpty(configName) + || string.IsNullOrEmpty(platformName)) + return false; + + var vcProject = project.Object as VCProject; + + if (filePath == null) { + var vcConfig = (from VCConfiguration _config + in (IVCCollection)vcProject.Configurations + where _config.Name == configName + "|" + platformName + select _config).FirstOrDefault(); + return ExpandString(ref stringToExpand, vcConfig); + } else { + var vcFile = (from VCFile _file in (IVCCollection)vcProject.Files + where _file.FullPath == filePath + select _file).FirstOrDefault(); + if (vcFile == null) + return false; + + var vcFileConfig = (from VCFileConfiguration _config + in (IVCCollection)vcFile.FileConfigurations + where _config.Name == configName + "|" + platformName + select _config).FirstOrDefault(); + return ExpandString(ref stringToExpand, vcFileConfig); + } + } + + /// <summary> + /// Performs an in-place expansion of MS Build properties in the form $(PropertyName) + /// and project item metadata in the form %(MetadataName).<para/> + /// Returns: 'true' if expansion was successful, 'false' otherwise<para/> + /// <paramref name="stringToExpand"/>: The string containing properties and/or metadata to + /// expand. This string is passed by ref and expansion is performed in-place.<para/> + /// <paramref name="config"/>: Either a VCConfiguration or VCFileConfiguration object to + /// use as provider of property expansion (through Evaluate()). Cannot be null.<para/> + /// </summary> + public static bool ExpandString( + ref string stringToExpand, + object config) + { + if (config == null) + return false; + + /* try property expansion through VCConfiguration.Evaluate() + * or VCFileConfiguration.Evaluate() */ + string expanded = stringToExpand; + VCProject vcProj = null; + VCFile vcFile = null; + string configName = "", platformName = ""; + var vcConfig = config as VCConfiguration; + if (vcConfig != null) { + vcProj = vcConfig.project as VCProject; + configName = vcConfig.ConfigurationName; + var vcPlatform = vcConfig.Platform as VCPlatform; + if (vcPlatform != null) + platformName = vcPlatform.Name; + try { + expanded = vcConfig.Evaluate(expanded); + } catch { } + } else { + var vcFileConfig = config as VCFileConfiguration; + if (vcFileConfig == null) + return false; + vcFile = vcFileConfig.File as VCFile; + if (vcFile != null) + vcProj = vcFile.project as VCProject; + var vcProjConfig = vcFileConfig.ProjectConfiguration as VCConfiguration; + if (vcProjConfig != null) { + configName = vcProjConfig.ConfigurationName; + var vcPlatform = vcProjConfig.Platform as VCPlatform; + if (vcPlatform != null) + platformName = vcPlatform.Name; + } + try { + expanded = vcFileConfig.Evaluate(expanded); + } catch { } + } + + /* fail-safe */ + foreach (Match propNameMatch in Regex.Matches(expanded, @"\$\(([^\)]+)\)")) { + string propName = propNameMatch.Groups[1].Value; + string propValue = ""; + switch (propName) { + case "Configuration": + case "ConfigurationName": + if (string.IsNullOrEmpty(configName)) + return false; + propValue = configName; + break; + case "Platform": + case "PlatformName": + if (string.IsNullOrEmpty(platformName)) + return false; + propValue = platformName; + break; + default: + return false; + } + expanded = expanded.Replace(string.Format("$({0})", propName), propValue); + } + + /* because item metadata is not expanded in Evaluate() */ + foreach (Match metaNameMatch in Regex.Matches(expanded, @"\%\(([^\)]+)\)")) { + string metaName = metaNameMatch.Groups[1].Value; + string metaValue = ""; + switch (metaName) { + case "FullPath": + if (vcFile == null) + return false; + metaValue = vcFile.FullPath; + break; + case "RootDir": + if (vcFile == null) + return false; + metaValue = Path.GetPathRoot(vcFile.FullPath); + break; + case "Filename": + if (vcFile == null) + return false; + metaValue = Path.GetFileNameWithoutExtension(vcFile.FullPath); + break; + case "Extension": + if (vcFile == null) + return false; + metaValue = Path.GetExtension(vcFile.FullPath); + break; + case "RelativeDir": + if (vcProj == null || vcFile == null) + return false; + metaValue = Path.GetDirectoryName(GetRelativePath( + Path.GetDirectoryName(vcProj.ProjectFile), + vcFile.FullPath)); + if (!metaValue.EndsWith("\\")) + metaValue += "\\"; + if (metaValue.StartsWith(".\\")) + metaValue = metaValue.Substring(2); + break; + case "Directory": + if (vcFile == null) + return false; + metaValue = Path.GetDirectoryName(GetRelativePath( + Path.GetPathRoot(vcFile.FullPath), + vcFile.FullPath)); + if (!metaValue.EndsWith("\\")) + metaValue += "\\"; + if (metaValue.StartsWith(".\\")) + metaValue = metaValue.Substring(2); + break; + case "Identity": + if (vcProj == null || vcFile == null) + return false; + metaValue = GetRelativePath( + Path.GetDirectoryName(vcProj.ProjectFile), + vcFile.FullPath); + if (metaValue.StartsWith(".\\")) + metaValue = metaValue.Substring(2); + break; + case "RecursiveDir": + case "ModifiedTime": + case "CreatedTime": + case "AccessedTime": + return false; + default: + var vcFileConfig = config as VCFileConfiguration; + if (vcFileConfig == null) + return false; + var propStoreTool = vcFileConfig.Tool as IVCRulePropertyStorage; + if (propStoreTool == null) + return false; + try { + metaValue = propStoreTool.GetEvaluatedPropertyValue(metaName); + } catch { + return false; + } + break; + } + expanded = expanded.Replace(string.Format("%({0})", metaName), metaValue); + } + + stringToExpand = expanded; + return true; + } + + private static string GetRegistrySoftwareString(string subKeyName, string valueName) + { + var keyName = new StringBuilder(); + keyName.Append(@"SOFTWARE\"); + if (System.Environment.Is64BitOperatingSystem && IntPtr.Size == 4) + keyName.Append(@"WOW6432Node\"); + keyName.Append(subKeyName); + + try { + using (var key = Registry.LocalMachine.OpenSubKey(keyName.ToString(), false)) { + if (key == null) + return ""; //key not found + + RegistryValueKind valueKind = key.GetValueKind(valueName); + if (valueKind != RegistryValueKind.String + && valueKind != RegistryValueKind.ExpandString) { + return ""; //wrong value kind + } + + Object objValue = key.GetValue(valueName); + if (objValue == null) + return ""; //error getting value + + return objValue.ToString(); + } + } catch { + return ""; + } + } + + public static string GetWindows10SDKVersion() + { +#if VS2019 || VS2022 + // In Visual Studio 2019: WindowsTargetPlatformVersion=10.0 + // will be treated as "use latest installed Windows 10 SDK". + // https://developercommunity.visualstudio.com/comments/407752/view.html + return "10.0"; +#else + string versionWin10SDK = HelperFunctions.GetRegistrySoftwareString( + @"Microsoft\Microsoft SDKs\Windows\v10.0", "ProductVersion"); + if (string.IsNullOrEmpty(versionWin10SDK)) + return versionWin10SDK; + while (versionWin10SDK.Split(new char[] { '.' }).Length < 4) + versionWin10SDK = versionWin10SDK + ".0"; + return versionWin10SDK; +#endif + } + + static string _VCPath; + public static string VCPath + { + set { _VCPath = value; } + get + { + if (!string.IsNullOrEmpty(_VCPath)) + return _VCPath; + else + return GetVCPathFromRegistry(); + } + } + + private static string GetVCPathFromRegistry() + { +#if VS2022 + Debug.Assert(false, "VCPath for VS2022 is not available through the registry"); + string vcPath = string.Empty; +#elif VS2019 + Debug.Assert(false, "VCPath for VS2019 is not available through the registry"); + string vcPath = string.Empty; +#elif VS2017 + string vsPath = GetRegistrySoftwareString(@"Microsoft\VisualStudio\SxS\VS7", "15.0"); + if (string.IsNullOrEmpty(vsPath)) + return ""; + string vcPath = Path.Combine(vsPath, "VC"); +#endif + return vcPath; + } + + public static bool SetVCVars(ProcessStartInfo startInfo) + { + return SetVCVars(null, startInfo); + } + + public static bool SetVCVars(VersionInformation VersionInfo, ProcessStartInfo startInfo) + { + if (VersionInfo == null) { + VersionInfo = QtVersionManager.The().GetVersionInfo( + QtVersionManager.The().GetDefaultVersion()); + } + bool isOS64Bit = System.Environment.Is64BitOperatingSystem; + bool isQt64Bit = VersionInfo.is64Bit(); + + string vcPath = VCPath; + if (vcPath == "") + return false; + + string comspecPath = Environment.GetEnvironmentVariable("COMSPEC"); + string vcVarsCmd = ""; + string vcVarsArg = ""; + if (isOS64Bit && isQt64Bit) + vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvars64.bat"); + else if (!isOS64Bit && !isQt64Bit) + vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvars32.bat"); + else if (isOS64Bit && !isQt64Bit) + vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvarsamd64_x86.bat"); + else if (!isOS64Bit && isQt64Bit) + vcVarsCmd = Path.Combine(vcPath, @"Auxiliary\Build\vcvarsx86_amd64.bat"); + + Messages.Print($"vcvars: {vcVarsCmd}"); + + const string markSTX = ":@:@:@"; + const string markEOL = ":#:#:#"; + string command = + string.Format("/c \"{0}\" {1} && echo {2} && set", vcVarsCmd, vcVarsArg, markSTX); + var vcVarsStartInfo = new ProcessStartInfo(comspecPath, command); + vcVarsStartInfo.CreateNoWindow = true; + vcVarsStartInfo.UseShellExecute = false; + vcVarsStartInfo.RedirectStandardError = true; + vcVarsStartInfo.RedirectStandardOutput = true; + + var process = Process.Start(vcVarsStartInfo); + StringBuilder stdOut = new StringBuilder(); + + process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => + stdOut.AppendFormat("{0}\n{1}\n", e.Data, markEOL); + process.BeginOutputReadLine(); + + process.WaitForExit(); + bool ok = (process.ExitCode == 0); + process.Close(); + if (!ok) + return false; + + SortedDictionary<string, List<string>> vcVars = + new SortedDictionary<string, List<string>>(); + string[] split = + stdOut.ToString().Split(new string[] { "\n", "=", ";" }, StringSplitOptions.None); + int i = 0; + for (; i < split.Length && split[i].Trim() != markSTX; i++) { + //Skip to start of data + } + i++; //Advance to next item + for (; i < split.Length && split[i].Trim() != markEOL; i++) { + //Skip to end of line + } + i++; //Advance to next item + for (; i < split.Length; i++) { + //Process first item (variable name) + string key = split[i].ToUpper().Trim(); + i++; //Advance to next item + List<string> vcVarValue = vcVars[key] = new List<string>(); + for (; i < split.Length && split[i].Trim() != markEOL; i++) { + //Process items up to end of line (variable value(s)) + vcVarValue.Add(split[i].Trim()); + } + } + + foreach (var vcVar in vcVars) { + if (vcVar.Value.Count == 1) { + startInfo.EnvironmentVariables[vcVar.Key] = vcVar.Value[0]; + } else { + if (!startInfo.EnvironmentVariables.ContainsKey(vcVar.Key)) { + foreach (var vcVarValue in vcVar.Value) { + if (!string.IsNullOrWhiteSpace(vcVarValue)) { + startInfo.EnvironmentVariables[vcVar.Key] += vcVarValue + ";"; + } + } + } else { + string[] startInfoVariableValues = startInfo.EnvironmentVariables[vcVar.Key] + .Split(new string[] { ";" }, StringSplitOptions.None); + foreach (var vcVarValue in vcVar.Value) { + if (!string.IsNullOrWhiteSpace(vcVarValue) + && !startInfoVariableValues.Any(s => s.Trim().Equals( + vcVarValue, + StringComparison.OrdinalIgnoreCase))) { + if (!startInfo.EnvironmentVariables[vcVar.Key].EndsWith(";")) + startInfo.EnvironmentVariables[vcVar.Key] += ";"; + startInfo.EnvironmentVariables[vcVar.Key] += vcVarValue + ";"; + } + } + } + } + } + string envPath = startInfo.EnvironmentVariables["PATH"]; + string clPath = envPath.Split(';') + .Select(path => Path.Combine(path, "cl.exe")) + .Where(pathToCl => File.Exists(pathToCl)) + .FirstOrDefault(); + Messages.Print($"cl: {clPath ?? "NOT FOUND"}"); + + return true; + } + + /// <summary> + /// Rooted canonical path is the absolute path for the specified path string + /// (cf. Path.GetFullPath()) without a trailing path separator. + /// </summary> + static string RootedCanonicalPath(string path) + { + try { + return Path + .GetFullPath(path) + .TrimEnd(new char[] { + Path.DirectorySeparatorChar, + Path.AltDirectorySeparatorChar + }); + } catch { + return ""; + } + } + + /// <summary> + /// If the given path is relative and a sub-path of the current directory, returns + /// a "relative canonical path", containing only the steps beyond the current directory. + /// Otherwise, returns the absolute ("rooted") canonical path. + /// </summary> + public static string CanonicalPath(string path) + { + string canonicalPath = RootedCanonicalPath(path); + if (!Path.IsPathRooted(path)) { + string currentCanonical = RootedCanonicalPath("."); + if (canonicalPath.StartsWith(currentCanonical, + StringComparison.InvariantCultureIgnoreCase)) { + return canonicalPath + .Substring(currentCanonical.Length) + .TrimStart(new char[] { + Path.DirectorySeparatorChar, + Path.AltDirectorySeparatorChar + }); + } else { + return canonicalPath; + } + } else { + return canonicalPath; + } + } + + public static bool PathEquals(string path1, string path2) + { + return (CanonicalPath(path1).Equals(CanonicalPath(path2), + StringComparison.InvariantCultureIgnoreCase)); + } + + public static bool PathIsRelativeTo(string path, string subPath) + { + return CanonicalPath(path).EndsWith(CanonicalPath(subPath), + StringComparison.InvariantCultureIgnoreCase); + } + + public static string Unquote(string text) + { + text = text.Trim(); + if (string.IsNullOrEmpty(text) + || text.Length < 3 + || !text.StartsWith("\"") + || !text.EndsWith("\"")) { + return text; + } + return text.Substring(1, text.Length - 2); + } + + public static string NewProjectGuid() + { + return string.Format("{{{0}}}", Guid.NewGuid().ToString().ToUpper()); + } + + public static string SafePath(string path) + { + if (string.IsNullOrEmpty(path)) + return null; + path = path.Replace("\"", ""); + if (!path.Contains(' ')) + return path; + if (path.EndsWith("\\")) + path += "\\"; + return string.Format("\"{0}\"", path); + } + } +} diff --git a/QtVsTools.Core/ImageButton.cs b/QtVsTools.Core/ImageButton.cs new file mode 100644 index 0000000..ab567ce --- /dev/null +++ b/QtVsTools.Core/ImageButton.cs @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Drawing; +using System.Windows.Forms; + +namespace QtVsTools.Core +{ + public class ImageButton : Button + { + private readonly Image img; + private readonly Image dimg; + + // support for disabled image + public ImageButton(Image image, Image dimage) + { + img = image; + dimg = dimage; + } + + public sealed override Color BackColor + { + get { return SystemColors.Control; } + } + + protected override void OnPaint(PaintEventArgs pevent) + { + base.OnPaint(pevent); + + var xoffset = (Size.Width - img.Width) / 2; + var yoffset = (Size.Height - img.Height) / 2; + var imgWidth = img.Width; + var imgHeight = img.Height; + + // make it smaller if necessary + if (xoffset < 0) + imgWidth = Size.Width; + if (yoffset < 0) + imgHeight = Size.Height; + + if ((dimg != null) && (!Enabled)) { + pevent.Graphics.DrawImage(dimg, xoffset, yoffset, + imgWidth, imgHeight); + } else if (img != null) { + pevent.Graphics.DrawImage(img, xoffset, yoffset, + imgWidth, imgHeight); + } + } + } +} diff --git a/QtVsTools.Core/LinkerToolWrapper.cs b/QtVsTools.Core/LinkerToolWrapper.cs new file mode 100644 index 0000000..cc57a34 --- /dev/null +++ b/QtVsTools.Core/LinkerToolWrapper.cs @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.VCProjectEngine; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace QtVsTools.Core +{ + /// <summary> + /// Adds convenience functions to the VCLinkerTool. + /// </summary> + public class LinkerToolWrapper + { + private VCLinkerTool linker; + + public LinkerToolWrapper(VCLinkerTool linkerTool) + { + linker = linkerTool; + } + + public List<string> AdditionalLibraryDirectories + { + get + { + if (linker.AdditionalLibraryDirectories == null) + return null; + var dirArray = linker.AdditionalLibraryDirectories.Split(new[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries); + var lst = new List<string>(dirArray); + for (var i = 0; i < lst.Count; ++i) { + var item = lst[i]; + if (item.StartsWith("\"", StringComparison.Ordinal) && item.EndsWith("\"", StringComparison.Ordinal)) { + item = item.Remove(0, 1); + item = item.Remove(item.Length - 1, 1); + lst[i] = item; + } + } + return lst; + } + + internal set + { + if (value == null) { + linker.AdditionalLibraryDirectories = null; + return; + } + + var newAdditionalLibraryDirectories = string.Empty; + var firstLoop = true; + foreach (var item in value) { + if (firstLoop) + firstLoop = false; + else + newAdditionalLibraryDirectories += ";"; + + if (!Path.IsPathRooted(item) || item.IndexOfAny(new[] { ' ', '\t' }) > 0) + newAdditionalLibraryDirectories += "\"" + item + "\""; + else + newAdditionalLibraryDirectories += item; + } + if (newAdditionalLibraryDirectories != linker.AdditionalLibraryDirectories) + linker.AdditionalLibraryDirectories = newAdditionalLibraryDirectories; + } + } + + public List<string> AdditionalDependencies + { + get + { + if (linker.AdditionalDependencies == null) + return null; + return splitByWhitespace(linker.AdditionalDependencies); + } + + internal set + { + if (value == null) { + linker.AdditionalDependencies = null; + return; + } + + var newAdditionalDependencies = string.Empty; + var separators = new[] { ' ', '\t' }; + var firstLoop = true; + foreach (var item in value) { + if (firstLoop) + firstLoop = false; + else + newAdditionalDependencies += " "; + + var idx = item.IndexOfAny(separators); + if (idx >= 0) + newAdditionalDependencies += "\"" + item + "\""; + else + newAdditionalDependencies += item; + } + if (newAdditionalDependencies != linker.AdditionalDependencies) + linker.AdditionalDependencies = newAdditionalDependencies; + } + } + + /// <summary> + /// Splits a given string by whitespace characters and takes care of double quotes. + /// </summary> + /// <param name="str">string to be split</param> + /// <returns></returns> + private static List<string> splitByWhitespace(string str) + { + var separators = new[] { ' ', '\t' }; + var i = str.IndexOf('"'); + if (i == -1) + return new List<string>(str.Split(separators, StringSplitOptions.RemoveEmptyEntries)); + + var ret = new List<string>(); + var startIndex = 0; + var r = new Regex(@"""[^""]*"""); + var mc = r.Matches(str); + foreach (Match match in mc) { + var item = match.Value; + item = item.Remove(0, 1); + item = item.Remove(item.Length - 1, 1); + + // Add all items before this match, using standard splitting. + var strBefore = str.Substring(startIndex, match.Index - startIndex); + var lstBefore = strBefore.Split(separators, StringSplitOptions.RemoveEmptyEntries); + ret.AddRange(lstBefore); + startIndex = match.Index + match.Length; + + if (item.Length == 0) + continue; + + ret.Add(item); + } + + if (startIndex < str.Length - 1) { + // Add all items after the quoted match, using standard splitting. + var strBefore = str.Substring(startIndex); + var lstBefore = strBefore.Split(separators, StringSplitOptions.RemoveEmptyEntries); + ret.AddRange(lstBefore); + } + + return ret; + } + + } +} diff --git a/QtVsTools.Core/MainWinWrapper.cs b/QtVsTools.Core/MainWinWrapper.cs new file mode 100644 index 0000000..f42ca31 --- /dev/null +++ b/QtVsTools.Core/MainWinWrapper.cs @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Windows.Forms; + +namespace QtVsTools.Core +{ + public class MainWinWrapper : IWin32Window + { + private readonly EnvDTE.DTE dteObject; + + public MainWinWrapper(EnvDTE.DTE dte) + { + dteObject = dte; + } + + public IntPtr Handle + { + get + { + if (dteObject != null) +#if VS2022 + return dteObject.MainWindow.HWnd; +#else + return new IntPtr(dteObject.MainWindow.HWnd); +#endif + return new IntPtr(0); + } + } + } +} diff --git a/QtVsTools.Core/Messages.cs b/QtVsTools.Core/Messages.cs new file mode 100644 index 0000000..c5eea32 --- /dev/null +++ b/QtVsTools.Core/Messages.cs @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using Thread = System.Threading.Thread; +using System.Windows.Forms; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Threading; +using QtVsTools.VisualStudio; + +namespace QtVsTools.Core +{ + public static class Messages + { + private static OutputWindow Window { get; set; } + private static OutputWindowPane Pane { get; set; } + + private static OutputWindowPane _BuildPane; + private static OutputWindowPane BuildPane + { + get + { + return _BuildPane ?? (_BuildPane = Window.OutputWindowPanes.Cast<OutputWindowPane>() + .Where(pane => pane.Guid == "{1BD8A850-02D1-11D1-BEE7-00A0C913D1F8}") + .FirstOrDefault()); + } + } + + /// <summary> + /// Show a message on the output pane. + /// </summary> + public static void Print(string text, bool clear = false, bool activate = false) + { + msgQueue.Enqueue(new Msg() + { + Clear = clear, + Text = text, + Activate = activate + }); + FlushMessages(); + } + + static void OutputWindowPane_Print(string text) + { + OutputWindowPane_Init(); + Pane.OutputString(text + "\r\n"); + // show buildPane if a build is in progress + if (Dte.Solution.SolutionBuild.BuildState == vsBuildState.vsBuildStateInProgress) + BuildPane?.Activate(); + } + + /// <summary> + /// Activates the message pane of the Qt VS Tools extension. + /// </summary> + public static void ActivateMessagePane() + { + msgQueue.Enqueue(new Msg() + { + Activate = true + }); + FlushMessages(); + } + + static void OutputWindowPane_Activate() + { + OutputWindowPane_Init(); + Pane?.Activate(); + } + + private static string ExceptionToString(System.Exception e) + { + return e.Message + "\r\n" + "(" + e.StackTrace.Trim() + ")"; + } + + private static readonly string ErrorString = SR.GetString("Messages_ErrorOccured"); + private static readonly string WarningString = SR.GetString("Messages_Warning"); + private static readonly string SolutionString = SR.GetString("Messages_SolveProblem"); + + static public void DisplayCriticalErrorMessage(System.Exception e) + { + MessageBox.Show(ErrorString + + ExceptionToString(e), + SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + static public void DisplayCriticalErrorMessage(string msg) + { + MessageBox.Show(ErrorString + + msg, + SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + static public void DisplayErrorMessage(System.Exception e) + { + MessageBox.Show(ErrorString + + ExceptionToString(e), + SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + static public void DisplayErrorMessage(string msg) + { + MessageBox.Show(ErrorString + + msg, + SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + static public void DisplayWarningMessage(System.Exception e, string solution) + { + MessageBox.Show(WarningString + + ExceptionToString(e) + + SolutionString + + solution, + SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + static public void DisplayWarningMessage(string msg) + { + MessageBox.Show(WarningString + + msg, + SR.GetString("Resources_QtVsTools"), MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + public static void ClearPane() + { + msgQueue.Enqueue(new Msg() + { + Clear = true + }); + FlushMessages(); + } + + static void OutputWindowPane_Clear() + { + OutputWindowPane_Init(); + Pane?.Clear(); + } + + class Msg + { + public bool Clear { get; set; } = false; + public string Text { get; set; } = null; + public bool Activate { get; set; } = false; + } + + static bool shuttingDown = false; + static ConcurrentQueue<Msg> msgQueue = new ConcurrentQueue<Msg>(); + static DTE Dte { get; set; } = null; + + private static void OnBeginShutdown() + { + shuttingDown = true; + } + + private static void OutputWindowPane_Init() + { + if (Dte == null) + Dte = VsServiceProvider.GetService<DTE>(); + var t = Stopwatch.StartNew(); + while (Pane == null && t.ElapsedMilliseconds < 5000) { + try { + Window = Dte.Windows.Item(Constants.vsWindowKindOutput).Object as OutputWindow; + Pane = Window?.OutputWindowPanes.Add(SR.GetString("Resources_QtVsTools")); + } catch { + } + if (Pane == null) + Thread.Yield(); + } + Dte.Events.DTEEvents.OnBeginShutdown += OnBeginShutdown; + } + + public static JoinableTaskFactory JoinableTaskFactory { get; set; } + static readonly object staticCriticalSection = new object(); + static Task FlushTask { get; set; } + static EventWaitHandle MessageReady { get; set; } + + static void FlushMessages() + { + lock (staticCriticalSection) { + if (FlushTask == null) { + MessageReady = new EventWaitHandle(false, EventResetMode.AutoReset); + FlushTask = Task.Run(async () => + { + while (!shuttingDown) { + if (!await MessageReady.ToTask(3000)) + continue; + while (!msgQueue.IsEmpty) { + Msg msg; + if (!msgQueue.TryDequeue(out msg)) { + await Task.Yield(); + continue; + } + //////////////////////////////////////////////////////////////////// + // Switch to main (UI) thread + await JoinableTaskFactory.SwitchToMainThreadAsync(); + if (msg.Clear) + OutputWindowPane_Clear(); + if (msg.Text != null) + OutputWindowPane_Print(msg.Text); + if (msg.Activate) + OutputWindowPane_Activate(); + //////////////////////////////////////////////////////////////////// + // Switch to background thread + await TaskScheduler.Default; + } + } + }); + } + } + MessageReady.Set(); + } + } +} diff --git a/QtVsTools.Core/MocCmdChecker.cs b/QtVsTools.Core/MocCmdChecker.cs new file mode 100644 index 0000000..dc0df1b --- /dev/null +++ b/QtVsTools.Core/MocCmdChecker.cs @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace QtVsTools.Core +{ + class MocCmdChecker + { + private Regex backslashRegEx = new Regex(@"\\+\.?\\+"); + private Regex endRegEx = new Regex(@"\\\.?$"); + + private string NormalizePath(string path) + { + var s = path.ToLower().Trim(); + s = backslashRegEx.Replace(s, "\\"); + s = endRegEx.Replace(s, string.Empty); + return s; + } + + public string NewCmdLine(string cmdLine, string includes, string defines, + string mocOptions, string mocFile, string newPchParameters, + string outputFile) + { + var inputMocFile = ProjectMacros.Path; + if (HelperFunctions.IsMocFile(outputFile)) + inputMocFile = mocFile; + var cmds = SplitIntoCommands(cmdLine); + var mocPos = MocCommandPosition(cmds); + if (mocPos < 0) + return null; + + var mocCmd = cmds[mocPos]; + var defs = ExtractDefines(mocCmd); + var incs = ExtractIncludes(mocCmd); + var pchParameters = ExtractPCHOptions(mocCmd); + var newIncludes = ExtractIncludes(includes); + var newDefines = ExtractDefines(defines); + + var equal = true; + if (newDefines.Count == defs.Count) { + foreach (var s in newDefines) { + if (defs.Contains(s)) { + defs.Remove(s); + } else { + equal = false; + break; + } + } + } else { + equal = false; + } + + equal = equal && newIncludes.Count == incs.Count; + if (equal) { + foreach (var s in newIncludes) { + if (incs.Contains(s)) { + incs.Remove(s); + } else { + equal = false; + break; + } + } + } + + equal = equal && pchParameters == newPchParameters; + if (equal) + return null; + + var newCmdLine = string.Empty; + for (var i = 0; i < cmds.Length; ++i) { + if (i == mocPos) { + newCmdLine = newCmdLine + "\"" + Resources.moc4Command + "\" " + + mocOptions + + " \"" + inputMocFile + "\" -o \"" + outputFile + "\"" + + " " + defines + " " + includes; + if (!string.IsNullOrEmpty(newPchParameters) && !newCmdLine.Contains(newPchParameters)) + newCmdLine += " " + newPchParameters; + } else { + newCmdLine = newCmdLine + cmds[i]; + } + if (i < cmds.Length - 1 && !newCmdLine.EndsWith("\r\n", StringComparison.Ordinal)) + newCmdLine = newCmdLine + "\r\n"; + } + return newCmdLine; + } + + private static string[] SplitIntoCommands(string cmdLine) + { + var cmds = cmdLine.Split(new[] { "&&", "\r\n" }, StringSplitOptions.RemoveEmptyEntries); + var res = new string[cmds.Length]; + for (var i = 0; i < cmds.Length; ++i) + res[i] = cmds[i].Trim(); + return res; + } + + private static int MocCommandPosition(string[] cmds) + { + var res = -1; + var reg = new Regex(@"(\S*moc.exe|""\S+:\\\.*moc.exe"")"); + for (var i = 0; i < cmds.Length; ++i) { + var m = reg.Match(cmds[i]); + if (m.Success) + return i; + } + return res; + } + + private static List<string> ExtractDefines(string cmdLine) + { + var reg = new Regex(@"-D(\S+)"); + var col = reg.Matches(cmdLine); + var lst = new List<string>(col.Count); + for (var i = 0; i < col.Count; ++i) + lst.Add(col[i].Groups[1].ToString()); + return lst; + } + + private List<string> ExtractIncludes(string cmdLine) + { + var reg = new Regex(@"-I([^\s""]+)|-I""([^""]+)"""); + var col = reg.Matches(cmdLine); + var lst = new List<string>(col.Count); + for (var i = 0; i < col.Count; ++i) { + var s = col[i].Groups[1].ToString(); + if (s.Length != 0) + lst.Add(NormalizePath(s)); + else + lst.Add(NormalizePath(col[i].Groups[2].ToString())); + } + return lst; + } + + private static string ExtractPCHOptions(string cmdLine) + { + var reg = new Regex(@"""-f(\S+)"" ""-f(\S+)"); + var col = reg.Matches(cmdLine); + if (col.Count != 1) + return null; + return col[0].ToString(); + } + } +} diff --git a/QtVsTools.Core/MsBuildProject.cs b/QtVsTools.Core/MsBuildProject.cs new file mode 100644 index 0000000..3b11f34 --- /dev/null +++ b/QtVsTools.Core/MsBuildProject.cs @@ -0,0 +1,1800 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using QtVsTools.Core.QtMsBuild; +using System.Text.RegularExpressions; +using Microsoft.Build.Construction; +using Microsoft.Build.Execution; +using Microsoft.Build.Evaluation; +using QtVsTools.VisualStudio; +using QtVsTools.SyntaxAnalysis; +using EnvDTE; + +namespace QtVsTools.Core +{ + using static HelperFunctions; + using static RegExpr; + + public class MsBuildProject + { + class MsBuildXmlFile + { + public string filePath = ""; + public XDocument xml = null; + public bool isDirty = false; + public XDocument xmlCommitted = null; + public bool isCommittedDirty = false; + } + + enum Files + { + Project = 0, + Filters, + User, + Count + } + MsBuildXmlFile[] files = new MsBuildXmlFile[(int)Files.Count]; + + MsBuildProject() + { + for (int i = 0; i < files.Length; i++) + files[i] = new MsBuildXmlFile(); + } + + MsBuildXmlFile this[Files file] + { + get + { + if ((int)file >= (int)Files.Count) + return files[0]; + return files[(int)file]; + } + } + + public string ProjectXml + { + get + { + var xml = this[Files.Project].xml; + if (xml == null) + return ""; + return xml.ToString(SaveOptions.None); + } + } + + private static XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003"; + + public static MsBuildProject Load(string pathToProject) + { + if (!File.Exists(pathToProject)) + return null; + + MsBuildProject project = new MsBuildProject(); + + project[Files.Project].filePath = pathToProject; + if (!LoadXml(project[Files.Project])) + return null; + + project[Files.Filters].filePath = pathToProject + ".filters"; + if (File.Exists(project[Files.Filters].filePath) && !LoadXml(project[Files.Filters])) + return null; + + project[Files.User].filePath = pathToProject + ".user"; + if (File.Exists(project[Files.User].filePath) && !LoadXml(project[Files.User])) + return null; + + return project; + } + + static bool LoadXml(MsBuildXmlFile xmlFile) + { + try { + var xmlText = File.ReadAllText(xmlFile.filePath, Encoding.UTF8); + using (var reader = XmlReader.Create(new StringReader(xmlText))) { + xmlFile.xml = XDocument.Load(reader, LoadOptions.SetLineInfo); + } + } catch (Exception) { + return false; + } + xmlFile.xmlCommitted = new XDocument(xmlFile.xml); + return true; + } + + public bool Save() + { + foreach (var file in files) { + if (file.isDirty) { + file.xml?.Save(file.filePath, SaveOptions.None); + file.isCommittedDirty = file.isDirty = false; + } + } + return true; + } + + void Commit() + { + foreach (var file in files.Where(x => x.xml != null)) { + if (file.isDirty) { + //file was modified: sync committed copy + file.xmlCommitted = new XDocument(file.xml); + file.isCommittedDirty = true; + } else { + //fail-safe: ensure non-dirty files are in sync with committed copy + file.xml = new XDocument(file.xmlCommitted); + file.isDirty = file.isCommittedDirty; + } + } + } + + void Rollback() + { + foreach (var file in files.Where(x => x.xml != null)) { + file.xml = new XDocument(file.xmlCommitted); + file.isDirty = file.isCommittedDirty; + } + } + + public string GetProperty(string property_name) + { + var xProperty = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Elements() + .Where(x => x.Name.LocalName == property_name) + .FirstOrDefault(); + if (xProperty == null) + return string.Empty; + return xProperty.Value; + } + + public string GetProperty(string item_type, string property_name) + { + var xProperty = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemDefinitionGroup") + .Elements(ns + item_type) + .Elements() + .Where(x => x.Name.LocalName == property_name) + .FirstOrDefault(); + if (xProperty == null) + return string.Empty; + return xProperty.Value; + } + + public IEnumerable<string> GetItems(string item_type) + { + return this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + item_type) + .Select((XElement x) => (string)x.Attribute("Include")); + } + + public bool EnableMultiProcessorCompilation() + { + var xClCompileDefs = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemDefinitionGroup") + .Elements(ns + "ClCompile"); + foreach (var xClCompileDef in xClCompileDefs) + xClCompileDef.Add(new XElement(ns + "MultiProcessorCompilation", "true")); + + this[Files.Project].isDirty = true; + Commit(); + return true; + } + + /// <summary> + /// Parser for project configuration conditional expressions of the type: + /// + /// '$(Configuration)|$(Platform)'=='_TOKEN_|_TOKEN_' + /// + /// </summary> + Parser _ConfigCondition; + Parser ConfigCondition + { + get + { + if (_ConfigCondition == null) { + var config = new Token("Configuration", CharWord.Repeat()); + var platform = new Token("Platform", CharWord.Repeat()); + var expr = "'$(Configuration)|$(Platform)'=='" & config & "|" & platform & "'"; + try { + _ConfigCondition = expr.Render(); + } catch { } + } + return _ConfigCondition; + } + } + + /// <summary> + /// Parser for project format version string: + /// + /// QtVS_vNNN + /// + /// </summary> + Parser _ProjectFormatVersion; + Parser ProjectFormatVersion + { + get + { + if (_ProjectFormatVersion == null) { + var expr = "QtVS_v" & new Token("VERSION", Char['0', '9'].Repeat(3)) + { + new Rule<int> { Capture(value => int.Parse(value)) } + }; + try { + _ProjectFormatVersion = expr.Render(); + } catch { } + } + return _ProjectFormatVersion; + } + } + + int? ParseProjectFormatVersion(string text) + { + if (ProjectFormatVersion == null) + return null; + try { + return ProjectFormatVersion.Parse(text) + .GetValues<int>("VERSION") + .First(); + } catch { + return null; + } + } + + const StringComparison IGNORE_CASE = StringComparison.InvariantCultureIgnoreCase; + readonly StringComparer IGNORE_CASE_ = StringComparer.InvariantCultureIgnoreCase; + + /// <summary> + /// Converts project format version to the latest version: + /// * Set latest project version; + /// * Add QtSettings property group; + /// * Set QtInstall property; + /// * Remove hard-coded macros, include paths and libs related to Qt modules. + /// * Set QtModules property; + /// </summary> + /// <returns>true if successful</returns> + public bool UpdateProjectFormatVersion() + { + if (ConfigCondition == null) + return false; + + // Get default Qt dir + string defaultQtDir = null; + var defaultVersionName = QtVersionManager.The().GetDefaultVersion(); + var defaultVersion = QtVersionManager.The().GetVersionInfo(defaultVersionName); + if (defaultVersion != null) + defaultQtDir = defaultVersion.qtDir; + + // Get project configurations + var configs = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "ProjectConfiguration"); + + // Get project global properties + var globals = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(x => (string)x.Attribute("Label") == "Globals") + .FirstOrDefault(); + if (globals == null) + return false; + + // Get project configuration properties + var configProps = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(pg => + (string)pg.Attribute("Label") == "Configuration" + && pg.Attribute("Condition") != null) + .ToDictionary(pg => (string)pg.Attribute("Condition")); + + // Set Qt project format version + var projKeyword = globals + .Elements(ns + "Keyword") + .Where(x => x.Value.StartsWith(Resources.qtProjectKeyword) + || x.Value.StartsWith(Resources.qtProjectV2Keyword)) + .FirstOrDefault(); + if (projKeyword == null) + return false; + var oldVersion = ParseProjectFormatVersion(projKeyword.Value); + if (oldVersion.HasValue && oldVersion.Value == Resources.qtProjectFormatVersion) + return true; // nothing to do! + + projKeyword.SetValue(string.Format("QtVS_v{0}", Resources.qtProjectFormatVersion)); + + // Find import of qt.props + var qtPropsImport = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ImportGroup") + .Elements(ns + "Import") + .Where(x => (string)x.Attribute("Project") == @"$(QtMsBuild)\qt.props") + .FirstOrDefault(); + if (qtPropsImport == null) + return false; + + var uncategorizedPropertyGroups = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(pg => pg.Attribute("Label") == null) + .ToList(); + + var propertyGroups = new Dictionary<string, XElement>(); + + // Upgrading from <= v3.2? + if (!oldVersion.HasValue + || oldVersion.Value < Resources.qtMinFormatVersion_PropertyEval) { + + // Find import of default Qt properties + var qtDefaultProps = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ImportGroup") + .Elements(ns + "Import") + .Where(pg => Path.GetFileName((string)pg.Attribute("Project")) + .Equals("qt_defaults.props", StringComparison.InvariantCultureIgnoreCase)) + .Select(pg => pg.Parent) + .FirstOrDefault(); + + // Create uncategorized property groups + foreach (var config in configs) { + string condition = string.Format("'$(Configuration)|$(Platform)'=='{0}'", + (string)config.Attribute("Include")); + var group = new XElement(ns + "PropertyGroup", + new XAttribute("Condition", condition)); + propertyGroups[condition] = group; + // Insert uncategorized groups after Qt defaults, if found + if (qtDefaultProps != null) + qtDefaultProps.AddAfterSelf(group); + } + + // Move uncategorized properties to newly created groups + foreach (var pg in uncategorizedPropertyGroups) { + foreach (var p in pg.Elements().ToList()) { + var condition = p.Attribute("Condition") ?? pg.Attribute("Condition"); + XElement configPropertyGroup = null; + if (condition != null) + propertyGroups.TryGetValue((string)condition, out configPropertyGroup); + if (configPropertyGroup != null) { + p.Remove(); + p.SetAttributeValue("Condition", null); + configPropertyGroup.Add(p); + } + } + if (!pg.Elements().Any()) + pg.Remove(); + } + } + + // Upgrading from <= v3.1? + if (!oldVersion.HasValue + || oldVersion.Value < Resources.qtMinFormatVersion_GlobalQtMsBuildProperty) { + + // Move Qt/MSBuild path to global property + var qtMsBuildProperty = globals + .ElementsAfterSelf(ns + "PropertyGroup") + .Elements(ns + "QtMsBuild") + .FirstOrDefault(); + if (qtMsBuildProperty != null) { + var qtMsBuildPropertyGroup = qtMsBuildProperty.Parent; + qtMsBuildProperty.Remove(); + qtMsBuildProperty.SetAttributeValue("Condition", + (string)qtMsBuildPropertyGroup.Attribute("Condition")); + globals.Add(qtMsBuildProperty); + qtMsBuildPropertyGroup.Remove(); + } + } + if (oldVersion.HasValue + && oldVersion.Value > Resources.qtMinFormatVersion_Settings) { + this[Files.Project].isDirty = true; + Commit(); + return true; + } + + // Upgrading from v3.0? + Dictionary<string, XElement> oldQtInstall = null; + Dictionary<string, XElement> oldQtSettings = null; + if (oldVersion.HasValue && oldVersion.Value == Resources.qtMinFormatVersion_Settings) { + oldQtInstall = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Elements(ns + "QtInstall") + .ToDictionary(x => (string)x.Parent.Attribute("Condition")); + oldQtInstall.Values.ToList() + .ForEach(x => x.Remove()); + + oldQtSettings = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(x => (string)x.Attribute("Label") == "QtSettings") + .ToDictionary(x => (string)x.Attribute("Condition")); + oldQtSettings.Values.ToList() + .ForEach(x => x.Remove()); + } + + // Find location for import of qt.props and for the QtSettings property group: + // (cf. ".vcxproj file elements" https://docs.microsoft.com/en-us/cpp/build/reference/vcxproj-file-structure?view=vs-2019#vcxproj-file-elements) + XElement insertionPoint = null; + + // * After the last UserMacros property group + insertionPoint = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(x => (string)x.Attribute("Label") == "UserMacros") + .LastOrDefault(); + + // * After the last PropertySheets import group + insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ImportGroup") + .Where(x => (string)x.Attribute("Label") == "PropertySheets") + .LastOrDefault(); + + // * Before the first ItemDefinitionGroup + insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemDefinitionGroup") + .Select(x => x.ElementsBeforeSelf().Last()) + .FirstOrDefault(); + + // * Before the first ItemGroup + insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Select(x => x.ElementsBeforeSelf().Last()) + .FirstOrDefault(); + + // * Before the import of Microsoft.Cpp.targets + insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "Import") + .Where(x => + (string)x.Attribute("Project") == @"$(VCTargetsPath)\Microsoft.Cpp.targets") + .Select(x => x.ElementsBeforeSelf().Last()) + .FirstOrDefault(); + + // * At the end of the file + insertionPoint = (insertionPoint != null) ? insertionPoint : this[Files.Project].xml + .Elements(ns + "Project") + .Elements() + .LastOrDefault(); + + if (insertionPoint == null) + return false; + + // Move import of qt.props to insertion point + if (qtPropsImport.Parent.Elements().SingleOrDefault() == qtPropsImport) + qtPropsImport.Parent.Remove(); // Remove import group + else + qtPropsImport.Remove(); // Remove import (group contains other imports) + insertionPoint.AddAfterSelf( + new XElement(ns + "ImportGroup", + new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt.props')"), + new XElement(ns + "Import", + new XAttribute("Project", @"$(QtMsBuild)\qt.props")))); + + // Create QtSettings property group above import of qt.props + var qtSettings = new List<XElement>(); + foreach (var config in configs) { + var configQtSettings = new XElement(ns + "PropertyGroup", + new XAttribute("Label", "QtSettings"), + new XAttribute("Condition", + string.Format("'$(Configuration)|$(Platform)'=='{0}'", + (string)config.Attribute("Include")))); + insertionPoint.AddAfterSelf(configQtSettings); + qtSettings.Add(configQtSettings); + } + + // Add uncategorized property groups + foreach (XElement propertyGroup in propertyGroups.Values) + insertionPoint.AddAfterSelf(propertyGroup); + + // Add import of default property values + insertionPoint.AddAfterSelf( + new XElement(ns + "ImportGroup", + new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt_defaults.props')"), + new XElement(ns + "Import", + new XAttribute("Project", @"$(QtMsBuild)\qt_defaults.props")))); + + //// Upgrading from v3.0: move Qt settings to newly created import groups + if (oldVersion.HasValue && oldVersion.Value == Resources.qtMinFormatVersion_Settings) { + foreach (var configQtSettings in qtSettings) { + var configCondition = (string)configQtSettings.Attribute("Condition"); + + XElement oldConfigQtInstall; + if (oldQtInstall.TryGetValue(configCondition, out oldConfigQtInstall)) + configQtSettings.Add(oldConfigQtInstall); + + XElement oldConfigQtSettings; + if (oldQtSettings.TryGetValue(configCondition, out oldConfigQtSettings)) { + foreach (var qtSetting in oldConfigQtSettings.Elements()) + configQtSettings.Add(qtSetting); + } + } + + this[Files.Project].isDirty = true; + Commit(); + return true; + } + + //// Upgrading from v2.0 + + // Get project user properties (old format) + var userProps = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ProjectExtensions") + .Elements(ns + "VisualStudio") + .Elements(ns + "UserProperties") + .FirstOrDefault(); + + // Copy Qt build reference to QtInstall project property + this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(x => ((string)x.Attribute("Label")) == Resources.projLabelQtSettings) + .ToList() + .ForEach(config => + { + string qtInstallValue = defaultVersionName; + if (userProps != null) { + string platform = null; + try { + platform = ConfigCondition + .Parse((string)config.Attribute("Condition")) + .GetValues<string>("Platform") + .FirstOrDefault(); + } catch { } + + if (!string.IsNullOrEmpty(platform)) { + var qtInstallName = string.Format("Qt5Version_x0020_{0}", platform); + qtInstallValue = (string)userProps.Attribute(qtInstallName); + } + } + if (!string.IsNullOrEmpty(qtInstallValue)) + config.Add(new XElement(ns + "QtInstall", qtInstallValue)); + }); + + // Get C++ compiler properties + var compiler = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemDefinitionGroup") + .Elements(ns + "ClCompile"); + + // Get linker properties + var linker = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemDefinitionGroup") + .Elements(ns + "Link"); + + // Qt module names, to copy to QtModules property + var moduleNames = new HashSet<string>(); + + // Qt module macros, to remove from compiler macros property + var moduleDefines = new HashSet<string>(); + + // Qt module includes, to remove from compiler include directories property + var moduleIncludePaths = new HashSet<string>(); + + // Qt module link libraries, to remove from liker dependencies property + var moduleLibs = new HashSet<string>(); + + // Go through all known Qt modules and check which ones are currently being used + foreach (var module in QtModules.Instance.GetAvailableModules()) { + + if (IsModuleUsed(module, compiler, linker)) { + + // Qt module names, to copy to QtModules property + if (!string.IsNullOrEmpty(module.proVarQT)) + moduleNames.UnionWith(module.proVarQT.Split(' ')); + + // Qt module macros, to remove from compiler macros property + moduleDefines.UnionWith(module.Defines); + + // Qt module includes, to remove from compiler include directories property + moduleIncludePaths.UnionWith( + module.IncludePath.Select(x => Path.GetFileName(x))); + + // Qt module link libraries, to remove from liker dependencies property + moduleLibs.UnionWith( + module.AdditionalLibraries.Select(x => Path.GetFileName(x))); + moduleLibs.UnionWith( + module.AdditionalLibrariesDebug.Select(x => Path.GetFileName(x))); + moduleLibs.Add(module.LibRelease); + moduleLibs.Add(module.LibDebug); + + if (IsPrivateIncludePathUsed(module, compiler)) { + // Qt private module names, to copy to QtModules property + moduleNames.UnionWith(module.proVarQT.Split(' ') + .Select(x => string.Format("{0}-private", x))); + } + } + } + + // Remove Qt module macros from compiler properties + foreach (var defines in compiler.Elements(ns + "PreprocessorDefinitions")) { + defines.SetValue(string.Join(";", defines.Value.Split(';') + .Where(x => !moduleDefines.Contains(x)))); + } + + // Remove Qt module include paths from compiler properties + foreach (var inclPath in compiler.Elements(ns + "AdditionalIncludeDirectories")) { + inclPath.SetValue(string.Join(";", inclPath.Value.Split(';') + .Select(x => Unquote(x)) + // Exclude paths rooted on $(QTDIR) + .Where(x => !x.StartsWith("$(QTDIR)", IGNORE_CASE)))); + } + + // Remove Qt module libraries from linker properties + foreach (var libs in linker.Elements(ns + "AdditionalDependencies")) { + libs.SetValue(string.Join(";", libs.Value.Split(';') + .Where(x => !moduleLibs.Contains(Path.GetFileName(Unquote(x)), IGNORE_CASE_)))); + } + + // Remove Qt lib path from linker properties + foreach (var libs in linker.Elements(ns + "AdditionalLibraryDirectories")) { + libs.SetValue(string.Join(";", libs.Value.Split(';') + .Select(x => Unquote(x)) + // Exclude paths rooted on $(QTDIR) + .Where(x => !x.StartsWith("$(QTDIR)", IGNORE_CASE)))); + } + + // Add Qt module names to QtModules project property + this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(x => ((string)x.Attribute("Label")) == Resources.projLabelQtSettings) + .ToList() + .ForEach(x => x.Add(new XElement(ns + "QtModules", string.Join(";", moduleNames)))); + + // Remove project user properties (old format) + if (userProps != null) { + userProps.Attributes().ToList().ForEach(userProp => + { + if (userProp.Name.LocalName == "lupdateOptions" + || userProp.Name.LocalName == "lupdateOnBuild" + || userProp.Name.LocalName == "lreleaseOptions" + || userProp.Name.LocalName == "MocDir" + || userProp.Name.LocalName == "MocOptions" + || userProp.Name.LocalName == "RccDir" + || userProp.Name.LocalName == "UicDir" + || userProp.Name.LocalName.StartsWith("Qt5Version_x0020_")) { + userProp.Remove(); + } + }); + } + + // Remove old properties from .user file + if (this[Files.User].xml != null) { + this[Files.User].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Elements() + .ToList() + .ForEach(userProp => + { + if (userProp.Name.LocalName == "QTDIR" + || userProp.Name.LocalName == "QmlDebug" + || userProp.Name.LocalName == "QmlDebugSettings" + || (userProp.Name.LocalName == "LocalDebuggerCommandArguments" + && (string)userProp == "$(QmlDebug)" + ) + || (userProp.Name.LocalName == "LocalDebuggerEnvironment" + && (string)userProp == "PATH=$(QTDIR)\\bin%3b$(PATH)" + ) + ) { + userProp.Remove(); + } + }); + this[Files.User].isDirty = true; + } + + // Convert OutputFile --> <tool>Dir + <tool>FileName + var qtItems = this[Files.Project].xml + .Elements(ns + "Project") + .SelectMany(x => x.Elements(ns + "ItemDefinitionGroup") + .Union(x.Elements(ns + "ItemGroup"))) + .SelectMany(x => x.Elements(ns + "QtMoc") + .Union(x.Elements(ns + "QtRcc")) + .Union(x.Elements(ns + "QtUic"))); + foreach (var qtItem in qtItems) { + var outputFile = qtItem.Element(ns + "OutputFile"); + if (outputFile != null) { + var qtTool = qtItem.Name.LocalName; + var outDir = Path.GetDirectoryName(outputFile.Value); + var outFileName = Path.GetFileName(outputFile.Value); + if (!string.IsNullOrEmpty(outDir)) + qtItem.Add(new XElement(ns + qtTool + "Dir", outDir)); + else + qtItem.Add(new XElement(ns + qtTool + "Dir", "$(ProjectDir)")); + qtItem.Add(new XElement(ns + qtTool + "FileName", outFileName)); + } + } + + // Remove old properties from project items + var oldQtProps = new[] { "QTDIR", "InputFile", "OutputFile" }; + var oldCppProps = new[] { "IncludePath", "Define", "Undefine" }; + var oldPropsAny = oldQtProps.Union(oldCppProps); + this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemDefinitionGroup") + .Union(this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup")) + .Elements().ToList().ForEach(item => + { + var itemName = item.Name.LocalName; + item.Elements().ToList().ForEach(itemProp => + { + var propName = itemProp.Name.LocalName; + if ((itemName == "QtMoc" && oldPropsAny.Contains(propName)) + || (itemName == "QtRcc" && oldQtProps.Contains(propName)) + || (itemName == "QtUic" && oldQtProps.Contains(propName)) + || (itemName == "QtRepc" && oldPropsAny.Contains(propName)) + ) { + itemProp.Remove(); + } + }); + }); + + this[Files.Project].isDirty = true; + Commit(); + return true; + } + + bool IsModuleUsed( + QtModule module, + IEnumerable<XElement> compiler, + IEnumerable<XElement> linker) + { + // Module .lib is present in linker additional dependencies + if (linker.Elements(ns + "AdditionalDependencies") + .SelectMany(x => x.Value.Split(';')) + .Any(x => Path.GetFileName(Unquote(x)).Equals(module.LibRelease, IGNORE_CASE) + || Path.GetFileName(Unquote(x)).Equals(module.LibDebug, IGNORE_CASE))) { + return true; + } + + // Module macro is present in pre-processor definitions + if (compiler.Elements(ns + "PreprocessorDefinitions") + .SelectMany(x => x.Value.Split(';')) + .Any(x => module.Defines.Contains(x))) { + return true; + } + + // Module is not present + return false; + } + + bool IsPrivateIncludePathUsed( + QtModule module, + IEnumerable<XElement> compiler) + { + // Module private header path is present in compiler include dirs + var privateIncludePattern = new Regex(string.Format( + @"^\$\(QTDIR\)[\\\/]include[\\\/]{0}[\\\/]\d+\.\d+\.\d+", + module.LibraryPrefix)); + if (compiler.Elements(ns + "AdditionalIncludeDirectories") + .SelectMany(x => x.Value.Split(';')) + .Any(x => privateIncludePattern.IsMatch(x))) { + return true; + } + + // Private header path is not present + return false; + } + + public bool SetDefaultWindowsSDKVersion(string winSDKVersion) + { + var xGlobals = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "PropertyGroup") + .Where(x => (string)x.Attribute("Label") == "Globals") + .FirstOrDefault(); + if (xGlobals == null) + return false; + if (xGlobals.Element(ns + "WindowsTargetPlatformVersion") != null) + return true; + xGlobals.Add( + new XElement(ns + "WindowsTargetPlatformVersion", winSDKVersion)); + + this[Files.Project].isDirty = true; + Commit(); + return true; + } + + public bool AddQtMsBuildReferences() + { + var isQtMsBuildEnabled = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ImportGroup") + .Elements(ns + "Import") + .Where(x => + x.Attribute("Project").Value == @"$(QtMsBuild)\qt.props") + .Any(); + if (isQtMsBuildEnabled) + return true; + + var xImportCppProps = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "Import") + .Where(x => + x.Attribute("Project").Value == @"$(VCTargetsPath)\Microsoft.Cpp.props") + .FirstOrDefault(); + if (xImportCppProps == null) + return false; + + var xImportCppTargets = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "Import") + .Where(x => + x.Attribute("Project").Value == @"$(VCTargetsPath)\Microsoft.Cpp.targets") + .FirstOrDefault(); + if (xImportCppTargets == null) + return false; + + xImportCppProps.AddAfterSelf( + new XElement(ns + "PropertyGroup", + new XAttribute("Condition", + @"'$(QtMsBuild)'=='' " + + @"or !Exists('$(QtMsBuild)\qt.targets')"), + new XElement(ns + "QtMsBuild", + @"$(MSBuildProjectDirectory)\QtMsBuild")), + + new XElement(ns + "Target", + new XAttribute("Name", "QtMsBuildNotFound"), + new XAttribute("BeforeTargets", "CustomBuild;ClCompile"), + new XAttribute("Condition", + @"!Exists('$(QtMsBuild)\qt.targets') " + + @"or !Exists('$(QtMsBuild)\qt.props')"), + new XElement(ns + "Message", + new XAttribute("Importance", "High"), + new XAttribute("Text", + "QtMsBuild: could not locate qt.targets, qt.props; " + + "project may not build correctly."))), + + new XElement(ns + "ImportGroup", + new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt.props')"), + new XElement(ns + "Import", + new XAttribute("Project", @"$(QtMsBuild)\qt.props")))); + + xImportCppTargets.AddAfterSelf( + new XElement(ns + "ImportGroup", + new XAttribute("Condition", @"Exists('$(QtMsBuild)\qt.targets')"), + new XElement(ns + "Import", + new XAttribute("Project", @"$(QtMsBuild)\qt.targets")))); + + this[Files.Project].isDirty = true; + Commit(); + return true; + } + + delegate string ItemCommandLineReplacement(string itemName, string cmdLine); + + bool SetCommandLines( + QtMsBuildContainer qtMsBuild, + IEnumerable<XElement> configurations, + IEnumerable<XElement> customBuilds, + string toolExec, + string itemType, + string workingDir, + IEnumerable<ItemCommandLineReplacement> extraReplacements) + { + var query = from customBuild in customBuilds + let itemName = customBuild.Attribute("Include").Value + from config in configurations + from command in customBuild.Elements(ns + "Command") + where command.Attribute("Condition").Value + == string.Format( + "'$(Configuration)|$(Platform)'=='{0}'", + (string)config.Attribute("Include")) + select new { customBuild, itemName, config, command }; + + var projPath = this[Files.Project].filePath; + bool error = false; + using (var evaluator = new MSBuildEvaluator(this[Files.Project])) { + foreach (var row in query) { + + var configId = (string)row.config.Attribute("Include"); + if (!row.command.Value.Contains(toolExec)) { + Messages.Print(string.Format( + "{0}: warning: [{1}] converting \"{2}\", configuration \"{3}\": " + + "tool not found: \"{4}\"; applying default options", + projPath, itemType, row.itemName, configId, toolExec)); + continue; + } + + XElement item; + row.customBuild.Add(item = + new XElement(ns + itemType, + new XAttribute("Include", row.itemName), + new XAttribute("ConfigName", configId))); + var configName = (string)row.config.Element(ns + "Configuration"); + var platformName = (string)row.config.Element(ns + "Platform"); + + /////////////////////////////////////////////////////////////////////////////// + // Replace fixed values with VS macros + // + // * Filename, e.g. foo.ui --> %(Filename)%(Extension) + var commandLine = row.command.Value + .Replace(Path.GetFileName(row.itemName), "%(Filename)%(Extension)", + StringComparison.InvariantCultureIgnoreCase); + // + // * Context specific, e.g. ui_foo.h --> ui_%(FileName).h + foreach (var replace in extraReplacements) + commandLine = replace(row.itemName, commandLine); + // + // * Configuration/platform, e.g. x64\Debug --> $(Platform)\$(Configuration) + commandLine = commandLine + .Replace(configName, "$(Configuration)", + StringComparison.InvariantCultureIgnoreCase) + .Replace(platformName, "$(Platform)", + StringComparison.InvariantCultureIgnoreCase); + + evaluator.Properties.Clear(); + foreach (var configProp in row.config.Elements()) + evaluator.Properties.Add(configProp.Name.LocalName, (string)configProp); + if (!qtMsBuild.SetCommandLine(itemType, item, commandLine, evaluator)) { + int lineNumber = 1; + var errorLine = row.command as IXmlLineInfo; + if (errorLine != null && errorLine.HasLineInfo()) + lineNumber = errorLine.LineNumber; + + Messages.Print(string.Format( + "{0}({1}): error: [{2}] converting \"{3}\", configuration \"{4}\": " + + "failed to convert custom build command", + projPath, lineNumber, itemType, row.itemName, configId)); + + item.Remove(); + error = true; + } + } + } + + return !error; + } + + IEnumerable<XElement> GetCustomBuilds(string toolExecName) + { + return this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "CustomBuild") + .Where(x => x.Elements(ns + "Command") + .Where(y => (y.Value.Contains(toolExecName))).Any()); + } + + void FinalizeProjectChanges(List<XElement> customBuilds, string itemTypeName) + { + customBuilds + .Elements().Where( + elem => elem.Name.LocalName != itemTypeName) + .ToList().ForEach(oldElem => oldElem.Remove()); + + customBuilds.Elements(ns + itemTypeName).ToList().ForEach(item => + { + item.Elements().ToList().ForEach(prop => + { + string configName = prop.Parent.Attribute("ConfigName").Value; + prop.SetAttributeValue("Condition", + string.Format("'$(Configuration)|$(Platform)'=='{0}'", configName)); + prop.Remove(); + item.Parent.Add(prop); + }); + item.Remove(); + }); + + customBuilds.ForEach(customBuild => + { + var filterCustomBuild = this[Files.Filters]?.xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "CustomBuild") + .Where(filterItem => + filterItem.Attribute("Include").Value + == customBuild.Attribute("Include").Value) + .FirstOrDefault(); + if (filterCustomBuild != null) { + filterCustomBuild.Name = ns + itemTypeName; + this[Files.Filters].isDirty = true; + } + + customBuild.Name = ns + itemTypeName; + this[Files.Project].isDirty = true; + }); + } + + string AddGeneratedFilesPath(string includePathList) + { + HashSet<string> includes = new HashSet<string> { + QtVSIPSettings.GetMocDirectory(), + QtVSIPSettings.GetRccDirectory(), + QtVSIPSettings.GetUicDirectory(), + }; + foreach (var includePath in includePathList.Split(new char[] { ';' })) + includes.Add(includePath); + return string.Join<string>(";", includes); + } + + string CustomBuildMocInput(XElement cbt) + { + var commandLine = (string)cbt.Element(ns + "Command"); + Dictionary<QtMoc.Property, string> properties; + using (var evaluator = new MSBuildEvaluator(this[Files.Project])) { + if (!QtMsBuildContainer.QtMocInstance.ParseCommandLine( + commandLine, evaluator, out properties)) { + return (string)cbt.Attribute("Include"); + } + } + string ouputFile; + if (!properties.TryGetValue(QtMoc.Property.InputFile, out ouputFile)) + return (string)cbt.Attribute("Include"); + return ouputFile; + } + + bool RemoveGeneratedFiles( + string projDir, + List<CustomBuildEval> cbEvals, + string configName, + string itemName, + Dictionary<string, List<XElement>> projItemsByPath, + Dictionary<string, List<XElement>> filterItemsByPath) + { + //remove items with generated files + bool hasGeneratedFiles = false; + var cbEval = cbEvals + .Where(x => x.ProjectConfig == configName && x.Identity == itemName) + .FirstOrDefault(); + if (cbEval != null) { + var outputFiles = cbEval.Outputs + .Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => HelperFunctions.CanonicalPath( + Path.IsPathRooted(x) ? x : Path.Combine(projDir, x))); + var outputItems = new List<XElement>(); + foreach (var outputFile in outputFiles) { + List<XElement> mocOutput = null; + if (projItemsByPath.TryGetValue(outputFile, out mocOutput)) { + outputItems.AddRange(mocOutput); + hasGeneratedFiles |= hasGeneratedFiles ? true : mocOutput + .Where(x => !x.Elements(ns + "ExcludedFromBuild") + .Where(y => + (string)y.Attribute("Condition") == string.Format( + "'$(Configuration)|$(Platform)'=='{0}'", configName) + && y.Value == "true") + .Any()) + .Any(); + } + if (filterItemsByPath.TryGetValue(outputFile, out mocOutput)) + outputItems.AddRange(mocOutput); + } + foreach (var item in outputItems.Where(x => x.Parent != null)) + item.Remove(); + } + return hasGeneratedFiles; + } + + public bool ConvertCustomBuildToQtMsBuild() + { + var cbEvals = EvaluateCustomBuild(); + + var qtMsBuild = new QtMsBuildContainer(new MsBuildConverterProvider()); + qtMsBuild.BeginSetItemProperties(); + + var projDir = Path.GetDirectoryName(this[Files.Project].filePath); + + var configurations = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "ProjectConfiguration"); + + var projItemsByPath = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements() + .Where(x => ((string)x.Attribute("Include")) + .IndexOfAny(Path.GetInvalidPathChars()) == -1) + .GroupBy(x => HelperFunctions.CanonicalPath( + Path.Combine(projDir, (string)x.Attribute("Include"))), + StringComparer.InvariantCultureIgnoreCase) + .ToDictionary(x => x.Key, x => new List<XElement>(x)); + + var filterItemsByPath = (this[Files.Filters].xml != null) + ? this[Files.Filters].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements() + .Where(x => ((string)x.Attribute("Include")) + .IndexOfAny(Path.GetInvalidPathChars()) == -1) + .GroupBy(x => HelperFunctions.CanonicalPath( + Path.Combine(projDir, (string)x.Attribute("Include"))), + StringComparer.InvariantCultureIgnoreCase) + .ToDictionary(x => x.Key, x => new List<XElement>(x)) + : new Dictionary<string, List<XElement>>(); + + var cppIncludePaths = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemDefinitionGroup") + .Elements(ns + "ClCompile") + .Elements(ns + "AdditionalIncludeDirectories"); + + //add generated files path to C++ additional include dirs + foreach (var cppIncludePath in cppIncludePaths) + cppIncludePath.Value = AddGeneratedFilesPath((string)cppIncludePath); + + // replace each set of .moc.cbt custom build steps + // with a single .cpp custom build step + var mocCbtCustomBuilds = GetCustomBuilds(QtMoc.ToolExecName) + .Where(x => + ((string)x.Attribute("Include")).EndsWith(".cbt", + StringComparison.InvariantCultureIgnoreCase) + || ((string)x.Attribute("Include")).EndsWith(".moc", + StringComparison.InvariantCultureIgnoreCase)) + .GroupBy(cbt => CustomBuildMocInput(cbt)); + + List<XElement> cbtToRemove = new List<XElement>(); + foreach (var cbtGroup in mocCbtCustomBuilds) { + + //create new CustomBuild item for .cpp + var newCbt = new XElement(ns + "CustomBuild", + new XAttribute("Include", cbtGroup.Key), + new XElement(ns + "FileType", "Document")); + + //add properties from .moc.cbt items + List<string> cbtPropertyNames = new List<string> { + "AdditionalInputs", + "Command", + "Message", + "Outputs", + }; + foreach (var cbt in cbtGroup) { + var enabledProperties = cbt.Elements().Where(x => + cbtPropertyNames.Contains(x.Name.LocalName) + && !x.Parent.Elements(ns + "ExcludedFromBuild").Where(y => + (string)x.Attribute("Condition") == (string)y.Attribute("Condition")) + .Any()); + foreach (var property in enabledProperties) + newCbt.Add(new XElement(property)); + cbtToRemove.Add(cbt); + } + cbtGroup.First().AddBeforeSelf(newCbt); + + //remove ClCompile item (cannot have duplicate items) + var cppMocItems = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "ClCompile") + .Where(x => + cbtGroup.Key.Equals((string)x.Attribute("Include"), + StringComparison.InvariantCultureIgnoreCase)); + foreach (var cppMocItem in cppMocItems) + cppMocItem.Remove(); + + //change type of item in filter + cppMocItems = this[Files.Filters]?.xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "ClCompile") + .Where(x => + cbtGroup.Key.Equals((string)x.Attribute("Include"), + StringComparison.InvariantCultureIgnoreCase)); + foreach (var cppMocItem in cppMocItems) + cppMocItem.Name = ns + "CustomBuild"; + } + + //remove .moc.cbt CustomBuild items + cbtToRemove.ForEach(x => x.Remove()); + + //convert moc custom build steps + var mocCustomBuilds = GetCustomBuilds(QtMoc.ToolExecName); + if (!SetCommandLines(qtMsBuild, configurations, mocCustomBuilds, + QtMoc.ToolExecName, QtMoc.ItemTypeName, + Path.GetDirectoryName(this[Files.Project].filePath), + new ItemCommandLineReplacement[] + { + (item, cmdLine) => cmdLine.Replace( + string.Format(@"\moc_{0}.cpp", Path.GetFileNameWithoutExtension(item)), + @"\moc_%(Filename).cpp", StringComparison.InvariantCultureIgnoreCase) + .Replace( + string.Format(" -o moc_{0}.cpp", Path.GetFileNameWithoutExtension(item)), + @" -o $(ProjectDir)\moc_%(Filename).cpp", + StringComparison.InvariantCultureIgnoreCase), + + (item, cmdLine) => cmdLine.Replace( + string.Format(@"\{0}.moc", Path.GetFileNameWithoutExtension(item)), + @"\%(Filename).moc", StringComparison.InvariantCultureIgnoreCase) + .Replace( + string.Format(" -o {0}.moc", Path.GetFileNameWithoutExtension(item)), + @" -o $(ProjectDir)\%(Filename).moc", + StringComparison.InvariantCultureIgnoreCase), + })) { + Rollback(); + return false; + } + List<XElement> mocDisableDynamicSource = new List<XElement>(); + foreach (var qtMoc in mocCustomBuilds.Elements(ns + QtMoc.ItemTypeName)) { + var itemName = (string)qtMoc.Attribute("Include"); + var configName = (string)qtMoc.Attribute("ConfigName"); + + //remove items with generated files + var hasGeneratedFiles = RemoveGeneratedFiles( + projDir, cbEvals, configName, itemName, + projItemsByPath, filterItemsByPath); + + //set properties + qtMsBuild.SetItemProperty(qtMoc, + QtMoc.Property.ExecutionDescription, "Moc'ing %(Identity)..."); + qtMsBuild.SetItemProperty(qtMoc, + QtMoc.Property.InputFile, "%(FullPath)"); + if (!HelperFunctions.IsSourceFile(itemName)) { + qtMsBuild.SetItemProperty(qtMoc, + QtMoc.Property.DynamicSource, "output"); + if (!hasGeneratedFiles) + mocDisableDynamicSource.Add(qtMoc); + } else { + qtMsBuild.SetItemProperty(qtMoc, + QtMoc.Property.DynamicSource, "input"); + } + var includePath = qtMsBuild.GetPropertyChangedValue( + QtMoc.Property.IncludePath, itemName, configName); + if (!string.IsNullOrEmpty(includePath)) { + qtMsBuild.SetItemProperty(qtMoc, + QtMoc.Property.IncludePath, AddGeneratedFilesPath(includePath)); + } + } + + //convert rcc custom build steps + var rccCustomBuilds = GetCustomBuilds(QtRcc.ToolExecName); + if (!SetCommandLines(qtMsBuild, configurations, rccCustomBuilds, + QtRcc.ToolExecName, QtRcc.ItemTypeName, + Path.GetDirectoryName(this[Files.Project].filePath), + new ItemCommandLineReplacement[] + { + (item, cmdLine) => cmdLine.Replace( + string.Format(@"\qrc_{0}.cpp", Path.GetFileNameWithoutExtension(item)), + @"\qrc_%(Filename).cpp", StringComparison.InvariantCultureIgnoreCase) + .Replace( + string.Format(" -o qrc_{0}.cpp", Path.GetFileNameWithoutExtension(item)), + @" -o $(ProjectDir)\qrc_%(Filename).cpp", + StringComparison.InvariantCultureIgnoreCase), + })) { + Rollback(); + return false; + } + foreach (var qtRcc in rccCustomBuilds.Elements(ns + QtRcc.ItemTypeName)) { + var itemName = (string)qtRcc.Attribute("Include"); + var configName = (string)qtRcc.Attribute("ConfigName"); + + //remove items with generated files + RemoveGeneratedFiles(projDir, cbEvals, configName, itemName, + projItemsByPath, filterItemsByPath); + + //set properties + qtMsBuild.SetItemProperty(qtRcc, + QtRcc.Property.ExecutionDescription, "Rcc'ing %(Identity)..."); + qtMsBuild.SetItemProperty(qtRcc, + QtRcc.Property.InputFile, "%(FullPath)"); + } + + //convert repc custom build steps + var repcCustomBuilds = GetCustomBuilds(QtRepc.ToolExecName); + if (!SetCommandLines(qtMsBuild, configurations, repcCustomBuilds, + QtRepc.ToolExecName, QtRepc.ItemTypeName, + Path.GetDirectoryName(this[Files.Project].filePath), + new ItemCommandLineReplacement[] { })) { + Rollback(); + return false; + } + foreach (var qtRepc in repcCustomBuilds.Elements(ns + QtRepc.ItemTypeName)) { + var itemName = (string)qtRepc.Attribute("Include"); + var configName = (string)qtRepc.Attribute("ConfigName"); + + //remove items with generated files + RemoveGeneratedFiles(projDir, cbEvals, configName, itemName, + projItemsByPath, filterItemsByPath); + + //set properties + qtMsBuild.SetItemProperty(qtRepc, + QtRepc.Property.ExecutionDescription, "Repc'ing %(Identity)..."); + qtMsBuild.SetItemProperty(qtRepc, + QtRepc.Property.InputFile, "%(FullPath)"); + } + + + //convert uic custom build steps + var uicCustomBuilds = GetCustomBuilds(QtUic.ToolExecName); + if (!SetCommandLines(qtMsBuild, configurations, uicCustomBuilds, + QtUic.ToolExecName, QtUic.ItemTypeName, + Path.GetDirectoryName(this[Files.Project].filePath), + new ItemCommandLineReplacement[] + { + (item, cmdLine) => cmdLine.Replace( + string.Format(@"\ui_{0}.h", Path.GetFileNameWithoutExtension(item)), + @"\ui_%(Filename).h", StringComparison.InvariantCultureIgnoreCase) + .Replace( + string.Format(" -o ui_{0}.h", Path.GetFileNameWithoutExtension(item)), + @" -o $(ProjectDir)\ui_%(Filename).h", + StringComparison.InvariantCultureIgnoreCase), + })) { + Rollback(); + return false; + } + foreach (var qtUic in uicCustomBuilds.Elements(ns + QtUic.ItemTypeName)) { + var itemName = (string)qtUic.Attribute("Include"); + var configName = (string)qtUic.Attribute("ConfigName"); + + //remove items with generated files + RemoveGeneratedFiles(projDir, cbEvals, configName, itemName, + projItemsByPath, filterItemsByPath); + + //set properties + qtMsBuild.SetItemProperty(qtUic, + QtUic.Property.ExecutionDescription, "Uic'ing %(Identity)..."); + qtMsBuild.SetItemProperty(qtUic, + QtUic.Property.InputFile, "%(FullPath)"); + } + + qtMsBuild.EndSetItemProperties(); + + //disable dynamic C++ source for moc headers without generated files + //(needed for the case of #include "moc_foo.cpp" in source file) + foreach (var qtMoc in mocDisableDynamicSource) { + qtMsBuild.SetItemProperty(qtMoc, + QtMoc.Property.DynamicSource, "false"); + } + + FinalizeProjectChanges(mocCustomBuilds.ToList(), QtMoc.ItemTypeName); + FinalizeProjectChanges(rccCustomBuilds.ToList(), QtRcc.ItemTypeName); + FinalizeProjectChanges(repcCustomBuilds.ToList(), QtRepc.ItemTypeName); + FinalizeProjectChanges(uicCustomBuilds.ToList(), QtUic.ItemTypeName); + + this[Files.Project].isDirty = this[Files.Filters].isDirty = true; + Commit(); + return true; + } + + bool TryReplaceTextInPlace(ref string text, Regex findWhat, string newText) + { + var match = findWhat.Match(text); + if (!match.Success) + return false; + do { + text = text.Remove(match.Index, match.Length).Insert(match.Index, newText); + match = findWhat.Match(text, match.Index); + } while (match.Success); + + return true; + } + + void ReplaceText(XElement xElem, Regex findWhat, string newText) + { + var elemValue = (string)xElem; + if (!string.IsNullOrEmpty(elemValue) + && TryReplaceTextInPlace(ref elemValue, findWhat, newText)) { + xElem.Value = elemValue; + } + } + + void ReplaceText(XAttribute xAttr, Regex findWhat, string newText) + { + var attrValue = (string)xAttr; + if (!string.IsNullOrEmpty(attrValue) + && TryReplaceTextInPlace(ref attrValue, findWhat, newText)) { + xAttr.Value = attrValue; + } + } + + /// <summary> + /// All path separators + /// </summary> + static readonly char[] slashChars = new[] + { + Path.DirectorySeparatorChar, + Path.AltDirectorySeparatorChar + }; + + /// <summary> + /// Pattern that matches one path separator char + /// </summary> + static readonly RegExpr slash = CharSet[slashChars]; + + /// <summary> + /// Gets a RegExpr that matches a given path, regardless + /// of case and varying directory separators + /// </summary> + static RegExpr GetPathPattern(string findWhatPath) + { + return + // Make pattern case-insensitive + IgnoreCase & + // Split path string by directory separators + findWhatPath.Split(slashChars, StringSplitOptions.RemoveEmptyEntries) + // Convert path parts to RegExpr (escapes regex special chars) + .Select(dirName => (RegExpr)dirName) + // Join all parts, separated by path separator pattern + .Aggregate((path, dirName) => path & slash & dirName); + } + + public void ReplacePath(string oldPath, string newPath) + { + Uri srcUri = new Uri(Path.GetFullPath(oldPath)); + Uri projUri = new Uri(this[Files.Project].filePath); + + RegExpr absolutePath = GetPathPattern(srcUri.AbsolutePath); + RegExpr relativePath = GetPathPattern(projUri.MakeRelativeUri(srcUri).OriginalString); + + Regex findWhat = (absolutePath | relativePath).Render().Regex; + + foreach (var xElem in this[Files.Project].xml.Descendants()) { + if (!xElem.HasElements) + ReplaceText(xElem, findWhat, newPath); + foreach (var xAttr in xElem.Attributes()) + ReplaceText(xAttr, findWhat, newPath); + } + this[Files.Project].isDirty = true; + Commit(); + } + + class MSBuildEvaluator : IVSMacroExpander, IDisposable + { + MsBuildXmlFile projFile; + string tempProjFilePath; + XElement evaluateTarget; + XElement evaluateProperty; + ProjectRootElement projRoot; + public Dictionary<string, string> expansionCache; + + public Dictionary<string, string> Properties + { + get; + private set; + } + + public MSBuildEvaluator(MsBuildXmlFile projFile) + { + this.projFile = projFile; + tempProjFilePath = string.Empty; + evaluateTarget = evaluateProperty = null; + expansionCache = new Dictionary<string, string>(); + Properties = new Dictionary<string, string>(); + } + + public void Dispose() + { + if (evaluateTarget != null) { + evaluateTarget.Remove(); + if (File.Exists(tempProjFilePath)) + File.Delete(tempProjFilePath); + } + } + + string ExpansionCacheKey(string stringToExpand) + { + var key = new StringBuilder(); + foreach (var property in Properties) + key.AppendFormat("{0};{1};", property.Key, property.Value); + key.Append(stringToExpand); + return key.ToString(); + } + + bool TryExpansionCache(string stringToExpand, out string expandedString) + { + return expansionCache.TryGetValue( + ExpansionCacheKey(stringToExpand), out expandedString); + } + + void AddToExpansionCache(string stringToExpand, string expandedString) + { + expansionCache[ExpansionCacheKey(stringToExpand)] = expandedString; + } + + public string ExpandString(string stringToExpand) + { + string expandedString; + if (TryExpansionCache(stringToExpand, out expandedString)) + return expandedString; + + if (evaluateTarget == null) { + projFile.xmlCommitted.Root.Add(evaluateTarget = new XElement(ns + "Target", + new XAttribute("Name", "MSBuildEvaluatorTarget"), + new XElement(ns + "PropertyGroup", + evaluateProperty = new XElement(ns + "MSBuildEvaluatorProperty")))); + } + if (stringToExpand != (string)evaluateProperty) { + evaluateProperty.SetValue(stringToExpand); + if (!string.IsNullOrEmpty(tempProjFilePath) && File.Exists(tempProjFilePath)) + File.Delete(tempProjFilePath); + tempProjFilePath = Path.Combine( + Path.GetDirectoryName(projFile.filePath), + Path.GetRandomFileName()); + if (File.Exists(tempProjFilePath)) + File.Delete(tempProjFilePath); + projFile.xmlCommitted.Save(tempProjFilePath); + projRoot = ProjectRootElement.Open(tempProjFilePath); + } + var projInst = new ProjectInstance(projRoot, Properties, + null, new ProjectCollection()); + var buildRequest = new BuildRequestData( + projInst, new string[] { "MSBuildEvaluatorTarget" }, + null, BuildRequestDataFlags.ProvideProjectStateAfterBuild); + var buildResult = BuildManager.DefaultBuildManager.Build( + new BuildParameters(), buildRequest); + expandedString = buildResult.ProjectStateAfterBuild + .GetPropertyValue("MSBuildEvaluatorProperty"); + + AddToExpansionCache(stringToExpand, expandedString); + return expandedString; + } + } + + class CustomBuildEval + { + public string ProjectConfig { get; set; } + public string Identity { get; set; } + public string AdditionalInputs { get; set; } + public string Outputs { get; set; } + public string Message { get; set; } + public string Command { get; set; } + } + + List<CustomBuildEval> EvaluateCustomBuild() + { + var eval = new List<CustomBuildEval>(); + + var pattern = new Regex(@"{([^}]+)}{([^}]+)}{([^}]+)}{([^}]+)}{([^}]+)}"); + + var projConfigs = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "ProjectConfiguration"); + + using (var evaluator = new MSBuildEvaluator(this[Files.Project])) { + + foreach (var projConfig in projConfigs) { + + evaluator.Properties.Clear(); + foreach (var configProp in projConfig.Elements()) + evaluator.Properties.Add(configProp.Name.LocalName, (string)configProp); + + var expandedValue = evaluator.ExpandString( + "@(CustomBuild->'" + + "{%(Identity)}" + + "{%(AdditionalInputs)}" + + "{%(Outputs)}" + + "{%(Message)}" + + "{%(Command)}')"); + + foreach (Match cbEval in pattern.Matches(expandedValue)) { + eval.Add(new CustomBuildEval + { + ProjectConfig = (string)projConfig.Attribute("Include"), + Identity = cbEval.Groups[1].Value, + AdditionalInputs = cbEval.Groups[2].Value, + Outputs = cbEval.Groups[3].Value, + Message = cbEval.Groups[4].Value, + Command = cbEval.Groups[5].Value, + }); + } + } + } + + return eval; + } + + public bool BuildTarget(string target) + { + if (this[Files.Project].isDirty) + return false; + + var configurations = this[Files.Project].xml + .Elements(ns + "Project") + .Elements(ns + "ItemGroup") + .Elements(ns + "ProjectConfiguration"); + + using (var buildManager = new BuildManager()) { + + foreach (var config in configurations) { + + var configProps = config.Elements() + .ToDictionary(x => x.Name.LocalName, x => x.Value); + + var projectInstance = new ProjectInstance(this[Files.Project].filePath, + new Dictionary<string, string>(configProps) + { { "QtVSToolsBuild", "true" } }, + null, new ProjectCollection()); + + var buildRequest = new BuildRequestData(projectInstance, + targetsToBuild: new[] { target }, + hostServices: null, + flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild); + + var result = buildManager.Build(new BuildParameters(), buildRequest); + if (result.OverallResult != BuildResultCode.Success) + return false; + + } + } + return true; + } + + static Regex ConditionParser = + new Regex(@"\'\$\(Configuration[^\)]*\)\|\$\(Platform[^\)]*\)\'\=\=\'([^\']+)\'"); + + class MsBuildConverterProvider : IPropertyStorageProvider + { + public string GetProperty(object propertyStorage, string itemType, string propertyName) + { + XElement xmlPropertyStorage = propertyStorage as XElement; + if (xmlPropertyStorage == null) + return ""; + XElement item; + if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") { + item = xmlPropertyStorage.Element(ns + itemType); + if (item == null) + return ""; + } else { + item = xmlPropertyStorage; + } + var prop = item.Element(ns + propertyName); + if (prop == null) + return ""; + return prop.Value; + } + + public bool SetProperty( + object propertyStorage, + string itemType, + string propertyName, + string propertyValue) + { + XElement xmlPropertyStorage = propertyStorage as XElement; + if (xmlPropertyStorage == null) + return false; + XElement item; + if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") { + item = xmlPropertyStorage.Element(ns + itemType); + if (item == null) + xmlPropertyStorage.Add(item = new XElement(ns + itemType)); + } else { + item = xmlPropertyStorage; + } + var prop = item.Element(ns + propertyName); + if (prop != null) + prop.Value = propertyValue; + else + item.Add(new XElement(ns + propertyName, propertyValue)); + return true; + } + + public bool DeleteProperty( + object propertyStorage, + string itemType, + string propertyName) + { + XElement xmlPropertyStorage = propertyStorage as XElement; + if (xmlPropertyStorage == null) + return false; + XElement item; + if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") { + item = xmlPropertyStorage.Element(ns + itemType); + if (item == null) + return true; + } else { + item = xmlPropertyStorage; + } + + var prop = item.Element(ns + propertyName); + if (prop != null) + prop.Remove(); + return true; + } + + public string GetConfigName(object propertyStorage) + { + XElement xmlPropertyStorage = propertyStorage as XElement; + if (xmlPropertyStorage == null) + return ""; + if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") { + var configName = ConditionParser + .Match(xmlPropertyStorage.Attribute("Condition").Value); + if (!configName.Success || configName.Groups.Count <= 1) + return ""; + return configName.Groups[1].Value; + } + return xmlPropertyStorage.Attribute("ConfigName").Value; + } + + public string GetItemType(object propertyStorage) + { + XElement xmlPropertyStorage = propertyStorage as XElement; + if (xmlPropertyStorage == null) + return ""; + if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") + return ""; + return xmlPropertyStorage.Name.LocalName; + } + + public string GetItemName(object propertyStorage) + { + XElement xmlPropertyStorage = propertyStorage as XElement; + if (xmlPropertyStorage == null) + return ""; + if (xmlPropertyStorage.Name.LocalName == "ItemDefinitionGroup") + return ""; + return xmlPropertyStorage.Attribute("Include").Value; + } + + public object GetParentProject(object propertyStorage) + { + XElement xmlPropertyStorage = propertyStorage as XElement; + if (xmlPropertyStorage == null) + return ""; + if (xmlPropertyStorage.Document == null) + return null; + return xmlPropertyStorage.Document.Root; + } + + public object GetProjectConfiguration(object project, string configName) + { + XElement xmlProject = project as XElement; + if (xmlProject == null) + return null; + return xmlProject.Elements(ns + "ItemDefinitionGroup") + .Where(config => config.Attribute("Condition").Value.Contains(configName)) + .FirstOrDefault(); + } + + public IEnumerable<object> GetItems( + object project, + string itemType, + string configName = "") + { + XElement xmlProject = project as XElement; + if (xmlProject == null) + return new List<object>(); + return + xmlProject.Elements(ns + "ItemGroup") + .Elements(ns + "CustomBuild") + .Elements(ns + itemType) + .Where(item => ( + configName == "" + || item.Attribute("ConfigName").Value == configName)) + .GroupBy(item => item.Attribute("Include").Value) + .Select(item => item.First()); + } + } + + } +} diff --git a/QtVsTools.Core/Observable.cs b/QtVsTools.Core/Observable.cs new file mode 100644 index 0000000..258430f --- /dev/null +++ b/QtVsTools.Core/Observable.cs @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace QtVsTools.Core +{ + public abstract class Observable : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public static T GetPropertyValue<T>(object obj, string name) + { + var retval = GetPropertyValue(obj, name); + if (retval == null) + return default(T); + return (T)retval; + } + + public static object GetPropertyValue(object obj, string name) + { + foreach (var part in name.Split('.')) { + if (obj == null) + return null; + + var type = obj.GetType(); + var propertyInfo = type.GetProperty(part); + if (propertyInfo == null) + return null; + obj = propertyInfo.GetValue(obj, null); + } + return obj; + } + + protected void SetValue<T>(ref T storage, T value, [CallerMemberName] string name = null) + { + if (Equals(storage, value)) + return; + + storage = value; + OnPropertyChanged(name); + } + + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var eventHandler = PropertyChanged; + if (eventHandler != null) + eventHandler.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/QtVsTools.Core/ProFileContent.cs b/QtVsTools.Core/ProFileContent.cs new file mode 100644 index 0000000..8bd3d2b --- /dev/null +++ b/QtVsTools.Core/ProFileContent.cs @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using Microsoft.VisualStudio.VCProjectEngine; + +namespace QtVsTools.Core +{ + internal class ProFileContent + { + public ProFileContent(VCProject proj) + { + export = true; + vcproj = proj; + options = new List<ProFileOption>(); + } + + public override string ToString() + { + return vcproj.Name; + } + + public VCProject Project + { + get + { + return vcproj; + } + } + + public bool Export + { + get + { + return export; + } + set + { + export = value; + } + } + + public List<ProFileOption> Options + { + get + { + return options; + } + } + + private VCProject vcproj; + private bool export; + private List<ProFileOption> options; + } +} diff --git a/QtVsTools.Core/ProFileOption.cs b/QtVsTools.Core/ProFileOption.cs new file mode 100644 index 0000000..54468ef --- /dev/null +++ b/QtVsTools.Core/ProFileOption.cs @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; + +namespace QtVsTools.Core +{ + internal class ProFileOption + { + public ProFileOption(string optname) + { + name = optname; + astype = AssignType.AT_PlusEquals; + comment = null; + shortComment = "Default"; + incComment = false; + newOpt = " \\\r\n "; + list = new List<string>(); + } + + public override string ToString() + { + return shortComment; + } + + public string Comment + { + get + { + return comment; + } + set + { + comment = value; + } + } + + public string ShortComment + { + get + { + return shortComment; + } + set + { + shortComment = value; + } + } + + public AssignType AssignSymbol + { + get + { + return astype; + } + set + { + astype = value; + } + } + + public string NewOption + { + get + { + return newOpt; + } + set + { + newOpt = value; + } + } + + public string Name + { + get + { + return name; + } + } + + public List<string> List + { + get + { + return list; + } + } + + public bool IncludeComment + { + get + { + return incComment; + } + set + { + incComment = value; + } + } + + public enum AssignType + { + AT_Equals = 1, + AT_PlusEquals = 2, // default + AT_MinusEquals = 3, + AT_Function = 4 + } + + private AssignType astype; + private string shortComment; + private bool incComment; + private string comment; + private string newOpt; + private string name; + private List<string> list; + } +} diff --git a/QtVsTools.Core/ProSolution.cs b/QtVsTools.Core/ProSolution.cs new file mode 100644 index 0000000..497f213 --- /dev/null +++ b/QtVsTools.Core/ProSolution.cs @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using EnvDTE; + +namespace QtVsTools.Core +{ + internal class ProSolution + { + public ProSolution(Solution sln) + { + prosln = sln; + proFiles = new List<ProFileContent>(); + } + + public List<ProFileContent> ProFiles + { + get + { + return proFiles; + } + } + + public Solution ProjectSolution + { + get + { + return prosln; + } + } + + private List<ProFileContent> proFiles; + private Solution prosln; + } +} diff --git a/QtVsTools.Core/ProjectExporter.cs b/QtVsTools.Core/ProjectExporter.cs new file mode 100644 index 0000000..681bd33 --- /dev/null +++ b/QtVsTools.Core/ProjectExporter.cs @@ -0,0 +1,853 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using EnvDTE80; +using Microsoft.VisualStudio.VCProjectEngine; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Windows.Forms; + +namespace QtVsTools.Core +{ + /// <summary> + /// Summary description for ProjectExporter. + /// </summary> + public class ProjectExporter + { + private DTE dteObject; + + public ProjectExporter(DTE dte) + { + dteObject = dte; + } + + private static void MakeFilesRelativePath(VCProject vcproj, List<string> files, string path) + { + for (var i = 0; i < files.Count; i++) { + var relPath = string.Empty; + if (files[i].IndexOf(':') != 1) { + relPath = HelperFunctions.GetRelativePath(path, + vcproj.ProjectDirectory + "\\" + files[i]); + } else { + relPath = HelperFunctions.GetRelativePath(path, files[i]); + } + files[i] = HelperFunctions.ChangePathFormat(relPath); + } + } + + private static bool ContainsFilesWithSpaces(List<string> files) + { + for (var i = 0; i < files.Count; i++) { + if (files[i].IndexOf(' ') != -1) + return true; + } + + return false; + } + + public static List<string> ConvertFilesToFullPath(List<string> files, string path) + { + var ret = new List<string>(files.Count); + foreach (var file in files) { + FileInfo fi; + if (file.IndexOf(':') != 1) + fi = new FileInfo(path + "\\" + file); + else + fi = new FileInfo(file); + + ret.Add(fi.FullName); + } + return ret; + } + + private ProSolution CreateProFileSolution(Solution sln) + { + ProFileContent content; + var prosln = new ProSolution(sln); + + foreach (var proj in HelperFunctions.ProjectsInSolution(sln.DTE)) { + try { + // only add qt projects + if (HelperFunctions.IsQtProject(proj)) { + content = CreateProFileContent(proj); + prosln.ProFiles.Add(content); + } else if (proj.Kind == ProjectKinds.vsProjectKindSolutionFolder) { + addProjectsInFolder(proj, prosln); + } + } catch { + // Catch all exceptions. Try to add as many projects as possible. + } + } + + return prosln; + } + + private void addProjectsInFolder(Project solutionFolder, ProSolution sln) + { + foreach (ProjectItem pi in solutionFolder.ProjectItems) { + var containedProject = pi.Object as Project; + if (HelperFunctions.IsQtProject(containedProject)) { + var content = CreateProFileContent(containedProject); + sln.ProFiles.Add(content); + } else if (containedProject.Kind == ProjectKinds.vsProjectKindSolutionFolder) { + addProjectsInFolder(containedProject, sln); + } + } + } + + private static ProFileContent CreateProFileContent(Project project) + { + ProFileOption option; + var qtPro = QtProject.Create(project); + var content = new ProFileContent(qtPro.VCProject); + + // hack to get active config + var activeConfig = project.ConfigurationManager.ActiveConfiguration.ConfigurationName; + var activePlatform = project.ConfigurationManager.ActiveConfiguration.PlatformName; + var config = (VCConfiguration)((IVCCollection)qtPro.VCProject.Configurations).Item(activeConfig); + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + var libTool = (VCLibrarianTool)((IVCCollection)config.Tools).Item("VCLibrarianTool"); + + var outPut = config.PrimaryOutput; + var fi = new FileInfo(outPut); + var destdir = HelperFunctions.GetRelativePath(qtPro.VCProject.ProjectDirectory, fi.DirectoryName); + destdir = HelperFunctions.ChangePathFormat(destdir); + var target = qtPro.VCProject.Name; + + option = new ProFileOption("TEMPLATE"); + option.Comment = Resources.ec_Template; + option.ShortComment = "Template"; + option.NewOption = null; // just one option... + option.AssignSymbol = ProFileOption.AssignType.AT_Equals; + content.Options.Add(option); + if (config.ConfigurationType == ConfigurationTypes.typeApplication) + option.List.Add("app"); + else + option.List.Add("lib"); + + option = new ProFileOption("TARGET"); + option.Comment = Resources.ec_Target; + option.ShortComment = "Target Name"; + option.NewOption = null; // just one option... + option.AssignSymbol = ProFileOption.AssignType.AT_Equals; + content.Options.Add(option); + option.List.Add(target); + + option = new ProFileOption("DESTDIR"); + option.Comment = Resources.ec_DestDir; + option.ShortComment = "Destination Directory"; + option.NewOption = null; // just one option... + option.AssignSymbol = ProFileOption.AssignType.AT_Equals; + content.Options.Add(option); + option.List.Add(destdir); + + // add the qt option + option = new ProFileOption("QT"); + var optionQT = option; + option.Comment = Resources.ec_Qt; + option.ShortComment = "Qt Options"; + option.NewOption = " "; // just space between the options... + content.Options.Add(option); + + // add the config option + option = new ProFileOption("CONFIG"); + var optionCONFIG = option; + option.Comment = Resources.ec_Config; + option.ShortComment = "Config Options"; + option.NewOption = " "; // just space between the options... + content.Options.Add(option); + + AddModules(qtPro, optionQT, optionCONFIG); + + if (config.ConfigurationType == ConfigurationTypes.typeStaticLibrary) + option.List.Add("staticlib"); + if (linker != null) { + var linkerRule = linker as IVCRulePropertyStorage; + var generateDebugInformation = (linkerRule != null) ? + linkerRule.GetUnevaluatedPropertyValue("GenerateDebugInformation") : null; + if (generateDebugInformation != "false") + option.List.Add("debug"); + else + option.List.Add("release"); + + if (linker.SubSystem == subSystemOption.subSystemConsole) + option.List.Add("console"); + + if (linker.AdditionalDependencies != null) { + if (linker.AdditionalDependencies.IndexOf("QAxServer", StringComparison.Ordinal) > -1) + option.List.Add("qaxserver"); + else if (linker.AdditionalDependencies.IndexOf("QAxContainer", StringComparison.Ordinal) > -1) + option.List.Add("qaxcontainer"); + else if (linker.AdditionalDependencies.IndexOf("QtHelp", StringComparison.Ordinal) > -1) + option.List.Add("help"); + } + } + + if (qtPro.IsDesignerPluginProject()) { + option.List.Add("designer"); + option.List.Add("plugin"); + } + + // add defines + option = new ProFileOption("DEFINES"); + option.Comment = Resources.ec_Defines; + option.ShortComment = "Defines"; + option.NewOption = " "; + option.AssignSymbol = ProFileOption.AssignType.AT_PlusEquals; + content.Options.Add(option); + AddPreprocessorDefinitions(option, compiler.GetPreprocessorDefinitions()); + + // add the include path option + option = new ProFileOption("INCLUDEPATH"); + option.Comment = Resources.ec_IncludePath; + option.ShortComment = "Include Path"; + content.Options.Add(option); + AddIncludePaths(project, option, compiler.GetAdditionalIncludeDirectories()); + + option = new ProFileOption("LIBS"); + option.Comment = Resources.ec_Libs; + option.ShortComment = "Additional Libraries"; + content.Options.Add(option); + if (linker != null) { + AddLibraries(project, option, linker.AdditionalLibraryDirectories, + linker.AdditionalDependencies); + } else if (libTool != null) { + AddLibraries(project, option, libTool.AdditionalLibraryDirectories, + libTool.AdditionalDependencies); + } + + option = new ProFileOption("PRECOMPILED_HEADER"); + option.Comment = Resources.ec_PrecompiledHeader; + option.ShortComment = "Using Precompiled Headers"; + option.AssignSymbol = ProFileOption.AssignType.AT_Equals; + content.Options.Add(option); + + if (qtPro.UsesPrecompiledHeaders()) + option.List.Add(compiler.GetPrecompiledHeaderThrough()); + + // add the depend path option + option = new ProFileOption("DEPENDPATH"); + option.Comment = Resources.ec_DependPath; + option.ShortComment = "Depend Path"; + content.Options.Add(option); + option.List.Add("."); + + var mocDir = QtVSIPSettings.GetMocDirectory(project, activeConfig.ToLower(), activePlatform.ToLower()); + mocDir = mocDir.Replace('\\', '/'); + option = new ProFileOption("MOC_DIR"); + option.Comment = Resources.ec_MocDir; + option.ShortComment = "Moc Directory"; + option.NewOption = null; // just one option... + content.Options.Add(option); + option.List.Add(mocDir); + + option = new ProFileOption("OBJECTS_DIR"); + option.Comment = Resources.ec_ObjDir; + option.ShortComment = "Objects Directory"; + option.NewOption = null; // just one option... + content.Options.Add(option); + option.List.Add(config.ConfigurationName.ToLower()); + + var uiDir = QtVSIPSettings.GetUicDirectory(project); + uiDir = uiDir.Replace('\\', '/'); + option = new ProFileOption("UI_DIR"); + option.Comment = Resources.ec_UiDir; + option.ShortComment = "UI Directory"; + option.NewOption = null; // just one option... + content.Options.Add(option); + option.List.Add(uiDir); + + var rccDir = QtVSIPSettings.GetRccDirectory(project); + rccDir = rccDir.Replace('\\', '/'); + option = new ProFileOption("RCC_DIR"); + option.Comment = Resources.ec_RccDir; + option.ShortComment = "RCC Directory"; + option.NewOption = null; // just one option... + content.Options.Add(option); + option.List.Add(rccDir); + + // add the include path option + option = new ProFileOption("include"); + option.Comment = Resources.ec_Include; + option.ShortComment = "Include file(s)"; + option.IncludeComment = false; // print the comment in the output file + option.AssignSymbol = ProFileOption.AssignType.AT_Function; + content.Options.Add(option); + + // add the translation files + option = new ProFileOption("TRANSLATIONS"); + option.Comment = Resources.ec_Translations; + option.ShortComment = "Translation files"; + option.IncludeComment = false; + content.Options.Add(option); + option.List.AddRange(HelperFunctions.GetProjectFiles(project, FilesToList.FL_Translation)); + + // add the rc file + if (File.Exists(qtPro.VCProject.ProjectDirectory + "\\" + project.Name + ".rc")) { + option = new ProFileOption("win32:RC_FILE"); + option.Comment = Resources.ec_rcFile; + option.ShortComment = "Windows resource file"; + option.IncludeComment = false; + option.AssignSymbol = ProFileOption.AssignType.AT_Equals; + content.Options.Add(option); + option.List.Add(project.Name + ".rc"); + } + + if (qtPro.IsDesignerPluginProject()) { + option = new ProFileOption("target.path"); + option.ShortComment = "Install the plugin in the designer plugins directory."; + option.IncludeComment = true; + option.AssignSymbol = ProFileOption.AssignType.AT_Equals; + option.List.Add("$$[QT_INSTALL_PLUGINS]/designer"); + content.Options.Add(option); + + option = new ProFileOption("INSTALLS"); + option.IncludeComment = false; + option.AssignSymbol = ProFileOption.AssignType.AT_PlusEquals; + option.List.Add("target"); + content.Options.Add(option); + } + + return content; + } + + private static ProFileContent CreatePriFileContent(Project project, string priFileDirectory) + { + ProFileOption option; + var qtPro = QtProject.Create(project); + var content = new ProFileContent(qtPro.VCProject); + var hasSpaces = false; + + // add the header files + option = new ProFileOption("HEADERS"); + option.ShortComment = "Header files"; + option.IncludeComment = false; + content.Options.Add(option); + option.List.AddRange(HelperFunctions.GetProjectFiles(project, FilesToList.FL_HFiles)); + MakeFilesRelativePath(qtPro.VCProject, option.List, priFileDirectory); + hasSpaces |= ContainsFilesWithSpaces(option.List); + + // add the source files + option = new ProFileOption("SOURCES"); + option.ShortComment = "Source files"; + option.IncludeComment = false; + content.Options.Add(option); + option.List.AddRange(HelperFunctions.GetProjectFiles(project, FilesToList.FL_CppFiles)); + MakeFilesRelativePath(qtPro.VCProject, option.List, priFileDirectory); + hasSpaces |= ContainsFilesWithSpaces(option.List); + + // add the form files + option = new ProFileOption("FORMS"); + option.ShortComment = "Forms"; + option.IncludeComment = false; + content.Options.Add(option); + option.List.AddRange(HelperFunctions.GetProjectFiles(project, FilesToList.FL_UiFiles)); + MakeFilesRelativePath(qtPro.VCProject, option.List, priFileDirectory); + hasSpaces |= ContainsFilesWithSpaces(option.List); + + // add the translation files + option = new ProFileOption("TRANSLATIONS"); + option.Comment = Resources.ec_Translations; + option.ShortComment = "Translation file(s)"; + option.IncludeComment = false; + option.List.AddRange(HelperFunctions.GetProjectFiles(project, FilesToList.FL_Translation)); + MakeFilesRelativePath(qtPro.VCProject, option.List, priFileDirectory); + hasSpaces |= ContainsFilesWithSpaces(option.List); + content.Options.Add(option); + + // add the resource files + option = new ProFileOption("RESOURCES"); + option.Comment = Resources.ec_Resources; + option.ShortComment = "Resource file(s)"; + option.IncludeComment = false; + content.Options.Add(option); + + foreach (var resFile in qtPro.GetResourceFiles()) + option.List.Add(resFile.RelativePath.Replace('\\', '/')); + + if (hasSpaces) + Messages.DisplayWarningMessage(SR.GetString("ExportProject_PriFileContainsSpaces")); + + return content; + } + + private static void AddPreprocessorDefinitions(ProFileOption option, string preprocessorDefinitions) + { + if (preprocessorDefinitions == null) + return; + + var excludeList = "UNICODE WIN32 NDEBUG QDESIGNER_EXPORT_WIDGETS "; + excludeList += "QT_THREAD_SUPPORT QT_PLUGIN QT_NO_DEBUG QT_CORE_LIB QT_GUI_LIB"; + + foreach (var define in preprocessorDefinitions.Split(';', ',')) { + if (excludeList.IndexOf(define, StringComparison.OrdinalIgnoreCase) == -1) + option.List.Add(define); + } + } + + private static void AddIncludePaths(Project project, ProFileOption option, string includePaths) + { + if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_ClProperties) + return; + + if (includePaths == null) + return; + + var versionManager = QtVersionManager.The(); + var qtDir = versionManager.GetInstallPath(project); + if (qtDir == null) + qtDir = Environment.GetEnvironmentVariable("QTDIR"); + if (qtDir == null) + qtDir = ""; + + qtDir = HelperFunctions.NormalizeRelativeFilePath(qtDir); + + foreach (var s in includePaths.Split(';', ',')) { + var d = HelperFunctions.NormalizeRelativeFilePath(s); + if (!d.StartsWith("$(qtdir)\\include", StringComparison.OrdinalIgnoreCase) && + !d.StartsWith(qtDir + "\\include", StringComparison.OrdinalIgnoreCase) && + !d.EndsWith("win32-msvc2005", StringComparison.OrdinalIgnoreCase)) { + + var vcConfig = project.ConfigurationManager.ActiveConfiguration.Object + as VCConfiguration; + if (vcConfig != null) + HelperFunctions.ExpandString(ref d, vcConfig); + if (HelperFunctions.IsAbsoluteFilePath(d)) + d = HelperFunctions.GetRelativePath(project.FullName, d); + if (!HelperFunctions.IsAbsoluteFilePath(d)) + option.List.Add(HelperFunctions.ChangePathFormat(d)); + } + } + } + + private static void AddLibraries(Project project, ProFileOption option, string paths, string deps) + { + if (QtProject.GetFormatVersion(project) < Resources.qtMinFormatVersion_ClProperties) + return; + + var versionManager = QtVersionManager.The(); + var qtDir = versionManager.GetInstallPath(project); + if (qtDir == null) + qtDir = Environment.GetEnvironmentVariable("QTDIR"); + if (qtDir == null) + qtDir = ""; + + qtDir = HelperFunctions.NormalizeRelativeFilePath(qtDir); + + if (paths != null) { + foreach (var s in paths.Split(';', ',')) { + var d = HelperFunctions.NormalizeRelativeFilePath(s); + if (!d.StartsWith("$(qtdir)\\lib", StringComparison.OrdinalIgnoreCase) && + !d.StartsWith(qtDir + "\\lib", StringComparison.OrdinalIgnoreCase)) { + if (HelperFunctions.IsAbsoluteFilePath(d)) + d = HelperFunctions.GetRelativePath(project.FullName, d); + if (!HelperFunctions.IsAbsoluteFilePath(d)) + option.List.Add("-L\"" + HelperFunctions.ChangePathFormat(d) + "\""); + } + } + } + + if (deps != null) { + foreach (var d in deps.Split(' ')) { + if (d.Length > 0 && + !d.StartsWith("$(qtdir)\\lib", StringComparison.OrdinalIgnoreCase) && + !d.StartsWith(qtDir + "\\lib", StringComparison.OrdinalIgnoreCase) && + !d.StartsWith("qt", StringComparison.OrdinalIgnoreCase) && + !d.StartsWith(".\\qt", StringComparison.OrdinalIgnoreCase) && d != ".") + option.List.Add("-l" + HelperFunctions.ChangePathFormat(d).Replace(".lib", "")); + } + } + } + + private static void AddModules(QtProject qtPrj, ProFileOption optionQT, ProFileOption optionCONFIG) + { + foreach (var module in QtModules.Instance.GetAvailableModules()) { + if (!qtPrj.HasModule(module.Id)) + continue; + + if (module.proVarQT != null) + optionQT.List.Add(module.proVarQT); + if (module.proVarCONFIG != null) + optionCONFIG.List.Add(module.proVarCONFIG); + } + } + + private void WriteProSolution(ProSolution prosln, bool openFile) + { + var sln = prosln.ProjectSolution; + if (string.IsNullOrEmpty(sln.FileName)) + return; + + var fi = new FileInfo(sln.FullName); + var slnDir = fi.Directory; + var createSlnFile = false; + + if ((slnDir != null) && (prosln.ProFiles.Count > 1)) { + if (MessageBox.Show(SR.GetString("ExportProject_SolutionProFileBuildIn", slnDir.FullName), + SR.GetString("ExportSolution"), MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + createSlnFile = true; + } + + if (createSlnFile) { + StreamWriter sw; + var slnName = HelperFunctions.RemoveFileNameExtension(fi); + var slnFileName = slnDir.FullName + "\\" + slnName + ".pro"; + + if (File.Exists(slnFileName)) { + if (MessageBox.Show(SR.GetString("ExportProject_ExistsOverwriteQuestion", slnFileName), + SR.GetString("ExportSolution"), MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) + return; + } + + try { + sw = new StreamWriter(File.Create(slnFileName)); + } catch (Exception e) { + Messages.DisplayErrorMessage(e); + return; + } + + var content = new ProFileContent(null); + + var option = new ProFileOption("TEMPLATE"); + option.NewOption = null; // just one option... + option.AssignSymbol = ProFileOption.AssignType.AT_Equals; + content.Options.Add(option); + option.List.Add("subdirs"); + + option = new ProFileOption("SUBDIRS"); + option.ShortComment = "#Projects"; + content.Options.Add(option); + + string proFullName, relativePath; + char[] trimChars = { '\\' }; + foreach (var profile in prosln.ProFiles) { + var fiProject = new FileInfo(profile.Project.ProjectFile); + var projectBaseName = HelperFunctions.RemoveFileNameExtension(fiProject); + proFullName = profile.Project.ProjectDirectory + projectBaseName + ".pro"; + relativePath = HelperFunctions.GetRelativePath(slnDir.FullName, proFullName); + relativePath = relativePath.TrimEnd(trimChars); + relativePath = HelperFunctions.ChangePathFormat(relativePath.Remove(0, 2)); + option.List.Add(relativePath); + } + + using (sw) { + sw.WriteLine(Resources.exportSolutionHeader); + for (var i = 0; i < content.Options.Count; i++) + WriteProFileOption(sw, content.Options[i]); + } + + if (openFile) + dteObject.OpenFile(Constants.vsViewKindTextView, slnFileName).Activate(); + } + } + + private void WriteProFile(ProFileContent content, string proFile, string priFileToInclude, bool openFile) + { + StreamWriter sw; + if (File.Exists(proFile)) { + if (MessageBox.Show(SR.GetString("ExportProject_ExistsOverwriteQuestion", proFile), + SR.GetString("ExportSolution"), MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { + return; + } + } + + try { + sw = new StreamWriter(File.Create(proFile)); + } catch (Exception e) { + Messages.DisplayErrorMessage(e); + return; + } + + if (!string.IsNullOrEmpty(priFileToInclude)) { + foreach (var option in content.Options) { + if (option.Name == "include" && !option.List.Contains(priFileToInclude)) { + var relativePriPath = HelperFunctions.GetRelativePath(Path.GetDirectoryName(proFile), priFileToInclude); + if (relativePriPath.StartsWith(".\\", StringComparison.Ordinal)) + relativePriPath = relativePriPath.Substring(2); + relativePriPath = HelperFunctions.ChangePathFormat(relativePriPath); + option.List.Add(relativePriPath); + break; + } + } + } + using (sw) { + sw.WriteLine(Resources.exportPriHeader); + WriteProFileOptions(sw, content.Options); + } + + // open the file in vs + if (openFile) + dteObject.OpenFile(Constants.vsViewKindTextView, proFile).Activate(); + } + + private void WritePriFile(ProFileContent content, string priFile) + { + StreamWriter sw; + + try { + sw = new StreamWriter(File.Create(priFile)); + } catch (Exception e) { + Messages.DisplayErrorMessage(e); + return; + } + + using (sw) { + sw.WriteLine(Resources.exportProHeader); + WriteProFileOptions(sw, content.Options); + } + } + + private static void WriteProFileOptions(StreamWriter sw, List<ProFileOption> options) + { + foreach (var option in options) + WriteProFileOption(sw, option); + } + + private static void WriteProFileOption(StreamWriter sw, ProFileOption option) + { + if (option.List.Count <= 0) + return; + + if (option.IncludeComment) + sw.WriteLine(sw.NewLine + "#" + option.ShortComment); + + if (option.AssignSymbol != ProFileOption.AssignType.AT_Function) { + sw.Write(option.Name); + + switch (option.AssignSymbol) { + case ProFileOption.AssignType.AT_Equals: + sw.Write(" = "); + break; + case ProFileOption.AssignType.AT_MinusEquals: + sw.Write(" -= "); + break; + case ProFileOption.AssignType.AT_PlusEquals: + sw.Write(" += "); + break; + } + + for (var i = 0; i < option.List.Count - 1; i++) + sw.Write(option.List[i] + option.NewOption); + sw.Write(option.List[option.List.Count - 1] + sw.NewLine); + } else { + for (var i = 0; i < option.List.Count; i++) + sw.WriteLine(option.Name + "(" + option.List[i] + ")"); + } + } + + private static VCFilter BestMatch(string path, Hashtable pathFilterTable) + { + var bestMatch = "."; + var inPath = path; + if (inPath.StartsWith(".\\", StringComparison.Ordinal)) + inPath = inPath.Substring(2); + foreach (string p in pathFilterTable.Keys) { + var best = 0; + for (var i = 0; i < inPath.Length; ++i) { + if (i < p.Length && inPath[i] == p[i]) + ++best; + else + break; + } + if (best > bestMatch.Length && p.Length == best) + bestMatch = p; + } + return pathFilterTable[bestMatch] as VCFilter; + } + + private static void CollectFilters(VCFilter filter, string path, ref Hashtable filterPathTable, + ref Hashtable pathFilterTable) + { + var newPath = "."; + if (path != null) + newPath = path + "\\" + filter.Name; + newPath = newPath.ToLower().Trim(); + newPath = Regex.Replace(newPath, @"\\+\.?\\+", "\\"); + newPath = Regex.Replace(newPath, @"\\\.?$", ""); + if (newPath.StartsWith(".\\", StringComparison.Ordinal)) + newPath = newPath.Substring(2); + filterPathTable.Add(filter, newPath); + pathFilterTable.Add(newPath, filter); + foreach (VCFilter f in (IVCCollection)filter.Filters) + CollectFilters(f, newPath, ref filterPathTable, ref pathFilterTable); + } + + public static void SyncIncludeFiles(VCProject vcproj, List<string> priFiles, + List<string> projFiles, DTE dte, bool flat, FakeFilter fakeFilter) + { + var cmpPriFiles = new List<string>(priFiles.Count); + foreach (var s in priFiles) + cmpPriFiles.Add(HelperFunctions.NormalizeFilePath(s).ToLower()); + cmpPriFiles.Sort(); + + var cmpProjFiles = new List<string>(projFiles.Count); + foreach (var s in projFiles) + cmpProjFiles.Add(HelperFunctions.NormalizeFilePath(s).ToLower()); + + var qtPro = QtProject.Create(vcproj); + var filterPathTable = new Hashtable(17); + var pathFilterTable = new Hashtable(17); + if (!flat && fakeFilter != null) { + var rootFilter = qtPro.FindFilterFromGuid(fakeFilter.UniqueIdentifier); + if (rootFilter == null) + qtPro.AddFilterToProject(Filters.SourceFiles()); + + CollectFilters(rootFilter, null, ref filterPathTable, ref pathFilterTable); + } + + // first check for new files + foreach (var file in cmpPriFiles) { + if (cmpProjFiles.IndexOf(file) > -1) + continue; + + if (flat) { + vcproj.AddFile(file); // the file is not in the project + } else { + var path = HelperFunctions.GetRelativePath(vcproj.ProjectDirectory, file); + if (path.StartsWith(".\\", StringComparison.Ordinal)) + path = path.Substring(2); + + var i = path.LastIndexOf('\\'); + if (i > -1) + path = path.Substring(0, i); + else + path = "."; + + if (pathFilterTable.Contains(path)) { + var f = pathFilterTable[path] as VCFilter; + f.AddFile(file); + continue; + } + + var filter = BestMatch(path, pathFilterTable); + + var filterDir = filterPathTable[filter] as string; + var name = path; + if (!name.StartsWith("..", StringComparison.Ordinal) && name.StartsWith(filterDir, StringComparison.Ordinal)) + name = name.Substring(filterDir.Length + 1); + + var newFilter = filter.AddFilter(name) as VCFilter; + newFilter.AddFile(file); + + filterPathTable.Add(newFilter, path); + pathFilterTable.Add(path, newFilter); + } + } + + // then check for deleted files + foreach (var file in cmpProjFiles) { + if (cmpPriFiles.IndexOf(file) == -1) { + // the file is not in the pri file + // (only removes it from the project, does not del. the file) + var info = new FileInfo(file); + HelperFunctions.RemoveFileInProject(vcproj, file); + Messages.Print("--- (Importing .pri file) file: " + info.Name + + " does not exist in .pri file, move to " + vcproj.ProjectDirectory + "Deleted"); + } + } + } + + public void ExportToProFile() + { + var expDlg = new ExportProjectDialog(); + + var sln = dteObject.Solution; + var prosln = CreateProFileSolution(sln); + + if (prosln.ProFiles.Count <= 0) { + Messages.DisplayWarningMessage(SR.GetString("ExportProject_NoProjectsToExport")); + return; + } + + expDlg.ProFileSolution = prosln; + expDlg.StartPosition = FormStartPosition.CenterParent; + var ww = new MainWinWrapper(dteObject); + if (expDlg.ShowDialog(ww) == DialogResult.OK) { + WriteProSolution(prosln, expDlg.OpenFiles); + + // create all the project .pro files + foreach (var profile in prosln.ProFiles) { + if (profile.Export) { + var project = HelperFunctions.VCProjectToProject(profile.Project); + string priFile = null; + if (expDlg.CreatePriFile) + priFile = ExportToPriFile(project); + else { + var priContent = CreatePriFileContent(project, profile.Project.ProjectDirectory); + profile.Options.AddRange(priContent.Options); + } + WriteProFile(profile, profile.Project.ProjectDirectory + profile.Project.Name + ".pro", priFile, expDlg.OpenFiles); + } + } + } + } + + public string ExportToPriFile(Project proj) + { + VCProject vcproj; + + if (HelperFunctions.IsQtProject(proj)) { + try { + vcproj = (VCProject)proj.Object; + } catch (Exception e) { + Messages.DisplayErrorMessage(e); + return null; + } + + // make the user able to choose .pri file + var fd = new SaveFileDialog(); + fd.OverwritePrompt = true; + fd.CheckPathExists = true; + fd.Title = SR.GetString("ExportProject_ExportPriFile"); + fd.Filter = "Project Include Files (*.pri)|*.pri"; + fd.InitialDirectory = vcproj.ProjectDirectory; + fd.FileName = vcproj.Name + ".pri"; + + if (fd.ShowDialog() != DialogResult.OK) + return null; + + ExportToPriFile(proj, fd.FileName); + return fd.FileName; + } + return null; + } + + public void ExportToPriFile(Project proj, string fileName) + { + var priFile = new FileInfo(fileName); + + var content = CreatePriFileContent(proj, priFile.DirectoryName); + WritePriFile(content, priFile.FullName); + } + } +} diff --git a/QtVsTools.Core/ProjectImporter.cs b/QtVsTools.Core/ProjectImporter.cs new file mode 100644 index 0000000..bd5da86 --- /dev/null +++ b/QtVsTools.Core/ProjectImporter.cs @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.VCProjectEngine; +using System; +using System.Collections.Generic; +using System.IO; +using System.Windows.Forms; + +namespace QtVsTools.Core +{ + public class ProjectImporter + { + private DTE dteObject; + const string projectFileExtension = ".vcxproj"; + + public ProjectImporter(DTE dte) + { + dteObject = dte; + } + + public void ImportProFile(string qtVersion) + { + FileDialog toOpen = new OpenFileDialog(); + toOpen.FilterIndex = 1; + toOpen.CheckFileExists = true; + toOpen.Title = SR.GetString("ExportProject_SelectQtProjectToAdd"); + toOpen.Filter = "Qt Project files (*.pro)|*.pro|All files (*.*)|*.*"; + + if (DialogResult.OK != toOpen.ShowDialog()) + return; + + var mainInfo = new FileInfo(toOpen.FileName); + if (HelperFunctions.IsSubDirsFile(mainInfo.FullName)) { + // we use the safe way. Make the user close the existing solution manually + if ((!string.IsNullOrEmpty(dteObject.Solution.FullName)) + || (HelperFunctions.ProjectsInSolution(dteObject).Count > 0)) { + if (MessageBox.Show(SR.GetString("ExportProject_SubdirsProfileSolutionClose"), + SR.GetString("OpenSolution"), MessageBoxButtons.OKCancel, MessageBoxIcon.Question) + == DialogResult.OK) { + dteObject.Solution.Close(true); + } else { + return; + } + } + + ImportSolution(mainInfo, qtVersion); + } else { + ImportProject(mainInfo, qtVersion); + } + } + + private void ImportSolution(FileInfo mainInfo, string qtVersion) + { + var versionInfo = QtVersionManager.The().GetVersionInfo(qtVersion); + var VCInfo = RunQmake(mainInfo, ".sln", true, versionInfo); + if (null == VCInfo) + return; + ImportQMakeSolution(VCInfo, versionInfo); + + try { + if (CheckQtVersion(versionInfo)) { + dteObject.Solution.Open(VCInfo.FullName); + if (qtVersion != null) { + QtVersionManager.The().SaveSolutionQtVersion(dteObject.Solution, qtVersion); + foreach (var prj in HelperFunctions.ProjectsInSolution(dteObject)) { + QtVersionManager.The().SaveProjectQtVersion(prj, qtVersion); + var qtPro = QtProject.Create(prj); + qtPro.SetQtEnvironment(); + ApplyPostImportSteps(qtPro); + } + } + } + + Messages.Print("--- (Import): Finished opening " + VCInfo.Name); + } catch (Exception e) { + Messages.DisplayCriticalErrorMessage(e); + } + } + + public void ImportProject(FileInfo mainInfo, string qtVersion) + { + var versionInfo = QtVersionManager.The().GetVersionInfo(qtVersion); + var VCInfo = RunQmake(mainInfo, projectFileExtension, false, versionInfo); + if (null == VCInfo) + return; + + ImportQMakeProject(VCInfo, versionInfo); + + try { + if (CheckQtVersion(versionInfo)) { + // no need to add the project again if it's already there... + if (!HelperFunctions.IsProjectInSolution(dteObject, VCInfo.FullName)) { + try { + dteObject.Solution.AddFromFile(VCInfo.FullName, false); + } catch (Exception /*exception*/) { + Messages.Print("--- (Import): Generated project could not be loaded."); + Messages.Print("--- (Import): Please look in the output above for errors and warnings."); + return; + } + Messages.Print("--- (Import): Added " + VCInfo.Name + " to Solution"); + } else { + Messages.Print("Project already in Solution"); + } + + Project pro = null; + foreach (var p in HelperFunctions.ProjectsInSolution(dteObject)) { + if (p.FullName.ToLower() == VCInfo.FullName.ToLower()) { + pro = p; + break; + } + } + if (pro != null) { + var qtPro = QtProject.Create(pro); + qtPro.SetQtEnvironment(); + var platformName = versionInfo.GetVSPlatformName(); + + if (qtVersion != null) + QtVersionManager.The().SaveProjectQtVersion(pro, qtVersion, platformName); + + if (!qtPro.SelectSolutionPlatform(platformName) || !qtPro.HasPlatform(platformName)) { + var newProject = false; + qtPro.CreatePlatform("Win32", platformName, null, versionInfo, ref newProject); + if (!qtPro.SelectSolutionPlatform(platformName)) + Messages.Print("Can't select the platform " + platformName + "."); + } + + // try to figure out if the project is a plugin project + try { + var activeConfig = pro.ConfigurationManager.ActiveConfiguration.ConfigurationName; + var config = (VCConfiguration)((IVCCollection)qtPro.VCProject.Configurations).Item(activeConfig); + if (config.ConfigurationType == ConfigurationTypes.typeDynamicLibrary) { + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + if (compiler.GetPreprocessorDefinitions().IndexOf("QT_PLUGIN", StringComparison.Ordinal) > -1 + && compiler.GetPreprocessorDefinitions().IndexOf("QDESIGNER_EXPORT_WIDGETS", StringComparison.Ordinal) > -1 + && compiler.GetAdditionalIncludeDirectories().IndexOf("QtDesigner", StringComparison.Ordinal) > -1 + && linker.AdditionalDependencies.IndexOf("QtDesigner", StringComparison.Ordinal) > -1) { + qtPro.MarkAsDesignerPluginProject(); + } + } + } catch (Exception) { } + + qtPro.SetQtEnvironment(); + ApplyPostImportSteps(qtPro); + } + } + } catch (Exception e) { + Messages.DisplayCriticalErrorMessage(SR.GetString("ExportProject_ProjectOrSolutionCorrupt", e.ToString())); + } + } + + private void ImportQMakeSolution(FileInfo solutionFile, VersionInformation vi) + { + var projects = ParseProjectsFromSolution(solutionFile); + foreach (var project in projects) { + var projectInfo = new FileInfo(project); + ImportQMakeProject(projectInfo, vi); + } + } + + private static List<string> ParseProjectsFromSolution(FileInfo solutionFile) + { + var sr = solutionFile.OpenText(); + var content = sr.ReadToEnd(); + sr.Close(); + + var projects = new List<string>(); + var index = content.IndexOf(projectFileExtension, StringComparison.Ordinal); + while (index != -1) { + var startIndex = content.LastIndexOf('\"', index, index) + 1; + var endIndex = content.IndexOf('\"', index); + projects.Add(content.Substring(startIndex, endIndex - startIndex)); + content = content.Substring(endIndex); + index = content.IndexOf(projectFileExtension, StringComparison.Ordinal); + } + return projects; + } + + private void ImportQMakeProject(FileInfo projectFile, VersionInformation vi) + { + var xmlProject = MsBuildProject.Load(projectFile.FullName); + xmlProject.ReplacePath(vi.qtDir, "$(QTDIR)"); + xmlProject.ReplacePath(projectFile.DirectoryName, "."); + + bool ok = xmlProject.AddQtMsBuildReferences(); + if (ok) + ok = xmlProject.ConvertCustomBuildToQtMsBuild(); + if (ok) + ok = xmlProject.EnableMultiProcessorCompilation(); + if (ok) { + string versionWin10SDK = HelperFunctions.GetWindows10SDKVersion(); + if (!string.IsNullOrEmpty(versionWin10SDK)) + ok = xmlProject.SetDefaultWindowsSDKVersion(versionWin10SDK); + } + if (ok) + ok = xmlProject.UpdateProjectFormatVersion(); + + if (!ok) { + Messages.Print( + SR.GetString("ImportProject_CannotConvertProject", projectFile.Name)); + } + xmlProject.Save(); + + // Initialize Qt variables + xmlProject.BuildTarget("QtVarsDesignTime"); + } + + private static void ApplyPostImportSteps(QtProject qtProject) + { + qtProject.RemoveResFilesFromGeneratedFilesFilter(); + qtProject.TranslateFilterNames(); + + // collapse the generated files/resources filters afterwards + qtProject.CollapseFilter(Filters.ResourceFiles().Name); + qtProject.CollapseFilter(Filters.GeneratedFiles().Name); + + try { + // save the project after modification + qtProject.Project.Save(null); + } catch { /* ignore */ } + } + + private FileInfo RunQmake(FileInfo mainInfo, string ext, bool recursive, VersionInformation vi) + { + var name = mainInfo.Name.Remove(mainInfo.Name.IndexOf('.')); + + var VCInfo = new FileInfo(mainInfo.DirectoryName + "\\" + name + ext); + + if (!VCInfo.Exists || DialogResult.Yes == MessageBox.Show(SR.GetString("ExportProject_ProjectExistsRegenerateOrReuse", VCInfo.Name), + SR.GetString("ProjectExists"), MessageBoxButtons.YesNo, MessageBoxIcon.Question)) { + Messages.Print("--- (Import): Generating new project of " + mainInfo.Name + " file"); + + var waitDialog = WaitDialog.Start( + "Open Qt Project File", + "Generating Visual Studio project...", delay: 2); + + var qmake = new QMakeImport(vi, mainInfo.FullName, recursive, dteObject); + int exitCode = qmake.Run(setVCVars: true); + waitDialog.Stop(); + + if (exitCode == 0) + return VCInfo; + } + + return null; + } + + private static bool CheckQtVersion(VersionInformation vi) + { + if (!vi.qt5Version) { + Messages.DisplayWarningMessage(SR.GetString("ExportProject_EditProjectFileManually")); + return false; + } + return true; + } + + } +} diff --git a/QtVsTools.Core/ProjectMacros.cs b/QtVsTools.Core/ProjectMacros.cs new file mode 100644 index 0000000..3e99c4d --- /dev/null +++ b/QtVsTools.Core/ProjectMacros.cs @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + public static class ProjectMacros + { + public const string Name = "%(Filename)"; + public const string FileName = "%(Identity)"; + public const string Path = "%(FullPath)"; + } +} diff --git a/QtVsTools.Core/Properties/AssemblyInfo.cs b/QtVsTools.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..901ee25 --- /dev/null +++ b/QtVsTools.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Qt Project Lib")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3d0ccde7-855f-431a-b832-f4f457171ff3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/QtVsTools.Core/QMake.cs b/QtVsTools.Core/QMake.cs new file mode 100644 index 0000000..4c0a985 --- /dev/null +++ b/QtVsTools.Core/QMake.cs @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using QtVsTools.VisualStudio; + +namespace QtVsTools.Core +{ + public abstract class QMake + { + public Dictionary<string, string> Vars { get; protected set; } + public string OutputFile { get; protected set; } + public uint DebugLevel { get; protected set; } + public string TemplatePrefix { get; protected set; } + public bool Recursive { get; protected set; } + public string ProFile { get; protected set; } + public string Query { get; protected set; } + public bool DisableWarnings { get; set; } + + protected VersionInformation QtVersion { get; private set; } + protected EnvDTE.DTE Dte { get; private set; } + + public QMake(VersionInformation qtVersion, EnvDTE.DTE dte = null) + { + Debug.Assert(qtVersion != null); + QtVersion = qtVersion; + Dte = dte ?? VsServiceProvider.GetService<EnvDTE.DTE>(); + } + + protected virtual string QMakeExe + { + get + { + var qmakePath = Path.Combine(QtVersion.qtDir, "bin", "qmake.exe"); + if (!File.Exists(qmakePath)) + qmakePath = Path.Combine(QtVersion.qtDir, "qmake.exe"); + return qmakePath; + } + } + + protected virtual string WorkingDirectory + { + get + { + return Path.GetDirectoryName(ProFile); + } + } + + string MakeRelative(string absolutePath) + { + var workDir = new Uri(Path.GetDirectoryName(ProFile) + Path.DirectorySeparatorChar); + var path = new Uri(absolutePath); + if (workDir.IsBaseOf(path)) { + return workDir.MakeRelativeUri(path).OriginalString + .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + } else { + return absolutePath; + } + } + + protected virtual string QMakeArgs + { + get + { + var args = new StringBuilder(); + if (Vars != null) { + foreach (KeyValuePair<string, string> v in Vars) { + args.AppendFormat(" {0}={1}", v.Key, v.Value); + } + } + + if (!string.IsNullOrEmpty(OutputFile)) + args.AppendFormat(" -o \"{0}\"", MakeRelative(OutputFile)); + + for (int i = 0; i < DebugLevel; ++i) + args.Append(" -d"); + + if (!string.IsNullOrEmpty(TemplatePrefix)) + args.AppendFormat(" -tp {0}", TemplatePrefix); + + if (Recursive) + args.Append(" -recursive"); + + if (DisableWarnings) + args.Append(" -Wnone"); + + if (!string.IsNullOrEmpty(ProFile)) + args.AppendFormat(" \"{0}\"", MakeRelative(ProFile)); + + if (!string.IsNullOrEmpty(Query)) + args.AppendFormat(" -query {0}", Query); + + return args.ToString(); + } + } + + protected virtual Process CreateProcess() + { + var qmakeStartInfo = new ProcessStartInfo + { + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + FileName = QMakeExe, + Arguments = QMakeArgs, + WorkingDirectory = WorkingDirectory, + }; + qmakeStartInfo.EnvironmentVariables["QTDIR"] = QtVersion.qtDir; + + var qmakeProc = new Process + { + StartInfo = qmakeStartInfo, + }; + qmakeProc.OutputDataReceived += (sender, ev) => OutMsg(ev.Data); + qmakeProc.ErrorDataReceived += (sender, ev) => ErrMsg(ev.Data); + + return qmakeProc; + } + + protected virtual void OutMsg(string msg) + { + if (Dte != null && !string.IsNullOrEmpty(msg)) + Messages.Print(msg); + } + + protected virtual void ErrMsg(string msg) + { + if (Dte != null && !string.IsNullOrEmpty(msg)) + Messages.Print(msg); + } + + protected virtual void InfoMsg(string msg) + { + if (Dte != null && !string.IsNullOrEmpty(msg)) + Messages.Print(msg); + } + + protected virtual void InfoStart(Process qmakeProc) + { + InfoMsg(string.Format("--- qmake({0}): started {1}", + qmakeProc.Id, qmakeProc.StartInfo.FileName)); + } + + protected virtual void InfoExit(Process qmakeProc) + { + InfoMsg(string.Format("--- qmake({0}): exit code {1} ({2:0.##} msecs)\r\n", + qmakeProc.Id, qmakeProc.ExitCode, + (qmakeProc.ExitTime - qmakeProc.StartTime).TotalMilliseconds)); + } + + public virtual int Run(bool setVCVars = false) + { + int exitCode = -1; + using (var qmakeProc = CreateProcess()) { + try { + if (setVCVars + && !HelperFunctions.SetVCVars(QtVersion, qmakeProc.StartInfo)) { + OutMsg("Error setting VC vars"); + } + if (qmakeProc.Start()) { + InfoStart(qmakeProc); + qmakeProc.BeginOutputReadLine(); + qmakeProc.BeginErrorReadLine(); + qmakeProc.WaitForExit(); + exitCode = qmakeProc.ExitCode; + InfoExit(qmakeProc); + } + } catch (Exception e) { + ErrMsg(string.Format("Exception \"{0}\":\r\n{1}", + e.Message, + e.StackTrace)); + } + } + return exitCode; + } + } + + public class QMakeImport : QMake + { + public QMakeImport( + VersionInformation qtVersion, + string proFilePath, + bool recursiveRun = false, + EnvDTE.DTE dte = null) + : base(qtVersion, dte) + { + ProFile = proFilePath; + TemplatePrefix = "vc"; + if (recursiveRun) + Recursive = true; + else + OutputFile = Path.ChangeExtension(proFilePath, ".vcxproj"); + Vars = new Dictionary<string, string> + { + { "QMAKE_INCDIR_QT", @"$(QTDIR)\include" }, + { "QMAKE_LIBDIR", @"$(QTDIR)\lib" }, + { "QMAKE_MOC", @"$(QTDIR)\bin\moc.exe" }, + { "QMAKE_QMAKE", @"$(QTDIR)\bin\qmake.exe" }, + }; + } + + protected override void InfoStart(Process qmakeProc) + { + base.InfoStart(qmakeProc); + InfoMsg("--- qmake: Working Directory: " + qmakeProc.StartInfo.WorkingDirectory); + InfoMsg("--- qmake: Arguments: " + qmakeProc.StartInfo.Arguments); + if (qmakeProc.StartInfo.EnvironmentVariables.ContainsKey("QMAKESPEC")) { + var qmakeSpec = qmakeProc.StartInfo.EnvironmentVariables["QMAKESPEC"]; + if (qmakeSpec != QtVersion.QMakeSpecDirectory) { + InfoMsg("--- qmake: Environment " + + "variable QMAKESPEC overwriting Qt version QMAKESPEC."); + InfoMsg("--- qmake: Qt version " + + "QMAKESPEC: " + QtVersion.QMakeSpecDirectory); + InfoMsg("--- qmake: Environment " + + "variable QMAKESPEC: " + qmakeSpec); + } + } + } + } +} diff --git a/QtVsTools.Core/QMakeConf.cs b/QtVsTools.Core/QMakeConf.cs new file mode 100644 index 0000000..c66a6c6 --- /dev/null +++ b/QtVsTools.Core/QMakeConf.cs @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections; +using System.IO; + +namespace QtVsTools.Core +{ + public class QMakeConf + { + public Hashtable Entries { get; private set; } + public string QMakeSpecDirectory { get; private set; } + + public QMakeConf(VersionInformation versionInfo, QMakeQuery qmakeQuery = null) + { + Entries = new Hashtable(); + QMakeSpecDirectory = Path.Combine(versionInfo.qtDir, "mkspecs", "default"); + var qmakeConf = Path.Combine(QMakeSpecDirectory, "qmake.conf"); + + // Starting from Qt5 beta2 there is no more "\\mkspecs\\default" folder available + // To find location of "qmake.conf" there is a need to run "qmake -query" command + // This is what happens below. + if (!File.Exists(qmakeConf)) { + if (qmakeQuery == null) + qmakeQuery = new QMakeQuery(versionInfo); + + string qtPrefix = qmakeQuery["QT_INSTALL_PREFIX"]; + if (string.IsNullOrEmpty(qtPrefix)) + throw new QtVSException("qmake error: no value for QT_INSTALL_PREFIX"); + + string qtArchData = qmakeQuery["QT_INSTALL_ARCHDATA"]; + if (string.IsNullOrEmpty(qtArchData)) + throw new QtVSException("qmake error: no value for QT_INSTALL_ARCHDATA"); + + string qmakeXSpec = qmakeQuery["QMAKE_XSPEC"]; + if (string.IsNullOrEmpty(qtArchData)) + throw new QtVSException("qmake error: no value for QMAKE_XSPEC"); + + qmakeConf = Path.Combine(qtPrefix, qtArchData, "mkspecs", qmakeXSpec, "qmake.conf"); + + if (!File.Exists(qmakeConf)) + throw new QtVSException("qmake.conf expected at " + qmakeConf + " not found"); + } + + ParseFile(qmakeConf); + } + + private void ParseFile(string filename) + { + var fileInfo = new FileInfo(filename); + if (fileInfo.Exists) { + var streamReader = new StreamReader(filename); + var line = streamReader.ReadLine(); + while (line != null) { + line = line.Trim(); + var commentStartIndex = line.IndexOf('#'); + if (commentStartIndex >= 0) + line = line.Remove(commentStartIndex); + var pos = line.IndexOf('='); + if (pos > 0) { + var op = "="; + if (line[pos - 1] == '+' || line[pos - 1] == '-') + op = line[pos - 1] + "="; + + var lineKey = line.Substring(0, pos - op.Length + 1).Trim(); + var lineValue = ExpandVariables(line.Substring(pos + 1).Trim()); + + if (op == "+=") { + Entries[lineKey] += " " + lineValue; + } else if (op == "-=") { + foreach (var remval in lineValue.Split(' ', '\t')) + RemoveValue(lineKey, remval); + } else { + Entries[lineKey] = lineValue; + } + } else if (line.StartsWith("include", StringComparison.Ordinal)) { + pos = line.IndexOf('('); + var posEnd = line.LastIndexOf(')'); + if (pos > 0 && pos < posEnd) { + var filenameToInclude = line.Substring(pos + 1, posEnd - pos - 1); + var saveCurrentDir = Environment.CurrentDirectory; + Environment.CurrentDirectory = fileInfo.Directory.FullName; + var fileInfoToInclude = new FileInfo(filenameToInclude); + if (fileInfoToInclude.Exists) + ParseFile(fileInfoToInclude.FullName); + Environment.CurrentDirectory = saveCurrentDir; + } + } + line = streamReader.ReadLine(); + } + streamReader.Close(); + + RemoveValue("QMAKE_LFLAGS_CONSOLE", "@QMAKE_SUBSYSTEM_SUFFIX@"); + RemoveValue("QMAKE_LFLAGS_WINDOWS", "@QMAKE_SUBSYSTEM_SUFFIX@"); + } + } + + private string ExpandVariables(string value) + { + var pos = value.IndexOf("$$", StringComparison.Ordinal); + while (pos != -1) { + var startPos = pos + 2; + var endPos = startPos; + + if (value[startPos] != '[') { // at the moment no handling of qmake internal variables + for (; endPos < value.Length; ++endPos) { + if ((Char.IsPunctuation(value[endPos]) && value[endPos] != '_') + || Char.IsWhiteSpace(value[endPos])) { + break; + } + } + if (endPos > startPos) { + var varName = value.Substring(startPos, endPos - startPos); + var varValue = (Entries[varName] ?? string.Empty).ToString(); + value = value.Substring(0, pos) + varValue + value.Substring(endPos); + endPos = pos + varValue.Length; + } + } + + pos = value.IndexOf("$$", endPos, StringComparison.Ordinal); + } + return value; + } + + private void RemoveValue(string key, string valueToRemove) + { + var pos = -1; + if (!Entries.Contains(key)) + return; + + var value = Entries[key].ToString(); + do { + pos = value.IndexOf(valueToRemove, StringComparison.Ordinal); + if (pos >= 0) + value = value.Remove(pos, valueToRemove.Length); + } while (pos >= 0); + Entries[key] = value; + } + } +} diff --git a/QtVsTools.Core/QMakeQuery.cs b/QtVsTools.Core/QMakeQuery.cs new file mode 100644 index 0000000..2dc1be5 --- /dev/null +++ b/QtVsTools.Core/QMakeQuery.cs @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using QtVsTools.SyntaxAnalysis; + +namespace QtVsTools.Core +{ + using static RegExpr; + + public class QMakeQuery : QMake + { + public QMakeQuery(VersionInformation vi) : base(vi) + { } + + StringBuilder stdOutput; + protected override void OutMsg(string msg) + { + stdOutput.AppendLine(msg); + } + + protected override void InfoStart(Process qmakeProc) + { + base.InfoStart(qmakeProc); + InfoMsg("--- qmake: Querying persistent properties"); + } + + public Dictionary<string, string> QueryAllValues() + { + string result = string.Empty; + stdOutput = new StringBuilder(); + Query = " "; + + if (Run() == 0 && stdOutput.Length > 0) { + return PropertyParser + .Parse(stdOutput.ToString()) + .GetValues<KeyValuePair<string, string>>("PROP") + .GroupBy(x => x.Key) + .Select(x => new { x.Key, Value = x.Last().Value }) + .ToDictionary(property => property.Key, property => property.Value); + } else { + return new Dictionary<string, string>(); + } + } + + public string this[string name] + { + get + { + string value = string.Empty; + if (Properties.TryGetValue(name, out value)) + return value; + else + return null; + } + } + + Dictionary<string, string> _Properties; + Dictionary<string, string> Properties => _Properties ?? (_Properties = QueryAllValues()); + + Parser _PropertyParser; + Parser PropertyParser + { + get + { + if (_PropertyParser != null) + return _PropertyParser; + + var charSeparator = Char[':']; + var charsName = CharSet[~(charSeparator + CharVertSpace)]; + var charsValue = CharSet[~CharVertSpace]; + + var propertyName = new Token("NAME", charsName.Repeat(atLeast: 1)); + var propertyValue = new Token("VALUE", charsValue.Repeat()); + var property = new Token("PROP", propertyName & charSeparator & propertyValue) + { + new Rule<KeyValuePair<string, string>> + { + Create("NAME", (string name) + => new KeyValuePair<string, string>(name, string.Empty)), + + Transform("VALUE", (KeyValuePair<string, string> prop, string value) + => new KeyValuePair<string, string>(prop.Key, value)) + } + }; + var propertyLine = StartOfLine & property & CharVertSpace.Repeat(); + return _PropertyParser = propertyLine.Repeat().Render(); + } + } + } +} diff --git a/QtVsTools.Core/QrcItem.cs b/QtVsTools.Core/QrcItem.cs new file mode 100644 index 0000000..97717c3 --- /dev/null +++ b/QtVsTools.Core/QrcItem.cs @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + public class QrcItem + { + public string Path { get; set; } + public string Alias { get; set; } + } +} diff --git a/QtVsTools.Core/QrcParser.cs b/QtVsTools.Core/QrcParser.cs new file mode 100644 index 0000000..5930dff --- /dev/null +++ b/QtVsTools.Core/QrcParser.cs @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Xml; + +namespace QtVsTools.Core +{ + public class QrcParser + { + private readonly string qrcFileName; + private readonly Stack<QrcPrefix> prefixes; + private readonly List<QrcPrefix> prefxs; + + public List<QrcPrefix> Prefixes + { + get { return prefxs; } + } + + public QrcParser(string fileName) + { + qrcFileName = fileName; + prefixes = new Stack<QrcPrefix>(); + prefxs = new List<QrcPrefix>(); + } + + public bool parse() + { + var fi = new System.IO.FileInfo(qrcFileName); + if (!fi.Exists) + return false; + try { + var reader = new XmlTextReader(qrcFileName); + QrcItem currentItem = null; + QrcPrefix currentPrefix = null; + while (reader.Read()) { + switch (reader.NodeType) { + case XmlNodeType.Element: + if (reader.LocalName.ToLower() == "qresource") { + currentPrefix = new QrcPrefix(); + currentPrefix.Prefix = reader.GetAttribute("prefix"); + currentPrefix.Language = reader.GetAttribute("lang"); + prefixes.Push(currentPrefix); + } else if (reader.LocalName.ToLower() == "file") { + currentItem = new QrcItem(); + currentItem.Alias = reader.GetAttribute("name"); + } + break; + case XmlNodeType.EndElement: + if (reader.LocalName.ToLower() == "qresource") { + prefxs.Add(prefixes.Pop()); + } else if (reader.LocalName.ToLower() == "file" + && prefixes.Peek() != null && currentItem != null) { + prefixes.Peek().AddQrcItem(currentItem); + currentItem = null; + } + break; + case XmlNodeType.Text: + if (currentItem != null) + currentItem.Path = reader.Value; + break; + } + } + reader.Close(); + } catch (System.Exception) { + return false; + } + return true; + } + } +} diff --git a/QtVsTools.Core/QrcPrefix.cs b/QtVsTools.Core/QrcPrefix.cs new file mode 100644 index 0000000..79686a5 --- /dev/null +++ b/QtVsTools.Core/QrcPrefix.cs @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; + +namespace QtVsTools.Core +{ + public class QrcPrefix + { + public string Prefix { get; set; } + public string Language { get; set; } + public List<QrcItem> Items { get; private set; } + + public QrcPrefix() + { + Items = new List<QrcItem>(); + } + + public void AddQrcItem(QrcItem item) + { + Items.Add(item); + } + } +} diff --git a/QtVsTools.Core/QtConfig.cs b/QtVsTools.Core/QtConfig.cs new file mode 100644 index 0000000..d3a3682 --- /dev/null +++ b/QtVsTools.Core/QtConfig.cs @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.IO; +using System.Text.RegularExpressions; + +namespace QtVsTools.Core +{ + enum BuildType + { + Unknown, + Static, + Shared + } + + /// <summary> + /// A very simple reader for the qconfig.pri file. + /// </summary> + class QtConfig + { + public BuildType BuildType { get; private set; } + + public string LibInfix { get; private set; } + + public bool Is64Bit { get; private set; } + + public string Namespace { get; private set; } + + public uint VersionMajor { get; private set; } + public uint VersionMinor { get; private set; } + public uint VersionPatch { get; private set; } + + public QtConfig(string qtdir) + { + LibInfix = string.Empty; + + var fi = new FileInfo(qtdir + "\\mkspecs\\qconfig.pri"); + if (!fi.Exists) + fi = new FileInfo(qtdir + "\\..\\mkspecs\\qconfig.pri"); + if (!fi.Exists) + return; + + var qConfig = File.ReadAllText(fi.FullName); + + var variableDef = new Regex(@"(\w+)\s*\{|(\})|([\w\.]+)\s*([\+\-]?\=)(.*)\n"); + var lastBlock = string.Empty; + bool inBlock = false; + foreach (Match match in variableDef.Matches(qConfig)) { + var block = match.Groups[1].Value; + var blockEnd = match.Groups[2].Value; + var name = match.Groups[3].Value; + var oper = match.Groups[4].Value; + var data = match.Groups[5].Value; + + if (!string.IsNullOrEmpty(block)) { + inBlock = true; + if (block == "else" && !string.IsNullOrEmpty(lastBlock)) + lastBlock = "!" + lastBlock; + else + lastBlock = block; + } else if (!string.IsNullOrEmpty(blockEnd)) { + inBlock = false; + if (lastBlock.StartsWith("!")) + lastBlock = ""; + } else if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(data) + && (!inBlock || lastBlock == "!host_build")) { + + data = data.Replace("\r", "").Trim(); + if (name == "CONFIG") { + var values = data.Split(new char[] { ' ', '\t' }, + StringSplitOptions.RemoveEmptyEntries); + foreach (var value in values) { + if (value == "static") { + BuildType = BuildType.Static; + break; + } else if (value == "shared") { + BuildType = BuildType.Shared; + break; + } + } + } else if (name == "QT_LIBINFIX") { + LibInfix = data; + } else if (name == "QT_ARCH") { + Is64Bit = (data == "x86_64"); + } else if (name == "QT_NAMESPACE") { + Namespace = data; + } else if (name == "QT_MAJOR_VERSION") { + if (uint.TryParse(data, out uint versionMajor)) + VersionMajor = versionMajor; + } else if (name == "QT_MINOR_VERSION") { + if (uint.TryParse(data, out uint versionMinor)) + VersionMinor = versionMinor; + } else if (name == "QT_PATCH_VERSION") { + if (uint.TryParse(data, out uint versionPatch)) + VersionPatch = versionPatch; + } + } + } + } + } +} diff --git a/QtVsTools.Core/QtModule.cs b/QtVsTools.Core/QtModule.cs new file mode 100644 index 0000000..812b6d5 --- /dev/null +++ b/QtVsTools.Core/QtModule.cs @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; + +namespace QtVsTools.Core +{ + public class QtModule + { + public string Name; + public bool Selectable; + public List<string> Defines = new List<string>(); + public string LibraryPrefix = string.Empty; + public bool HasDLL = true; + public List<string> AdditionalLibraries = new List<string>(); + public List<string> AdditionalLibrariesDebug = new List<string>(); + public List<string> IncludePath = new List<string>(); + public string proVarQT; + public string proVarCONFIG; + + public string LibRelease + { + get + { + return + LibraryPrefix.StartsWith("Qt", StringComparison.Ordinal) + ? "Qt5" + LibraryPrefix.Substring(2) + ".lib" + : LibraryPrefix + ".lib"; + } + } + + public string LibDebug + { + get + { + return + LibraryPrefix.StartsWith("Qt", StringComparison.Ordinal) + ? "Qt5" + LibraryPrefix.Substring(2) + "d.lib" + : LibraryPrefix + "d.lib"; + } + } + + public QtModule(int id) + { + Id = id; + } + + public int Id { get; } = -1; + + public List<string> GetIncludePath() + { + return IncludePath; + } + + public List<string> GetLibs(bool isDebugCfg, VersionInformation vi) + { + return GetLibs(isDebugCfg, vi.IsStaticBuild(), vi.LibInfix()); + } + + public List<string> GetLibs(bool isDebugCfg, bool isStaticBuild, string libInfix) + { + // TODO: isStaticBuild is never used. + var libs = new List<string>(); + var libName = LibraryPrefix; + if (libName.StartsWith("Qt", StringComparison.Ordinal)) + libName = "Qt5" + libName.Substring(2); + libName += libInfix; + if (isDebugCfg) + libName += "d"; + libName += ".lib"; + libs.Add(libName); + libs.AddRange(GetAdditionalLibs(isDebugCfg)); + return libs; + } + + private List<string> GetAdditionalLibs(bool isDebugCfg) + { + if (isDebugCfg && AdditionalLibrariesDebug.Count > 0) + return AdditionalLibrariesDebug; + return AdditionalLibraries; + } + } +} diff --git a/QtVsTools.Core/QtModules.cs b/QtVsTools.Core/QtModules.cs new file mode 100644 index 0000000..0b1bdde --- /dev/null +++ b/QtVsTools.Core/QtModules.cs @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace QtVsTools.Core +{ + public class QtModules + { + private static QtModules instance = new QtModules(); + private readonly Dictionary<int, QtModule> modules = new Dictionary<int, QtModule>(); + + public static QtModules Instance + { + get { return instance; } + } + + public QtModule Module(int id) + { + QtModule module; + modules.TryGetValue(id, out module); + return module; + } + + public List<QtModule> GetAvailableModules() + { + var lst = new List<QtModule>(modules.Count); + foreach (var entry in modules) + lst.Add(entry.Value); + return lst; + } + + private QtModules() + { + var uri = new Uri( + System.Reflection.Assembly.GetExecutingAssembly().EscapedCodeBase); + var pkgInstallPath = Path.GetDirectoryName( + Uri.UnescapeDataString(uri.AbsolutePath)) + @"\"; + + var modulesFile = Path.Combine(pkgInstallPath, "qtmodules.xml"); + if (!File.Exists(modulesFile)) + return; + + var xmlText = File.ReadAllText(modulesFile, Encoding.UTF8); + XDocument xml = null; + try { + using (var reader = XmlReader.Create(new StringReader(xmlText))) { + xml = XDocument.Load(reader); + } + } catch { } + + if (xml == null) + return; + + foreach (var xModule in xml.Elements("QtVsTools").Elements("Module")) { + int id = (int)xModule.Attribute("Id"); + QtModule module = new QtModule(id); + module.Name = (string)xModule.Element("Name"); + module.Selectable = ((string)xModule.Element("Selectable") == "true"); + module.LibraryPrefix = (string)xModule.Element("LibraryPrefix"); + module.HasDLL = ((string)xModule.Element("HasDLL") == "true"); + module.proVarQT = (string)xModule.Element("proVarQT"); + module.proVarCONFIG = (string)xModule.Element("proVarCONFIG"); + module.IncludePath = xModule.Elements("IncludePath") + .Select(x => x.Value).ToList(); + module.Defines = xModule.Elements("Defines") + .Select(x => x.Value).ToList(); + module.AdditionalLibraries = xModule.Elements("AdditionalLibraries") + .Select(x => x.Value).ToList(); + module.AdditionalLibrariesDebug = + xModule.Elements("AdditionalLibrariesDebug") + .Select(x => x.Value).ToList(); + if (string.IsNullOrEmpty(module.Name) + || string.IsNullOrEmpty(module.LibraryPrefix)) { + Messages.Print("\r\nCritical error: incorrect format of qtmodules.xml"); + throw new QtVSException("qtmodules.xml"); + } + modules.Add(id, module); + } + } + } +} diff --git a/QtVsTools.Core/QtMsBuild.cs b/QtVsTools.Core/QtMsBuild.cs new file mode 100644 index 0000000..45ce0b9 --- /dev/null +++ b/QtVsTools.Core/QtMsBuild.cs @@ -0,0 +1,1567 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System; +using System.IO; + +using CommandLineParser = QtVsTools.Core.CommandLine.Parser; +using CommandLineOption = QtVsTools.Core.CommandLine.Option; + +namespace QtVsTools.Core.QtMsBuild +{ + public interface IVSMacroExpander + { + string ExpandString(string stringToExpand); + } + + public interface IPropertyStorageProvider + { + string GetProperty( + object propertyStorage, + string itemType, + string propertyName); + + bool SetProperty( + object propertyStorage, + string itemType, + string propertyName, + string propertyValue); + + bool DeleteProperty( + object propertyStorage, + string itemType, + string propertyName); + + string GetConfigName( + object propertyStorage); + + string GetItemType( + object propertyStorage); + + string GetItemName( + object propertyStorage); + + object GetParentProject( + object propertyStorage); + + object GetProjectConfiguration( + object project, + string configName); + + IEnumerable<object> GetItems( + object project, + string itemType, + string configName = ""); + } + + public class QtMsBuildContainer + { + + IPropertyStorageProvider provider; + public QtMsBuildContainer(IPropertyStorageProvider provider) + { + this.provider = provider; + } + + public string GetPropertyValueByName( + object propertyStorage, + string itemType, + string propertyName) + { + return provider.GetProperty(propertyStorage, itemType, propertyName); + } + + bool SetPropertyValueByName( + object propertyStorage, + string itemType, + string propertyName, + string propertyValue) + { + return provider.SetProperty(propertyStorage, itemType, propertyName, propertyValue); + } + + bool DeletePropertyByName( + object propertyStorage, + string itemType, + string propertyName) + { + return provider.DeleteProperty(propertyStorage, itemType, propertyName); + } + + class ItemPropertyChange + { + //key + public string ConfigName; + public string ItemTypeName; + public string ItemName; + public string PropertyName; + + //value + public string PropertyValue; + public object PropertyStorage; + + public void CopyFrom(ItemPropertyChange change) + { + ConfigName = change.ConfigName; + ItemTypeName = change.ItemTypeName; + ItemName = change.ItemName; + PropertyName = change.PropertyName; + PropertyValue = change.PropertyValue; + PropertyStorage = change.PropertyStorage; + } + + public bool IsMocSource + { + get + { + return ItemTypeName == QtMoc.ItemTypeName + && !HelperFunctions.IsHeaderFile(ItemName); + } + } + + public string GroupKey + { + get + { + return string.Join(",", new string[] { + ConfigName, ItemTypeName, PropertyName, PropertyValue, + IsMocSource.ToString() + }); + } + } + + public string Key + { + get + { + return string.Join(",", new string[] { + ConfigName, ItemTypeName, PropertyName, ItemName + }); + } + } + } + + public IEnumerable<object> GetItems(string itemType, string configName = "") + { + return provider.GetItems(GetProject(), itemType, configName); + } + + int GetItemCount(string itemType, bool? isMocSource = null, string configName = "") + { + var items = GetItems(itemType, configName); + if (!isMocSource.HasValue) { + return items + .Count(); + } else if (!isMocSource.Value) { + return items.Where(x => + provider.GetItemType(x) != QtMoc.ItemTypeName + || HelperFunctions.IsHeaderFile(provider.GetItemName(x))) + .Count(); + } else { + return items.Where(x => + provider.GetItemType(x) == QtMoc.ItemTypeName + && !HelperFunctions.IsHeaderFile(provider.GetItemName(x))) + .Count(); + } + } + + object GetProjectConfiguration(string configName) + { + return provider.GetProjectConfiguration(GetProject(), configName); + } + + Dictionary<string, ItemPropertyChange> itemPropertyChanges + = new Dictionary<string, ItemPropertyChange>(); + Dictionary<string, List<ItemPropertyChange>> itemPropertyChangesGrouped + = new Dictionary<string, List<ItemPropertyChange>>(); + bool pendingChanges = false; + + void AddChange(ItemPropertyChange newChange) + { + ItemPropertyChange oldChange; + if (itemPropertyChanges.TryGetValue(newChange.Key, out oldChange)) { + if (oldChange.GroupKey == newChange.GroupKey) { + oldChange.CopyFrom(newChange); + return; + } else { + RemoveChange(oldChange); + } + } + + List<ItemPropertyChange> changeGroup; + if (!itemPropertyChangesGrouped.TryGetValue(newChange.GroupKey, out changeGroup)) { + itemPropertyChangesGrouped.Add( + newChange.GroupKey, + changeGroup = new List<ItemPropertyChange>()); + } + changeGroup.Add(newChange); + itemPropertyChanges.Add(newChange.Key, newChange); + } + + void RemoveChange(ItemPropertyChange change) + { + List<ItemPropertyChange> changeGroup; + if (itemPropertyChangesGrouped.TryGetValue(change.GroupKey, out changeGroup)) { + changeGroup.Remove(change); + if (changeGroup.Count == 0) + itemPropertyChangesGrouped.Remove(change.GroupKey); + } + itemPropertyChanges.Remove(change.Key); + } + + object GetProject() + { + var change = itemPropertyChanges.Values.FirstOrDefault(); + if (change == null) + return null; + return provider.GetParentProject(change.PropertyStorage); + } + + public bool BeginSetItemProperties() + { + if (pendingChanges) + return false; + itemPropertyChanges.Clear(); + itemPropertyChangesGrouped.Clear(); + pendingChanges = true; + return true; + } + + public bool SetItemPropertyByName( + object propertyStorage, + string propertyName, + string propertyValue) + { + if (propertyStorage == null) + return false; + + string configName = provider.GetConfigName(propertyStorage); + string itemType = provider.GetItemType(propertyStorage); + string itemName = provider.GetItemName(propertyStorage); + + var newChange = new ItemPropertyChange + { + ConfigName = configName, + ItemTypeName = itemType, + ItemName = itemName, + PropertyName = propertyName, + PropertyValue = propertyValue, + PropertyStorage = propertyStorage, + }; + + if (!pendingChanges) { + if (!BeginSetItemProperties()) + return false; + AddChange(newChange); + if (!EndSetItemProperties()) + return false; + } else { + AddChange(newChange); + } + + return true; + } + + bool SetGroupItemProperties(List<ItemPropertyChange> changeGroup) + { + //grouped by configName, itemTypeName, propertyName, propertyValue, isMocSource + var firstChange = changeGroup.FirstOrDefault(); + if (firstChange == null) + return false; + string configName = firstChange.ConfigName; + string itemTypeName = firstChange.ItemTypeName; + string propertyName = firstChange.PropertyName; + string propertyValue = firstChange.PropertyValue; + bool isMocSource = firstChange.IsMocSource; + int itemCount = GetItemCount(itemTypeName, isMocSource, configName); + int groupCount = changeGroup.Count; + object projConfig = GetProjectConfiguration(configName); + + if (!isMocSource && groupCount == itemCount) { + //all items are setting the same value for this property + // -> set at project level + if (!SetPropertyValueByName( + projConfig, + itemTypeName, + propertyName, + propertyValue)) { + return false; + } + + // -> remove old property from each item + foreach (var change in changeGroup) { + if (!DeletePropertyByName( + change.PropertyStorage, + change.ItemTypeName, + change.PropertyName)) { + return false; + } + + } + } else { + //different property values per item + // -> set at each item + foreach (var change in changeGroup) { + if (GetPropertyValueByName( + projConfig, + change.ItemTypeName, + change.PropertyName) != change.PropertyValue) { + if (!SetPropertyValueByName( + change.PropertyStorage, + change.ItemTypeName, + change.PropertyName, + change.PropertyValue)) { + return false; + } + + } + } + } + return true; + } + + public bool EndSetItemProperties() + { + if (!pendingChanges) + return false; + + var changeGroupsNormal = itemPropertyChangesGrouped.Values + .Where(x => x.Any() && !x.First().IsMocSource); + foreach (var changeGroup in changeGroupsNormal) + SetGroupItemProperties(changeGroup); + + var changeGroupsMocSource = itemPropertyChangesGrouped.Values + .Where(x => x.Any() && x.First().IsMocSource); + foreach (var changeGroup in changeGroupsMocSource) + SetGroupItemProperties(changeGroup); + + itemPropertyChanges.Clear(); + itemPropertyChangesGrouped.Clear(); + pendingChanges = false; + return true; + } + + string GetPropertyChangedValue( + string configName, + string itemTypeName, + string itemName, + string propertyName) + { + if (!pendingChanges) + return null; + + var change = new ItemPropertyChange + { + ConfigName = configName, + ItemTypeName = itemTypeName, + ItemName = itemName, + PropertyName = propertyName + }; + if (!itemPropertyChanges.TryGetValue(change.Key, out change)) + return null; + + return change.PropertyValue; + } + + public string GetPropertyChangedValue( + QtMoc.Property property, + string itemName, + string configName) + { + return GetPropertyChangedValue( + configName, + QtMoc.ItemTypeName, + itemName, + property.ToString()); + } + + public string GetPropertyChangedValue( + QtRcc.Property property, + string itemName, + string configName) + { + return GetPropertyChangedValue( + configName, + QtRcc.ItemTypeName, + itemName, + property.ToString()); + } + + public string GetPropertyChangedValue( + QtRepc.Property property, + string itemName, + string configName) + { + return GetPropertyChangedValue( + configName, + QtRepc.ItemTypeName, + itemName, + property.ToString()); + } + + public string GetPropertyChangedValue( + QtUic.Property property, + string itemName, + string configName) + { + return GetPropertyChangedValue( + configName, + QtUic.ItemTypeName, + itemName, + property.ToString()); + } + + public bool SetCommandLine( + string itemType, + object propertyStorage, + string commandLine, + IVSMacroExpander macros) + { + switch (itemType) { + case QtMoc.ItemTypeName: + return SetQtMocCommandLine(propertyStorage, commandLine, macros); + case QtRcc.ItemTypeName: + return SetQtRccCommandLine(propertyStorage, commandLine, macros); + case QtRepc.ItemTypeName: + return SetQtRepcCommandLine(propertyStorage, commandLine, macros); + case QtUic.ItemTypeName: + return SetQtUicCommandLine(propertyStorage, commandLine, macros); + } + return false; + } + + #region QtMoc + static QtMoc qtMocInstance; + public static QtMoc QtMocInstance + { + get + { + if (qtMocInstance == null) + qtMocInstance = new QtMoc(); + return qtMocInstance; + } + } + + public string GetPropertyValue(object propertyStorage, QtMoc.Property property) + { + return GetPropertyValueByName( + propertyStorage, + QtMoc.ItemTypeName, + property.ToString()); + } + + public bool SetItemProperty( + object propertyStorage, + QtMoc.Property property, + string propertyValue) + { + return SetItemPropertyByName(propertyStorage, property.ToString(), propertyValue); + } + + public bool SetQtMocCommandLine( + object propertyStorage, + string commandLine, + IVSMacroExpander macros) + { + Dictionary<QtMoc.Property, string> properties; + if (!QtMocInstance.ParseCommandLine(commandLine, macros, out properties)) + return false; + foreach (var property in properties) { + if (!SetItemProperty(propertyStorage, property.Key, property.Value)) + return false; + } + return true; + } + + public string GenerateQtMocCommandLine(object propertyStorage) + { + return QtMocInstance.GenerateCommandLine(this, propertyStorage); + } + #endregion + + #region QtRcc + static QtRcc qtRccInstance; + public static QtRcc QtRccInstance + { + get + { + if (qtRccInstance == null) + qtRccInstance = new QtRcc(); + return qtRccInstance; + } + } + + public string GetPropertyValue(object propertyStorage, QtRcc.Property property) + { + return GetPropertyValueByName( + propertyStorage, + QtRcc.ItemTypeName, + property.ToString()); + } + + public bool SetItemProperty( + object propertyStorage, + QtRcc.Property property, + string propertyValue) + { + return SetItemPropertyByName(propertyStorage, property.ToString(), propertyValue); + } + + public bool SetQtRccCommandLine( + object propertyStorage, + string commandLine, + IVSMacroExpander macros) + { + Dictionary<QtRcc.Property, string> properties; + if (!QtRccInstance.ParseCommandLine(commandLine, macros, out properties)) + return false; + foreach (var property in properties) { + if (!SetItemProperty(propertyStorage, property.Key, property.Value)) + return false; + } + return true; + } + + public string GenerateQtRccCommandLine(object propertyStorage) + { + return QtRccInstance.GenerateCommandLine(this, propertyStorage); + } + #endregion + + #region QtRepc + static QtRepc qtRepcInstance; + public static QtRepc QtRepcInstance + { + get + { + if (qtRepcInstance == null) + qtRepcInstance = new QtRepc(); + return qtRepcInstance; + } + } + + public string GetPropertyValue(object propertyStorage, QtRepc.Property property) + { + return GetPropertyValueByName( + propertyStorage, + QtRepc.ItemTypeName, + property.ToString()); + } + + public bool SetItemProperty( + object propertyStorage, + QtRepc.Property property, + string propertyValue) + { + return SetItemPropertyByName(propertyStorage, property.ToString(), propertyValue); + } + + public bool SetQtRepcCommandLine( + object propertyStorage, + string commandLine, + IVSMacroExpander macros) + { + Dictionary<QtRepc.Property, string> properties; + if (!QtRepcInstance.ParseCommandLine(commandLine, macros, out properties)) + return false; + foreach (var property in properties) { + if (!SetItemProperty(propertyStorage, property.Key, property.Value)) + return false; + } + return true; + } + + public string GenerateQtRepcCommandLine(object propertyStorage) + { + return QtRepcInstance.GenerateCommandLine(this, propertyStorage); + } + #endregion + + #region QtUic + static QtUic qtUicInstance; + public static QtUic QtUicInstance + { + get + { + if (qtUicInstance == null) + qtUicInstance = new QtUic(); + return qtUicInstance; + } + } + + public string GetPropertyValue(object propertyStorage, QtUic.Property property) + { + return GetPropertyValueByName( + propertyStorage, + QtUic.ItemTypeName, + property.ToString()); + } + + public bool SetItemProperty( + object propertyStorage, + QtUic.Property property, + string propertyValue) + { + return SetItemPropertyByName(propertyStorage, property.ToString(), propertyValue); + } + + public bool SetQtUicCommandLine( + object propertyStorage, + string commandLine, + IVSMacroExpander macros) + { + Dictionary<QtUic.Property, string> properties; + if (!QtUicInstance.ParseCommandLine(commandLine, macros, out properties)) + return false; + foreach (var property in properties) { + if (!SetItemProperty(propertyStorage, property.Key, property.Value)) + return false; + } + return true; + } + + public string GenerateQtUicCommandLine(object propertyStorage) + { + return QtUicInstance.GenerateCommandLine(this, propertyStorage); + } + #endregion + + } + + public abstract class QtTool + { + protected CommandLineParser parser; + protected CommandLineOption outputOption; + protected CommandLineOption helpOption; + protected CommandLineOption versionOption; + + protected QtTool(bool defaultInputOutput = true) + { + parser = new CommandLineParser(); + parser.SetSingleDashWordOptionMode( + CommandLineParser.SingleDashWordOptionMode.ParseAsLongOptions); + + helpOption = parser.AddHelpOption(); + versionOption = parser.AddVersionOption(); + + if (defaultInputOutput) { + outputOption = new CommandLineOption("o"); + outputOption.ValueName = "file"; + outputOption.Flags = CommandLineOption.Flag.ShortOptionStyle; + parser.AddOption(outputOption); + } + } + + protected virtual void ExtractInputOutput( + string toolExecName, + out string inputPath, + out string outputPath) + { + inputPath = outputPath = ""; + + string filePath = parser.PositionalArguments.Where( + arg => !arg.EndsWith(toolExecName, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefault(); + if (!string.IsNullOrEmpty(filePath)) + inputPath = filePath; + + if (outputOption != null && parser.IsSet(outputOption)) + outputPath = parser.Value(outputOption); + } + + protected bool ParseCommandLine( + string commandLine, + IVSMacroExpander macros, + string toolExecName, + out string qtDir, + out string inputPath, + out string outputPath) + { + qtDir = inputPath = outputPath = ""; + if (!parser.Parse(commandLine, macros, toolExecName)) + return false; + + string execPath = parser.PositionalArguments.Where( + arg => arg.EndsWith(toolExecName, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefault(); + if (!string.IsNullOrEmpty(execPath)) { + var execDir = Path.GetDirectoryName(execPath); + if (!string.IsNullOrEmpty(execDir)) + qtDir = HelperFunctions.CanonicalPath(Path.Combine(execDir, "..")); + } + + ExtractInputOutput(toolExecName, out inputPath, out outputPath); + return true; + } + + protected void GenerateCommandLineOption( + StringBuilder commandLine, + CommandLineOption option, + string values = "", + bool useQuotes = false) + { + var name = option.Names.First(); + var escape = (name.Length == 1) ? "-" : "--"; + + if (!string.IsNullOrEmpty(option.ValueName)) { + foreach (var value in values.Split(new char[] { ';' })) { + if (useQuotes) + commandLine.AppendFormat(" {0}{1} \"{2}\"", escape, name, value); + else + commandLine.AppendFormat(" {0}{1} {2}", escape, name, value); + } + } else { + commandLine.AppendFormat(" {0}{1}", escape, name); + } + } + } + + public sealed class QtMoc : QtTool + { + public const string ItemTypeName = "QtMoc"; + public const string ToolExecName = "moc.exe"; + + public enum Property + { + ExecutionDescription, + QTDIR, + InputFile, + OutputFile, + IncludePath, + MacFramework, + PreprocessOnly, + Define, + Undefine, + Metadata, + CompilerFlavor, + NoInclude, + PathPrefix, + ForceInclude, + PrependInclude, + Include, + NoNotesWarnings, + NoNotes, + NoWarnings, + IgnoreConflicts, + OptionsFile, + CommandLineTemplate, + AdditionalOptions, + DynamicSource, + ParallelProcess, + AdditionalDependencies, + } + + Dictionary<Property, CommandLineOption> options + = new Dictionary<Property, CommandLineOption>(); + + public QtMoc() : base() + { + parser.AddOption(options[Property.IncludePath] = + new CommandLineOption("I") + { + ValueName = "dir", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.MacFramework] = + new CommandLineOption("F") + { + ValueName = "framework", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.PreprocessOnly] = + new CommandLineOption("E")); + + parser.AddOption(options[Property.Define] = + new CommandLineOption("D") + { + ValueName = "macro[=def]", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.Undefine] = + new CommandLineOption("U") + { + ValueName = ("macro"), + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.Metadata] = + new CommandLineOption("M") + { + ValueName = "key=value", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.CompilerFlavor] = + new CommandLineOption("compiler-flavor") + { + ValueName = "flavor" + }); + + parser.AddOption(options[Property.NoInclude] = + new CommandLineOption("i")); + + parser.AddOption(options[Property.PathPrefix] = + new CommandLineOption("p") + { + ValueName = "path", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.ForceInclude] = + new CommandLineOption("f") + { + ValueName = "file", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.PrependInclude] = + new CommandLineOption("b") + { + ValueName = "file" + }); + + parser.AddOption(options[Property.Include] = + new CommandLineOption("include") + { + ValueName = "file" + }); + + parser.AddOption(options[Property.NoNotesWarnings] = + new CommandLineOption("n") + { + ValueName = "which", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.NoNotes] = + new CommandLineOption("no-notes")); + + parser.AddOption(options[Property.NoWarnings] = + new CommandLineOption("no-warnings")); + + parser.AddOption(options[Property.IgnoreConflicts] = + new CommandLineOption("ignore-option-clashes")); + } + + public bool ParseCommandLine( + string commandLine, + IVSMacroExpander macros, + out Dictionary<Property, string> properties) + { + properties = new Dictionary<Property, string>(); + + string qtDir, inputPath, outputPath; + if (!ParseCommandLine( + commandLine, + macros, + ToolExecName, + out qtDir, + out inputPath, + out outputPath)) { + return false; + } + + if (!string.IsNullOrEmpty(qtDir)) + properties[Property.QTDIR] = qtDir; + + if (!string.IsNullOrEmpty(inputPath)) + properties[Property.InputFile] = inputPath; + + if (!string.IsNullOrEmpty(outputPath)) + properties[Property.OutputFile] = outputPath; + + if (parser.IsSet(options[Property.IncludePath])) { + properties[Property.IncludePath] = + string.Join(";", parser.Values(options[Property.IncludePath])); + } + + if (parser.IsSet(options[Property.MacFramework])) { + properties[Property.MacFramework] = + string.Join(";", parser.Values(options[Property.MacFramework])); + } + + if (parser.IsSet(options[Property.PreprocessOnly])) + properties[Property.PreprocessOnly] = "true"; + + if (parser.IsSet(options[Property.Define])) { + properties[Property.Define] = + string.Join(";", parser.Values(options[Property.Define])); + } + + if (parser.IsSet(options[Property.Undefine])) { + properties[Property.Undefine] = + string.Join(";", parser.Values(options[Property.Undefine])); + } + + if (parser.IsSet(options[Property.Metadata])) { + properties[Property.Metadata] = + string.Join(";", parser.Values(options[Property.Metadata])); + } + + if (parser.IsSet(options[Property.CompilerFlavor])) { + properties[Property.CompilerFlavor] = + string.Join(";", parser.Values(options[Property.CompilerFlavor])); + } + + if (parser.IsSet(options[Property.NoInclude])) + properties[Property.NoInclude] = "true"; + + if (parser.IsSet(options[Property.PathPrefix])) { + properties[Property.PathPrefix] = + string.Join(";", parser.Values(options[Property.PathPrefix])); + } + + if (parser.IsSet(options[Property.ForceInclude])) { + properties[Property.ForceInclude] = + string.Join(";", parser.Values(options[Property.ForceInclude])); + } + + if (parser.IsSet(options[Property.PrependInclude])) { + properties[Property.PrependInclude] = + string.Join(";", parser.Values(options[Property.PrependInclude])); + } + + if (parser.IsSet(options[Property.Include])) { + properties[Property.Include] = + string.Join(";", parser.Values(options[Property.Include])); + } + + if (parser.IsSet(options[Property.NoNotesWarnings])) { + properties[Property.NoNotesWarnings] = + string.Join(";", parser.Values(options[Property.NoNotesWarnings])); + } + + if (parser.IsSet(options[Property.NoNotes])) + properties[Property.NoNotes] = "true"; + + if (parser.IsSet(options[Property.NoWarnings])) + properties[Property.NoWarnings] = "true"; + + if (parser.IsSet(options[Property.IgnoreConflicts])) + properties[Property.IgnoreConflicts] = "true"; + + return true; + } + + public string GenerateCommandLine(QtMsBuildContainer container, object propertyStorage) + { + var cmd = new StringBuilder(); + cmd.AppendFormat(@"""{0}\bin\{1}"" ""{2}"" -o ""{3}""", + container.GetPropertyValue(propertyStorage, Property.QTDIR), + ToolExecName, + container.GetPropertyValue(propertyStorage, Property.InputFile), + container.GetPropertyValue(propertyStorage, Property.OutputFile)); + + string value = container.GetPropertyValue(propertyStorage, Property.IncludePath); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.IncludePath], value, true); + + value = container.GetPropertyValue(propertyStorage, Property.MacFramework); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.MacFramework], value); + + if (container.GetPropertyValue(propertyStorage, Property.PreprocessOnly) == "true") + GenerateCommandLineOption(cmd, options[Property.PreprocessOnly]); + + value = container.GetPropertyValue(propertyStorage, Property.Define); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Define], value); + + value = container.GetPropertyValue(propertyStorage, Property.Undefine); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Undefine], value); + + value = container.GetPropertyValue(propertyStorage, Property.Metadata); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Metadata], value, true); + + value = container.GetPropertyValue(propertyStorage, Property.CompilerFlavor); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.CompilerFlavor], value); + + if (container.GetPropertyValue(propertyStorage, Property.NoInclude) == "true") + GenerateCommandLineOption(cmd, options[Property.NoInclude]); + + value = container.GetPropertyValue(propertyStorage, Property.PathPrefix); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.PathPrefix], value, true); + + value = container.GetPropertyValue(propertyStorage, Property.ForceInclude); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.ForceInclude], value, true); + + value = container.GetPropertyValue(propertyStorage, Property.PrependInclude); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.PrependInclude], value, true); + + value = container.GetPropertyValue(propertyStorage, Property.Include); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Include], value, true); + + value = container.GetPropertyValue(propertyStorage, Property.NoNotesWarnings); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.NoNotesWarnings], value, true); + + if (container.GetPropertyValue(propertyStorage, Property.NoNotes) == "true") + GenerateCommandLineOption(cmd, options[Property.NoNotes]); + + if (container.GetPropertyValue(propertyStorage, Property.NoWarnings) == "true") + GenerateCommandLineOption(cmd, options[Property.NoWarnings]); + + if (container.GetPropertyValue(propertyStorage, Property.IgnoreConflicts) == "true") + GenerateCommandLineOption(cmd, options[Property.IgnoreConflicts]); + + return cmd.ToString(); + } + + } + + public sealed class QtRcc : QtTool + { + public const string ItemTypeName = "QtRcc"; + public const string ToolExecName = "rcc.exe"; + + public enum Property + { + ExecutionDescription, + QTDIR, + InputFile, + OutputFile, + TempFile, + InitFuncName, + Root, + Compression, + NoCompression, + CompressThreshold, + BinaryOutput, + PassNumber, + NoNamespace, + Verbose, + List, + Project, + FormatVersion, + CommandLineTemplate, + AdditionalOptions, + DynamicSource, + ParallelProcess, + AdditionalDependencies, + } + + Dictionary<Property, CommandLineOption> options + = new Dictionary<Property, CommandLineOption>(); + + public QtRcc() : base() + { + parser.AddOption(options[Property.TempFile] = + new CommandLineOption(new string[] { "t", "temp" }, "file")); + + parser.AddOption(options[Property.InitFuncName] = + new CommandLineOption("name", "name")); + + parser.AddOption(options[Property.Root] = + new CommandLineOption("root", "path")); + + parser.AddOption(options[Property.Compression] = + new CommandLineOption("compress", "level")); + + parser.AddOption(options[Property.NoCompression] = + new CommandLineOption("no-compress")); + + parser.AddOption(options[Property.CompressThreshold] = + new CommandLineOption("threshold", "level")); + + parser.AddOption(options[Property.BinaryOutput] = + new CommandLineOption("binary")); + + parser.AddOption(options[Property.PassNumber] = + new CommandLineOption("pass", "number")); + + parser.AddOption(options[Property.NoNamespace] = + new CommandLineOption("namespace")); + + parser.AddOption(options[Property.Verbose] = + new CommandLineOption("verbose")); + + parser.AddOption(options[Property.List] = + new CommandLineOption("list")); + + parser.AddOption(options[Property.Project] = + new CommandLineOption("project")); + + parser.AddOption(options[Property.FormatVersion] = + new CommandLineOption("format-version", "number")); + } + + public bool ParseCommandLine( + string commandLine, + IVSMacroExpander macros, + out Dictionary<Property, string> properties) + { + properties = new Dictionary<Property, string>(); + + string qtDir, inputPath, outputPath; + if (!ParseCommandLine( + commandLine, + macros, + ToolExecName, + out qtDir, + out inputPath, + out outputPath)) { + return false; + } + + if (!string.IsNullOrEmpty(qtDir)) + properties[Property.QTDIR] = qtDir; + + if (!string.IsNullOrEmpty(inputPath)) + properties[Property.InputFile] = inputPath; + + if (!string.IsNullOrEmpty(outputPath)) + properties[Property.OutputFile] = outputPath; + + if (parser.IsSet(options[Property.InitFuncName])) + properties[Property.InitFuncName] = parser.Value(options[Property.InitFuncName]); + + if (parser.IsSet(options[Property.Root])) + properties[Property.Root] = parser.Value(options[Property.Root]); + + if (parser.IsSet(options[Property.Compression])) { + int level; + if (!int.TryParse(parser.Value(options[Property.Compression]), out level)) + return false; + if (level < 1 || 9 < level) + return false; + properties[Property.Compression] = string.Format("level{0}", level); + } else { + properties[Property.Compression] = "default"; + } + + if (parser.IsSet(options[Property.NoCompression])) + properties[Property.NoCompression] = "true"; + + if (parser.IsSet(options[Property.CompressThreshold])) { + properties[Property.CompressThreshold] = + parser.Value(options[Property.CompressThreshold]); + } + + if (parser.IsSet(options[Property.BinaryOutput])) + properties[Property.BinaryOutput] = "true"; + + if (parser.IsSet(options[Property.PassNumber])) + properties[Property.PassNumber] = parser.Value(options[Property.PassNumber]); + + if (parser.IsSet(options[Property.NoNamespace])) + properties[Property.NoNamespace] = "true"; + + if (parser.IsSet(options[Property.Verbose])) + properties[Property.Verbose] = "true"; + + if (parser.IsSet(options[Property.List])) + properties[Property.List] = "true"; + + if (parser.IsSet(options[Property.Project])) + properties[Property.Project] = "true"; + + if (parser.IsSet(options[Property.FormatVersion])) + properties[Property.FormatVersion] = parser.Value(options[Property.FormatVersion]); + + return true; + } + + public string GenerateCommandLine(QtMsBuildContainer container, object propertyStorage) + { + var cmd = new StringBuilder(); + cmd.AppendFormat(@"""{0}\bin\{1}"" ""{2}"" -o ""{3}""", + container.GetPropertyValue(propertyStorage, Property.QTDIR), + ToolExecName, + container.GetPropertyValue(propertyStorage, Property.InputFile), + container.GetPropertyValue(propertyStorage, Property.OutputFile)); + + string value = container.GetPropertyValue(propertyStorage, Property.InitFuncName); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.InitFuncName], value); + + value = container.GetPropertyValue(propertyStorage, Property.Root); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Root], value, true); + + value = container.GetPropertyValue(propertyStorage, Property.Compression); + if (value.StartsWith("level")) { + GenerateCommandLineOption(cmd, + options[Property.Compression], + value.Substring(5), true); + } + + if (container.GetPropertyValue(propertyStorage, Property.NoCompression) == "true") + GenerateCommandLineOption(cmd, options[Property.NoCompression]); + + value = container.GetPropertyValue(propertyStorage, Property.CompressThreshold); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.CompressThreshold], value); + + if (container.GetPropertyValue(propertyStorage, Property.BinaryOutput) == "true") + GenerateCommandLineOption(cmd, options[Property.BinaryOutput]); + + value = container.GetPropertyValue(propertyStorage, Property.PassNumber); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.PassNumber], value); + + if (container.GetPropertyValue(propertyStorage, Property.Verbose) == "true") + GenerateCommandLineOption(cmd, options[Property.Verbose]); + + if (container.GetPropertyValue(propertyStorage, Property.List) == "true") + GenerateCommandLineOption(cmd, options[Property.List]); + + if (container.GetPropertyValue(propertyStorage, Property.Project) == "true") + GenerateCommandLineOption(cmd, options[Property.Project]); + + value = container.GetPropertyValue(propertyStorage, Property.FormatVersion); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.FormatVersion], value); + + return cmd.ToString(); + } + } + + public sealed class QtRepc : QtTool + { + public const string ItemTypeName = "QtRepc"; + public const string ToolExecName = "repc.exe"; + + public enum Property + { + ExecutionDescription, + QTDIR, + InputFileType, + InputFile, + OutputFileType, + OutputFile, + IncludePath, + AlwaysClass, + PrintDebug, + } + + Dictionary<Property, CommandLineOption> options + = new Dictionary<Property, CommandLineOption>(); + + public QtRepc() : base(defaultInputOutput: false) + { + parser.AddOption(options[Property.InputFileType] = + new CommandLineOption("i") + { + ValueName = "<rep|src>", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.OutputFileType] = + new CommandLineOption("o") + { + ValueName = "<source|replica|merged|rep>", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.IncludePath] = + new CommandLineOption("I") + { + ValueName = "dir", + Flags = CommandLineOption.Flag.ShortOptionStyle + }); + + parser.AddOption(options[Property.AlwaysClass] = + new CommandLineOption("c")); + + parser.AddOption(options[Property.PrintDebug] = + new CommandLineOption("d")); + } + + protected override void ExtractInputOutput( + string toolExecName, + out string inputPath, + out string outputPath) + { + inputPath = outputPath = ""; + + var args = new Queue<string>(parser.PositionalArguments + .Where(arg => !arg.EndsWith(toolExecName, + StringComparison.InvariantCultureIgnoreCase))); + + if (args.Any()) + inputPath = args.Dequeue(); + + if (args.Any()) + outputPath = args.Dequeue(); + } + + public bool ParseCommandLine( + string commandLine, + IVSMacroExpander macros, + out Dictionary<Property, string> properties) + { + properties = new Dictionary<Property, string>(); + + string qtDir, inputPath, outputPath; + if (!ParseCommandLine( + commandLine, + macros, + ToolExecName, + out qtDir, + out inputPath, + out outputPath)) { + return false; + } + + if (!string.IsNullOrEmpty(qtDir)) + properties[Property.QTDIR] = qtDir; + + if (parser.IsSet(options[Property.InputFileType])) { + properties[Property.InputFileType] = + string.Join(";", parser.Values(options[Property.InputFileType])); + } + + if (!string.IsNullOrEmpty(inputPath)) + properties[Property.InputFile] = inputPath; + + if (parser.IsSet(options[Property.OutputFileType])) { + properties[Property.OutputFileType] = + string.Join(";", parser.Values(options[Property.OutputFileType])); + } + + if (!string.IsNullOrEmpty(outputPath)) + properties[Property.OutputFile] = outputPath; + + if (parser.IsSet(options[Property.IncludePath])) { + properties[Property.IncludePath] = + string.Join(";", parser.Values(options[Property.IncludePath])); + } + + if (parser.IsSet(options[Property.AlwaysClass])) + properties[Property.AlwaysClass] = "true"; + + if (parser.IsSet(options[Property.PrintDebug])) + properties[Property.PrintDebug] = "true"; + + return true; + } + + public string GenerateCommandLine(QtMsBuildContainer container, object propertyStorage) + { + var cmd = new StringBuilder(); + cmd.AppendFormat(@"""{0}\bin\{1}""", + container.GetPropertyValue(propertyStorage, Property.QTDIR), ToolExecName); + + var inputType = container.GetPropertyValue(propertyStorage, Property.InputFileType); + if (!string.IsNullOrEmpty(inputType)) + GenerateCommandLineOption(cmd, options[Property.InputFileType], inputType); + + var outputType = container.GetPropertyValue(propertyStorage, Property.OutputFileType); + if (!string.IsNullOrEmpty(inputType)) + GenerateCommandLineOption(cmd, options[Property.InputFileType], inputType); + + string value = container.GetPropertyValue(propertyStorage, Property.IncludePath); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.IncludePath], value, true); + + if (container.GetPropertyValue(propertyStorage, Property.AlwaysClass) == "true") + GenerateCommandLineOption(cmd, options[Property.AlwaysClass]); + + if (container.GetPropertyValue(propertyStorage, Property.PrintDebug) == "true") + GenerateCommandLineOption(cmd, options[Property.PrintDebug]); + + value = container.GetPropertyValue(propertyStorage, Property.InputFile); + if (!string.IsNullOrEmpty(value)) + cmd.AppendFormat(" \"{0}\"", value); + + value = container.GetPropertyValue(propertyStorage, Property.OutputFile); + if (!string.IsNullOrEmpty(value)) + cmd.AppendFormat(" \"{0}\"", value); + + return cmd.ToString(); + } + + } + + public sealed class QtUic : QtTool + { + public const string ItemTypeName = "QtUic"; + public const string ToolExecName = "uic.exe"; + + public enum Property + { + ExecutionDescription, + QTDIR, + InputFile, + OutputFile, + DisplayDependencies, + NoProtection, + NoImplicitIncludes, + Postfix, + Translate, + Include, + Generator, + IdBased, + CommandLineTemplate, + AdditionalOptions, + ParallelProcess, + AdditionalDependencies, + } + + Dictionary<Property, CommandLineOption> options + = new Dictionary<Property, CommandLineOption>(); + + public QtUic() : base() + { + parser.AddOption(options[Property.DisplayDependencies] = + new CommandLineOption(new string[] { "d", "dependencies" })); + + parser.AddOption(options[Property.NoProtection] = + new CommandLineOption(new string[] { "p", "no-protection" })); + + parser.AddOption(options[Property.NoImplicitIncludes] = + new CommandLineOption(new string[] { "n", "no-implicit-includes" })); + + parser.AddOption(options[Property.Postfix] = + new CommandLineOption("postfix", "postfix")); + + parser.AddOption(options[Property.Translate] = + new CommandLineOption(new string[] { "tr", "translate" }, "function")); + + parser.AddOption(options[Property.Include] = + new CommandLineOption("include", "include-file")); + + parser.AddOption(options[Property.Generator] = + new CommandLineOption(new string[] { "g", "generator" }, "java|cpp")); + + parser.AddOption(options[Property.IdBased] = + new CommandLineOption("idbased")); + } + + public bool ParseCommandLine( + string commandLine, + IVSMacroExpander macros, + out Dictionary<Property, string> properties) + { + properties = new Dictionary<Property, string>(); + + string qtDir, inputPath, outputPath; + if (!ParseCommandLine( + commandLine, + macros, + ToolExecName, + out qtDir, + out inputPath, + out outputPath)) { + return false; + } + + if (!string.IsNullOrEmpty(qtDir)) + properties[Property.QTDIR] = qtDir; + + if (!string.IsNullOrEmpty(inputPath)) + properties[Property.InputFile] = inputPath; + + if (!string.IsNullOrEmpty(outputPath)) + properties[Property.OutputFile] = outputPath; + + if (parser.IsSet(options[Property.DisplayDependencies])) + properties[Property.DisplayDependencies] = "true"; + + if (parser.IsSet(options[Property.NoProtection])) + properties[Property.NoProtection] = "true"; + + if (parser.IsSet(options[Property.NoImplicitIncludes])) + properties[Property.NoImplicitIncludes] = "true"; + + if (parser.IsSet(options[Property.Postfix])) + properties[Property.Postfix] = parser.Value(options[Property.Postfix]); + + if (parser.IsSet(options[Property.Translate])) + properties[Property.Translate] = parser.Value(options[Property.Translate]); + + if (parser.IsSet(options[Property.Include])) + properties[Property.Include] = parser.Value(options[Property.Include]); + + if (parser.IsSet(options[Property.Generator])) + properties[Property.Generator] = parser.Value(options[Property.Generator]); + + if (parser.IsSet(options[Property.IdBased])) + properties[Property.IdBased] = "true"; + + return true; + } + + public string GenerateCommandLine(QtMsBuildContainer container, object propertyStorage) + { + var cmd = new StringBuilder(); + cmd.AppendFormat(@"""{0}\bin\{1}"" ""{2}"" -o ""{3}""", + container.GetPropertyValue(propertyStorage, Property.QTDIR), + ToolExecName, + container.GetPropertyValue(propertyStorage, Property.InputFile), + container.GetPropertyValue(propertyStorage, Property.OutputFile)); + + if (container.GetPropertyValue( + propertyStorage, + Property.DisplayDependencies) + == "true") { + GenerateCommandLineOption(cmd, options[Property.DisplayDependencies]); + } + + if (container.GetPropertyValue(propertyStorage, Property.NoProtection) == "true") + GenerateCommandLineOption(cmd, options[Property.NoProtection]); + + if (container.GetPropertyValue(propertyStorage, Property.NoImplicitIncludes) == "true") + GenerateCommandLineOption(cmd, options[Property.NoImplicitIncludes]); + + string value = container.GetPropertyValue(propertyStorage, Property.Postfix); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Postfix], value); + + value = container.GetPropertyValue(propertyStorage, Property.Translate); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Translate], value); + + value = container.GetPropertyValue(propertyStorage, Property.Include); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Include], value); + + value = container.GetPropertyValue(propertyStorage, Property.Generator); + if (!string.IsNullOrEmpty(value)) + GenerateCommandLineOption(cmd, options[Property.Generator], value); + + if (container.GetPropertyValue(propertyStorage, Property.IdBased) == "true") + GenerateCommandLineOption(cmd, options[Property.IdBased]); + + return cmd.ToString(); + } + } +} diff --git a/QtVsTools.Core/QtProject.cs b/QtVsTools.Core/QtProject.cs new file mode 100644 index 0000000..4f4183a --- /dev/null +++ b/QtVsTools.Core/QtProject.cs @@ -0,0 +1,4126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.VCProjectEngine; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Xml; +using QtVsTools.Core.QtMsBuild; + +namespace QtVsTools.Core +{ + /// <summary> + /// QtProject holds the Qt specific properties for a Visual Studio project. + /// There exists at most one QtProject per EnvDTE.Project. + /// Use QtProject.Create to get the QtProject for a Project or VCProject. + /// </summary> + public class QtProject + { + private DTE dte; + private Project envPro; + private VCProject vcPro; + private MocCmdChecker mocCmdChecker; + private Array lastConfigurationRowNames; + private static Dictionary<Project, QtProject> instances = new Dictionary<Project, QtProject>(); + private QtMsBuildContainer qtMsBuild; + + public static QtVsTools.VisualStudio.IProjectTracker ProjectTracker { get; set; } + + public static QtProject Create(VCProject vcProject) + { + return Create((Project)vcProject.Object); + } + + public static QtProject Create(Project project) + { + QtProject qtProject = null; + if (project != null && !instances.TryGetValue(project, out qtProject)) { + qtProject = new QtProject(project); + instances.Add(project, qtProject); + } + return qtProject; + } + + public static void ClearInstances() + { + instances.Clear(); + } + + private QtProject(Project project) + { + if (project == null) + throw new QtVSException(SR.GetString("QtProject_CannotConstructWithoutValidProject")); + envPro = project; + dte = envPro.DTE; + vcPro = envPro.Object as VCProject; + qtMsBuild = new QtMsBuildContainer(new VCPropertyStorageProvider()); + } + + public VCProject VCProject + { + get { return vcPro; } + } + + public Project Project + { + get { return envPro; } + } + + public static string GetRuleName(VCConfiguration config, string itemType) + { + if (config == null) + return string.Empty; + try { + return config.GetEvaluatedPropertyValue(itemType + "RuleName"); + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return string.Empty; + } + } + + public static string GetRuleName(VCProject project, string itemType) + { + if (project == null) + return string.Empty; + var configs = project.Configurations as IVCCollection; + if (configs.Count == 0) + return string.Empty; + try { + var firstConfig = configs.Item(1) as VCConfiguration; + if (firstConfig == null) + return string.Empty; + return GetRuleName(firstConfig, itemType); + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return string.Empty; + } + } + + public string GetRuleName(string itemType) + { + return GetRuleName(vcPro, itemType); + } + + public static bool IsQtMsBuildEnabled(VCProject project) + { + if (project == null) + return false; + try { + var configs = project.Configurations as IVCCollection; + if (configs.Count == 0) + return false; + var firstConfig = configs.Item(1) as VCConfiguration; + var ruleName = GetRuleName(firstConfig, QtMoc.ItemTypeName); + var qtMoc = firstConfig.Rules.Item(ruleName) as IVCRulePropertyStorage; + if (qtMoc == null) + return false; + } catch (Exception) { + return false; + } + return true; + } + + public static bool IsQtMsBuildEnabled(Project project) + { + if (project == null) + return false; + return IsQtMsBuildEnabled(project.Object as VCProject); + } + + private bool? isQtMsBuildEnabled = null; + public bool IsQtMsBuildEnabled() + { + if (!isQtMsBuildEnabled.HasValue) { + if (vcPro != null) + isQtMsBuildEnabled = IsQtMsBuildEnabled(vcPro); + else if (envPro != null) + isQtMsBuildEnabled = IsQtMsBuildEnabled(envPro); + else + return false; + } + return isQtMsBuildEnabled.Value; + } + + public string ProjectDir + { + get + { + return vcPro.ProjectDirectory; + } + } + + /// <summary> + /// Returns true if the ConfigurationRowNames have changed + /// since the last evaluation of this property. + /// </summary> + public bool ConfigurationRowNamesChanged + { + get + { + var ret = false; + if (lastConfigurationRowNames == null) { + lastConfigurationRowNames = envPro.ConfigurationManager.ConfigurationRowNames as Array; + } else { + var currentConfigurationRowNames = envPro.ConfigurationManager.ConfigurationRowNames as Array; + if (!HelperFunctions.ArraysEqual(lastConfigurationRowNames, currentConfigurationRowNames)) { + lastConfigurationRowNames = currentConfigurationRowNames; + ret = true; + } + } + return ret; + } + } + + /// <summary> + /// Returns the file name of the generated ui header file relative to + /// the project directory. + /// </summary> + /// <param name="uiFile">name of the ui file</param> + public string GetUiGeneratedFileName(string uiFile) + { + var fi = new FileInfo(uiFile); + var file = fi.Name; + if (HelperFunctions.IsUicFile(file)) { + return QtVSIPSettings.GetUicDirectory(envPro) + + "\\ui_" + file.Remove(file.Length - 3, 3) + ".h"; + } + return null; + } + + /// <summary> + /// Returns the moc-generated file name for the given source or header file. + /// </summary> + /// <param name="file">header or source file in the project</param> + /// <returns></returns> + private static string GetMocFileName(string file) + { + var fi = new FileInfo(file); + + var name = fi.Name; + if (HelperFunctions.IsHeaderFile(fi.Name)) + return "moc_" + name.Substring(0, name.LastIndexOf('.')) + ".cpp"; + if (HelperFunctions.IsSourceFile(fi.Name)) + return name.Substring(0, name.LastIndexOf('.')) + ".moc"; + return null; + } + + /// <summary> + /// Returns the file name of the generated moc file relative to the + /// project directory. + /// </summary> + /// The directory of the moc file depends on the file configuration. + /// Every appearance of "$(ConfigurationName)" in the path will be + /// replaced by the value of configName. + /// <param name="file">full file name of either the header or the source file</param> + /// <returns></returns> + private string GetRelativeMocFilePath(string file, string configName, string platformName) + { + var fileName = GetMocFileName(file); + if (fileName == null) + return null; + var mocDir = QtVSIPSettings.GetMocDirectory(envPro, configName, platformName, file) + + "\\" + fileName; + if (HelperFunctions.IsAbsoluteFilePath(mocDir)) + mocDir = HelperFunctions.GetRelativePath(vcPro.ProjectDirectory, mocDir); + return mocDir; + } + + /// <summary> + /// Returns the file name of the generated moc file relative to the + /// project directory. + /// </summary> + /// The returned file path may contain the macros $(ConfigurationName) and $(PlatformName). + /// <param name="file">full file name of either the header or the source file</param> + /// <returns></returns> + private string GetRelativeMocFilePath(string file) + { + return GetRelativeMocFilePath(file, null, null); + } + + /// <summary> + /// Marks the specified project as a Qt project. + /// </summary> + public void MarkAsQtProject() + { + vcPro.keyword = string.Format("{0}_v{1}", + Resources.qtProjectKeyword, Resources.qtProjectFormatVersion); + } + + public static int GetFormatVersion(VCProject vcPro) + { + if (vcPro == null) + return 0; + if (vcPro.keyword.StartsWith(Resources.qtProjectKeyword, + StringComparison.InvariantCultureIgnoreCase)) { + return Convert.ToInt32(vcPro.keyword.Substring(6)); + } else if (vcPro.keyword.StartsWith(Resources.qtProjectV2Keyword, + StringComparison.InvariantCultureIgnoreCase)) { + return 200; + } else { + return 0; + } + } + + public static int GetFormatVersion(Project pro) + { + if (pro == null) + return 0; + return GetFormatVersion(pro.Object as VCProject); + } + + public int FormatVersion { get { return GetFormatVersion(Project); } } + + public string GetPropertyValue(string propName) + { + return GetPropertyValue(Project, propName); + } + + public string GetPropertyValue(string configName, string platformName, string propName) + { + return GetPropertyValue(Project, configName, platformName, propName); + } + + public static string GetPropertyValue( + EnvDTE.Project dteProject, + string propName) + { + var activeConfig = dteProject.ConfigurationManager?.ActiveConfiguration; + if (activeConfig == null) + return null; + return GetPropertyValue( + dteProject, activeConfig, propName); + } + + public static string GetPropertyValue( + EnvDTE.Project dteProject, + EnvDTE.Configuration dteConfig, + string propName) + { + if (dteProject == null || dteConfig == null) + return null; + return GetPropertyValue( + dteProject.Object as VCProject, + dteConfig.ConfigurationName, + dteConfig.PlatformName, + propName); + } + + public static string GetPropertyValue( + EnvDTE.Project dteProject, + string configName, + string platformName, + string propName) + { + return GetPropertyValue( + dteProject.Object as VCProject, configName, platformName, propName); + } + + public static string GetPropertyValue( + VCProject vcProject, + string configName, + string platformName, + string propName) + { + var vcConfigs = vcProject.Configurations as IVCCollection; + if (vcConfigs == null) + return null; + var configId = string.Format("{0}|{1}", + configName, platformName); + var vcConfig = vcConfigs.Item(configId) as VCConfiguration; + if (vcConfig == null) + return null; + return GetPropertyValue(vcConfig, propName); + } + + public static string GetPropertyValue( + VCConfiguration vcConfig, + string propName) + { + return vcConfig.GetEvaluatedPropertyValue(propName); + } + + public void AddDefine(string define, uint bldConf) + { + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var compiler = CompilerToolWrapper.Create(config); + + if (((!IsDebugConfiguration(config)) && ((bldConf & BuildConfig.Release) != 0)) || + ((IsDebugConfiguration(config)) && ((bldConf & BuildConfig.Debug) != 0))) { + compiler.AddPreprocessorDefinition(define); + } + } + } + + public void AddModule(int id) + { + if (HasModule(id)) + return; + + var vm = QtVersionManager.The(); + var versionInfo = vm.GetVersionInfo(Project); + if (versionInfo == null) + versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion()); + + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + + var info = QtModules.Instance.Module(id); + if (FormatVersion >= Resources.qtMinFormatVersion_Settings) { + var config3 = config as VCConfiguration3; + if (config3 == null) + continue; + if (!string.IsNullOrEmpty(info.proVarQT)) { + var qtModulesValue = config.GetUnevaluatedPropertyValue("QtModules"); + var qtModules = new HashSet<string>( + !string.IsNullOrEmpty(qtModulesValue) + ? qtModulesValue.Split(';') + : new string[] { }); + qtModules.UnionWith(info.proVarQT.Split(' ')); + config3.SetPropertyValue(Resources.projLabelQtSettings, true, + "QtModules", string.Join(";", qtModules)); + } + // In V3 project format, compiler and linker options + // required by modules are set by Qt/MSBuild. + continue; + } + + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + + if (compiler != null) { + foreach (var define in info.Defines) + compiler.AddPreprocessorDefinition(define); + + var incPathList = info.GetIncludePath(); + foreach (var incPath in incPathList) + compiler.AddAdditionalIncludeDirectories(incPath); + } + if (linker != null) { + var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo); + var linkerWrapper = new LinkerToolWrapper(linker); + var additionalDeps = linkerWrapper.AdditionalDependencies; + var dependenciesChanged = false; + if (additionalDeps == null || additionalDeps.Count == 0) { + additionalDeps = moduleLibs; + dependenciesChanged = true; + } else { + foreach (var moduleLib in moduleLibs) { + if (!additionalDeps.Contains(moduleLib)) { + additionalDeps.Add(moduleLib); + dependenciesChanged = true; + } + } + } + if (dependenciesChanged) + linkerWrapper.AdditionalDependencies = additionalDeps; + } + } + } + + public void RemoveModule(int id) + { + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + + var info = QtModules.Instance.Module(id); + if (compiler != null) { + foreach (var define in info.Defines) + compiler.RemovePreprocessorDefinition(define); + var additionalIncludeDirs = compiler.AdditionalIncludeDirectories; + if (additionalIncludeDirs != null) { + var lst = new List<string>(additionalIncludeDirs); + foreach (var includePath in info.IncludePath) { + lst.Remove(includePath); + lst.Remove('\"' + includePath + '\"'); + } + compiler.AdditionalIncludeDirectories = lst; + } + } + if (linker != null && linker.AdditionalDependencies != null) { + var linkerWrapper = new LinkerToolWrapper(linker); + var vm = QtVersionManager.The(); + var versionInfo = vm.GetVersionInfo(Project); + if (versionInfo == null) + versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion()); + + var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo); + var additionalDependencies = linkerWrapper.AdditionalDependencies; + var dependenciesChanged = false; + foreach (var moduleLib in moduleLibs) + dependenciesChanged |= additionalDependencies.Remove(moduleLib); + if (dependenciesChanged) + linkerWrapper.AdditionalDependencies = additionalDependencies; + } + } + } + + public void UpdateModules(VersionInformation oldVersion, VersionInformation newVersion) + { + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + + if (linker != null) { + if (oldVersion == null) { + var linkerWrapper = new LinkerToolWrapper(linker); + var additionalDependencies = linkerWrapper.AdditionalDependencies; + + var libsDesktop = new List<string>(); + foreach (var module in QtModules.Instance.GetAvailableModules()) { + if (HasModule(module.Id)) + libsDesktop.AddRange(module.AdditionalLibraries); + } + var libsToAdd = libsDesktop; + + var changed = false; + foreach (var libToAdd in libsToAdd) { + if (!additionalDependencies.Contains(libToAdd)) { + additionalDependencies.Add(libToAdd); + changed = true; + } + } + if (changed) + linkerWrapper.AdditionalDependencies = additionalDependencies; + } + + if (newVersion.qtMajor >= 5) { + var compiler = CompilerToolWrapper.Create(config); + if (compiler != null) + compiler.RemovePreprocessorDefinition("QT_DLL"); + continue; + } + + if (oldVersion == null || newVersion.IsStaticBuild() != oldVersion.IsStaticBuild()) { + var compiler = CompilerToolWrapper.Create(config); + if (newVersion.IsStaticBuild()) { + if (compiler != null) + compiler.RemovePreprocessorDefinition("QT_DLL"); + } else { + if (compiler != null) + compiler.AddPreprocessorDefinition("QT_DLL"); + } + } + } + } + } + + public bool HasModule(int id) + { + var foundInIncludes = false; + var foundInLibs = false; + + var vm = QtVersionManager.The(); + var versionInfo = vm.GetVersionInfo(Project); + if (versionInfo == null) + versionInfo = vm.GetVersionInfo(vm.GetDefaultVersion()); + if (versionInfo == null) + return false; // neither a default or project Qt version + var info = QtModules.Instance.Module(id); + if (info == null) + return false; + + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + + if (compiler != null) { + if (compiler.GetAdditionalIncludeDirectories() == null) + continue; + var incPathList = info.GetIncludePath(); + var includeDirs = compiler.GetAdditionalIncludeDirectoriesList(); + foundInIncludes = (incPathList.Count > 0); + foreach (var incPath in incPathList) { + var fixedIncludeDir = FixFilePathForComparison(incPath); + if (!includeDirs.Any(dir => + FixFilePathForComparison(dir) == fixedIncludeDir)) { + foundInIncludes = false; + break; + } + } + } + + if (foundInIncludes) + break; + + List<string> libs = null; + if (linker != null) { + var linkerWrapper = new LinkerToolWrapper(linker); + libs = linkerWrapper.AdditionalDependencies; + } + + if (libs != null) { + var moduleLibs = info.GetLibs(IsDebugConfiguration(config), versionInfo); + foundInLibs = moduleLibs.All(moduleLib => libs.Contains(moduleLib)); + } + } + return foundInIncludes || foundInLibs; + } + + public void WriteProjectBasicConfigurations(uint type, bool usePrecompiledHeader) + { + WriteProjectBasicConfigurations(type, usePrecompiledHeader, null); + } + + public void WriteProjectBasicConfigurations(uint type, bool usePrecompiledHeader, VersionInformation vi) + { + var configType = ConfigurationTypes.typeApplication; + var targetExtension = ".exe"; + string qtVersion = null; + var vm = QtVersionManager.The(); + if (vi == null) { + qtVersion = vm.GetDefaultVersion(); + vi = vm.GetVersionInfo(qtVersion); + } + + switch (type & TemplateType.ProjectType) { + case TemplateType.DynamicLibrary: + configType = ConfigurationTypes.typeDynamicLibrary; + targetExtension = ".dll"; + break; + case TemplateType.StaticLibrary: + configType = ConfigurationTypes.typeStaticLibrary; + targetExtension = ".lib"; + break; + } + + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + config.ConfigurationType = configType; + var compiler = CompilerToolWrapper.Create(config); + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + var librarian = (VCLibrarianTool)((IVCCollection)config.Tools).Item("VCLibrarianTool"); + + if (linker != null) { + if ((type & TemplateType.ConsoleSystem) != 0) + linker.SubSystem = subSystemOption.subSystemConsole; + else + linker.SubSystem = subSystemOption.subSystemWindows; + + linker.OutputFile = "$(OutDir)\\$(ProjectName)" + targetExtension; + } else { + librarian.OutputFile = "$(OutDir)\\$(ProjectName)" + targetExtension; + } + + if ((type & TemplateType.PluginProject) != 0) + compiler.AddPreprocessorDefinition("QT_PLUGIN"); + + var isDebugConfiguration = false; + if (config.Name.StartsWith("Release", StringComparison.Ordinal)) { + compiler.SetDebugInformationFormat(debugOption.debugDisabled); + compiler.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDLL; + } else if (config.Name.StartsWith("Debug", StringComparison.Ordinal)) { + isDebugConfiguration = true; + compiler.SetOptimization(optimizeOption.optimizeDisabled); + compiler.SetDebugInformationFormat(debugOption.debugEnabled); + compiler.RuntimeLibrary = runtimeLibraryOption.rtMultiThreadedDebugDLL; + } + + compiler.SetTreatWChar_tAsBuiltInType(true); + + if (linker != null) + linker.GenerateDebugInformation = isDebugConfiguration; + + if (usePrecompiledHeader) + UsePrecompiledHeaders(config); + } + if ((type & TemplateType.PluginProject) != 0) + MarkAsDesignerPluginProject(); + } + + public void MarkAsDesignerPluginProject() + { + Project.Globals["IsDesignerPlugin"] = true.ToString(); + if (!Project.Globals.get_VariablePersists("IsDesignerPlugin")) + Project.Globals.set_VariablePersists("IsDesignerPlugin", true); + } + + public void AddUic4BuildStepMsBuild( + VCFileConfiguration config, + string description, + string outputFile) + { + var file = config.File as VCFile; + if (file != null) + file.ItemType = QtUic.ItemTypeName; + qtMsBuild.SetItemProperty(config, QtUic.Property.ExecutionDescription, description); + qtMsBuild.SetItemProperty(config, QtUic.Property.OutputFile, outputFile); + } + + public void AddUic4BuildStepCustomBuild( + VCFileConfiguration config, + string description, + string outputFile) + { + //SetItemType(config, ItemType.CustomBuild); + var tool = HelperFunctions.GetCustomBuildTool(config); + if (tool != null) { + tool.AdditionalDependencies = Resources.uic4Command; + tool.Description = description; + tool.Outputs = "\"" + outputFile + "\""; + tool.CommandLine = "\"" + Resources.uic4Command + "\" -o \"" + + outputFile + "\" \"" + ProjectMacros.Path + "\""; + } + } + + /// <summary> + /// This function adds a uic4 build step to a given file. + /// </summary> + /// <param name="file">file</param> + public void AddUic4BuildStep(VCFile file) + { + if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_Settings) { + file.ItemType = QtUic.ItemTypeName; + return; + } + + CustomTool toolSettings = + IsQtMsBuildEnabled() ? CustomTool.MSBuildTarget : CustomTool.CustomBuildStep; + + try { + var uiFile = GetUiGeneratedFileName(file.FullPath); + var uiBaseName = file.Name.Remove(file.Name.LastIndexOf('.')); + var uiFileMacro = uiFile.Replace(uiBaseName, ProjectMacros.Name); + var uiFileExists = GetFileFromProject(uiFile) != null; + string description = "Uic'ing " + ProjectMacros.FileName + "..."; + + foreach (VCFileConfiguration config in (IVCCollection)file.FileConfigurations) { + + switch (toolSettings) { + case CustomTool.MSBuildTarget: + AddUic4BuildStepMsBuild(config, description, uiFileMacro); + break; + default: + AddUic4BuildStepCustomBuild(config, description, uiFileMacro); + break; + } + + var conf = config.ProjectConfiguration as VCConfiguration; + var compiler = CompilerToolWrapper.Create(conf); + if (compiler != null && !uiFileExists) { + var uiDir = QtVSIPSettings.GetUicDirectory(envPro); + if (compiler.GetAdditionalIncludeDirectories().IndexOf(uiDir, StringComparison.Ordinal) < 0) + compiler.AddAdditionalIncludeDirectories(uiDir); + } + } + if (toolSettings == CustomTool.CustomBuildStep && !uiFileExists) + AddFileInFilter(Filters.GeneratedFiles(), uiFile); + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotAddUicStep", file.FullPath)); + } + } + + /// <summary> + /// Surrounds the argument by double quotes. + /// Makes sure, that the trailing double quote is not escaped by a backslash. + /// Escapes all quotation mark characters in the argument + /// + /// This must follow the format recognized by CommandLineToArgvW: + /// (https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw) + /// + /// CommandLineToArgvW has a special interpretation of backslash characters when they are + /// followed by a quotation mark character ("). This interpretation assumes that any + /// preceding argument is a valid file system path, or else it may behave unpredictably. + /// + /// This special interpretation controls the "in quotes" mode tracked by the parser. When + /// this mode is off, whitespace terminates the current argument. When on, whitespace is + /// added to the argument like all other characters. + /// + /// * 2n backslashes followed by a quotation mark produce n backslashes followed by + /// begin/end quote. This does not become part of the parsed argument, but toggles the + /// "in quotes" mode. + /// + /// * (2n) + 1 backslashes followed by a quotation mark again produce n backslashes + /// followed by a quotation mark literal ("). This does not toggle the "in quotes" mode. + /// + /// * n backslashes not followed by a quotation mark simply produce n backslashes. + /// + /// </summary> + private static string SafelyQuoteCommandLineArgument(string arg) + { + var quotedArg = new StringBuilder(); + quotedArg.Append("\""); + + // Split argument by quotation mark characters + // All argument parts except the last are followed by a quotation mark character + var argParts = arg.Split(new char[] { '\"' }); + for (int i = 0; i < argParts.Length; ++i) { + var part = argParts[i]; + quotedArg.Append(part); + + // Duplicate backslashes immediately preceding quotation mark character + if (part.EndsWith("\\")) + quotedArg.Append(part.Reverse().TakeWhile(c => c == '\\').ToArray()); + + // Escape all quotation mark characters in argument + if (i < argParts.Length - 1) + quotedArg.Append("\\\""); + } + + quotedArg.Append("\""); + return quotedArg.ToString(); + } + + public string GetDefines(VCFileConfiguration conf) + { + var defines = string.Empty; + var propsFile = conf.Tool as IVCRulePropertyStorage; + var projectConfig = conf.ProjectConfiguration as VCConfiguration; + var propsProject = projectConfig.Rules.Item("CL") as IVCRulePropertyStorage; + if (propsFile != null) { + try { + defines = propsFile.GetUnevaluatedPropertyValue("PreprocessorDefinitions"); + } catch { } + } + if (string.IsNullOrEmpty(defines) && propsProject != null) { + try { + defines = propsProject.GetUnevaluatedPropertyValue("PreprocessorDefinitions"); + } catch { } + } + + if (string.IsNullOrEmpty(defines)) + return string.Empty; + + var defineList = defines.Split( + new char[] { ';' }, + StringSplitOptions.RemoveEmptyEntries) + .ToList(); + + var preprocessorDefines = string.Empty; + var alreadyAdded = new List<string>(); + var rxp = new Regex(@"\s|(\$\()"); + foreach (var define in defineList) { + if (!alreadyAdded.Contains(define)) { + var mustSurroundByDoubleQuotes = rxp.IsMatch(define); + // Yes, a preprocessor definition can contain spaces or a macro name. + // Example: PROJECTDIR=$(InputDir) + + if (mustSurroundByDoubleQuotes) { + preprocessorDefines += " "; + preprocessorDefines += SafelyQuoteCommandLineArgument("-D" + define); + } else { + preprocessorDefines += " -D" + define; + } + alreadyAdded.Add(define); + } + } + return preprocessorDefines; + } + + private string GetIncludes(VCFileConfiguration conf) + { + var includeList = GetIncludesFromCompilerTool(CompilerToolWrapper.Create(conf)); + + var projectConfig = conf.ProjectConfiguration as VCConfiguration; + includeList.AddRange(GetIncludesFromCompilerTool(CompilerToolWrapper.Create(projectConfig))); + + var propertySheets = projectConfig.PropertySheets as IVCCollection; + if (propertySheets != null) { + foreach (VCPropertySheet sheet in propertySheets) + includeList.AddRange(GetIncludesFromPropertySheet(sheet)); + } + + var includes = string.Empty; + var alreadyAdded = new List<string>(); + foreach (var include in includeList) { + if (!alreadyAdded.Contains(include)) { + var incl = HelperFunctions.NormalizeRelativeFilePath(include); + if (incl.Length > 0) + includes += " " + SafelyQuoteCommandLineArgument("-I" + incl); + alreadyAdded.Add(include); + } + } + return includes; + } + + private List<string> GetIncludesFromPropertySheet(VCPropertySheet sheet) + { + var includeList = GetIncludesFromCompilerTool(CompilerToolWrapper.Create(sheet)); + var propertySheets = sheet.PropertySheets as IVCCollection; + if (propertySheets != null) { + foreach (VCPropertySheet subSheet in propertySheets) + includeList.AddRange(GetIncludesFromPropertySheet(subSheet)); + } + return includeList; + } + + private static List<string> GetIncludesFromCompilerTool(CompilerToolWrapper compiler) + { + try { + if (!string.IsNullOrEmpty(compiler.GetAdditionalIncludeDirectories())) { + var includes = compiler.GetAdditionalIncludeDirectoriesList(); + return new List<string>(includes); + } + } catch { } + return new List<string>(); + } + + private static bool IsDebugConfiguration(VCConfiguration conf) + { + var tool = CompilerToolWrapper.Create(conf); + if (tool != null) { + return tool.RuntimeLibrary == runtimeLibraryOption.rtMultiThreadedDebug + || tool.RuntimeLibrary == runtimeLibraryOption.rtMultiThreadedDebugDLL; + } + return false; + } + + private string GetPCHMocOptions(VCFile file, CompilerToolWrapper compiler) + { + // As .moc files are included, we should not add anything there + if (!HelperFunctions.IsHeaderFile(file.Name)) + return string.Empty; + + var additionalMocOptions = "\"-f" + compiler.GetPrecompiledHeaderThrough().Replace('\\', '/') + "\" "; + //Get mocDir without .\\ at the beginning of it + var mocDir = QtVSIPSettings.GetMocDirectory(envPro); + if (mocDir.StartsWith(".\\", StringComparison.Ordinal)) + mocDir = mocDir.Substring(2); + + //Get the absolute path + mocDir = vcPro.ProjectDirectory + mocDir; + var fullPathGeneric = Path.Combine( + Path.GetDirectoryName(file.FullPath), "%(Filename)%(Extension)"); + var relPathToFile = HelperFunctions.GetRelativePath( + mocDir, fullPathGeneric).Replace('\\', '/'); + additionalMocOptions += "\"-f" + relPathToFile + "\""; + return additionalMocOptions; + } + + + void AddMocStepSetBuildExclusions( + VCFile sourceFile, + VCFileConfiguration workFileConfig, + VCFile mocFile) + { + var hasDifferentMocFilePerConfig = + QtVSIPSettings.HasDifferentMocFilePerConfig(envPro); + var hasDifferentMocFilePerPlatform = + QtVSIPSettings.HasDifferentMocFilePerPlatform(envPro); + + var workFile = workFileConfig.File as VCFile; + var mocFileName = GetMocFileName(sourceFile.FullPath); + var mocableIsCPP = HelperFunctions.IsMocFile(mocFileName); + var vcConfig = workFileConfig.ProjectConfiguration as VCConfiguration; + var platform = vcConfig.Platform as VCPlatform; + var platformName = platform.Name; + + if (hasDifferentMocFilePerPlatform && hasDifferentMocFilePerConfig) { + foreach (VCFileConfiguration mocConf + in (IVCCollection)mocFile.FileConfigurations) { + var projectCfg = mocConf.ProjectConfiguration as VCConfiguration; + if (projectCfg.Name != vcConfig.Name + || (IsMoccedFileIncluded(sourceFile) && !mocableIsCPP)) { + if (!mocConf.ExcludedFromBuild) + mocConf.ExcludedFromBuild = true; + } else { + if (mocConf.ExcludedFromBuild != workFileConfig.ExcludedFromBuild) + mocConf.ExcludedFromBuild = workFileConfig.ExcludedFromBuild; + } + } + } else if (hasDifferentMocFilePerPlatform) { + foreach (VCFileConfiguration mocConf + in (IVCCollection)mocFile.FileConfigurations) { + var projectCfg = mocConf.ProjectConfiguration as VCConfiguration; + var mocConfPlatform = projectCfg.Platform as VCPlatform; + if (projectCfg.ConfigurationName != vcConfig.ConfigurationName) + continue; + + var exclude = mocConfPlatform.Name != platformName + || (IsMoccedFileIncluded(sourceFile) && !mocableIsCPP); + if (exclude) { + if (mocConf.ExcludedFromBuild != exclude) + mocConf.ExcludedFromBuild = exclude; + } else { + if (mocConf.ExcludedFromBuild != workFileConfig.ExcludedFromBuild) + mocConf.ExcludedFromBuild = workFileConfig.ExcludedFromBuild; + } + } + } else if (hasDifferentMocFilePerConfig) { + foreach (VCFileConfiguration mocConf + in (IVCCollection)mocFile.FileConfigurations) { + var projectCfg = mocConf.ProjectConfiguration as VCConfiguration; + var mocConfPlatform = projectCfg.Platform as VCPlatform; + if (platformName != mocConfPlatform.Name) + continue; + if (projectCfg.Name != vcConfig.Name + || (IsMoccedFileIncluded(sourceFile))) { + if (!mocConf.ExcludedFromBuild) + mocConf.ExcludedFromBuild = true; + } else { + if (mocConf.ExcludedFromBuild != workFileConfig.ExcludedFromBuild) + mocConf.ExcludedFromBuild = workFileConfig.ExcludedFromBuild; + } + } + } else { + var moccedFileConfig = GetVCFileConfigurationByName( + mocFile, + workFileConfig.Name); + if (moccedFileConfig != null) { + var cppFile = GetCppFileForMocStep(sourceFile); + if (cppFile != null && IsMoccedFileIncluded(cppFile)) { + if (!moccedFileConfig.ExcludedFromBuild) + moccedFileConfig.ExcludedFromBuild = true; + } else if (moccedFileConfig.ExcludedFromBuild + != workFileConfig.ExcludedFromBuild) { + moccedFileConfig.ExcludedFromBuild = workFileConfig.ExcludedFromBuild; + } + } + } + } + + void AddMocStepCustomBuild( + VCFile sourceFile, + VCFileConfiguration workFileConfig, + VCFile mocFile, + string defines, + string includes, + string description) + { + var workFile = workFileConfig.File as VCFile; + var mocFileName = GetMocFileName(sourceFile.FullPath); + var mocableIsCPP = HelperFunctions.IsMocFile(mocFileName); + var vcConfig = workFileConfig.ProjectConfiguration as VCConfiguration; + + workFile.ItemType = "CustomBuild"; + + VCCustomBuildTool tool = HelperFunctions.GetCustomBuildTool(workFileConfig); + string fileToMoc = null; + if (mocableIsCPP) { + fileToMoc = HelperFunctions.GetRelativePath( + vcPro.ProjectDirectory, + sourceFile.FullPath); + } else { + fileToMoc = ProjectMacros.Path; + } + if (tool == null) + throw new QtVSException( + SR.GetString("QtProject_CannotFindCustomBuildTool", + workFile.FullPath)); + + var dps = tool.AdditionalDependencies; + if (dps.IndexOf("\"" + Resources.moc4Command + "\"", StringComparison.Ordinal) < 0) { + if (dps.Length > 0 && !dps.EndsWith(";", StringComparison.Ordinal)) + dps += ";"; + tool.AdditionalDependencies = dps + "\"" + + Resources.moc4Command + "\";" + fileToMoc; + } + + tool.Description = description; + + var baseFileName = sourceFile.Name.Remove(sourceFile.Name.LastIndexOf('.')); + var outputMocFile = string.Empty; + var outputMocMacro = string.Empty; + + var inputMocFile = ProjectMacros.Path; + if (mocableIsCPP) + inputMocFile = sourceFile.RelativePath; + var output = tool.Outputs; + var pattern = "(\"(.*\\\\" + mocFileName + ")\"|(\\S*" + + mocFileName + "))"; + var regExp = new Regex(pattern); + var matchList = regExp.Matches(tool.Outputs.Replace(ProjectMacros.Name, baseFileName)); + if (matchList.Count > 0) { + if (matchList[0].Length > 0) + outputMocFile = matchList[0].ToString(); + else if (matchList[1].Length > 1) + outputMocFile = matchList[1].ToString(); + + if (outputMocFile.StartsWith("\"", StringComparison.Ordinal)) + outputMocFile = outputMocFile.Substring(1); + if (outputMocFile.EndsWith("\"", StringComparison.Ordinal)) + outputMocFile = outputMocFile.Substring(0, outputMocFile.Length - 1); + var outputMocPath = Path.GetDirectoryName(outputMocFile); + var stringToReplace = Path.GetFileName(outputMocFile); + outputMocMacro = + outputMocPath + + "\\" + + stringToReplace.Replace(baseFileName, ProjectMacros.Name); + } else { + outputMocFile = GetRelativeMocFilePath(sourceFile.FullPath); + var outputMocPath = Path.GetDirectoryName(outputMocFile); + var stringToReplace = Path.GetFileName(outputMocFile); + outputMocMacro = + outputMocPath + + "\\" + + stringToReplace.Replace(baseFileName, ProjectMacros.Name); + if (output.Length > 0 && !output.EndsWith(";", StringComparison.Ordinal)) + output += ";"; + tool.Outputs = output + "\"" + outputMocMacro + "\""; + } + + var newCmdLine = "\"" + Resources.moc4Command + "\" " + + QtVSIPSettings.GetMocOptions(envPro) + + " \"" + inputMocFile + "\" -o \"" + + outputMocMacro + "\""; + + //Tell moc to include the PCH header if we are using precompiled headers in the project + var compiler = CompilerToolWrapper.Create(vcConfig); + if (compiler.GetUsePrecompiledHeader() != pchOption.pchNone) + newCmdLine += " " + GetPCHMocOptions(sourceFile, compiler); + + var versionManager = QtVersionManager.The(); + var versionInfo = VersionInformation.Get(versionManager.GetInstallPath(envPro)); + var mocSupportsIncludes = (versionInfo.qtMajor == 4 && versionInfo.qtMinor >= 2) + || versionInfo.qtMajor >= 5; + + var strDefinesIncludes = defines + includes; + var cmdLineLength = newCmdLine.Length + strDefinesIncludes.Length + 1; + + if (cmdLineLength > HelperFunctions.GetMaximumCommandLineLength() + && mocSupportsIncludes) { + // Command line is too long. We must use an options file. + var mocIncludeCommands = string.Empty; + var mocIncludeFile = "\"" + outputMocFile + ".inc\""; + var redirectOp = " > "; + var maxCmdLineLength = + HelperFunctions.GetMaximumCommandLineLength() + - (mocIncludeFile.Length + 1); + + var options = defines.Split( + new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + var matches = Regex.Matches(includes, "([\"])(?:(?=(\\\\?))\\2.)*?\\1"); + foreach (Match match in matches) { + options.Add(match.Value); + includes = includes.Replace( + match.Value, string.Empty, StringComparison.Ordinal); + } + options.AddRange( + includes.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); + + // Since 5.2.0, MOC uses QCommandLineParser and parses the content of + // the moc_*.inc file differently. For example, "-I.\foo\bar" results + // in an error message, because the parser thinks it got an additional + // positional argument. Change the option into a format MOC understands. + if (versionInfo.qtMajor == 5 && versionInfo.qtMinor >= 2) { + for (var o = 0; o < options.Count; ++o) + options[o] = Regex.Replace(options[o], "\"(-I|-D)", "$1=\""); + } + + var i = options.Count - 1; + for (; i >= 0; --i) { + if (options[i].Length == 0) + continue; + mocIncludeCommands += + "echo " + options[i] + redirectOp + mocIncludeFile + "\r\n"; + cmdLineLength -= options[i].Length + 1; + if (cmdLineLength < maxCmdLineLength) + break; + if (i == options.Count - 1) // first loop + redirectOp = " >> "; + } + strDefinesIncludes = "@" + mocIncludeFile; + for (var k = 0; k < i; ++k) { + if (options[k].Length > 0) + strDefinesIncludes += " " + options[k]; + } + newCmdLine = mocIncludeCommands + newCmdLine + " " + strDefinesIncludes; + } else { + newCmdLine = newCmdLine + " " + strDefinesIncludes; + } + + if (tool.CommandLine.Trim().Length > 0) { + var cmdLine = tool.CommandLine; + + // remove the moc option file commands + { + var rex = new Regex("^echo.+[.](moc|cpp)[.]inc\"\r\n", RegexOptions.Multiline); + cmdLine = rex.Replace(cmdLine, string.Empty); + } + + var m = Regex.Match(cmdLine, @"(\S*moc.exe|""\S+:\\\.*moc.exe"")"); + if (m.Success) { + var start = m.Index; + var end = cmdLine.IndexOf("&&", start, StringComparison.Ordinal); + var a = cmdLine.IndexOf("\r\n", start, StringComparison.Ordinal); + if (a > -1 && (a < end || end < 0)) + end = a; + if (end < 0) + end = cmdLine.Length; + tool.CommandLine = cmdLine.Replace( + cmdLine.Substring(start, end - start), newCmdLine); + } else { + tool.CommandLine = cmdLine + "\r\n" + newCmdLine; + } + } else { + tool.CommandLine = newCmdLine; + } + } + + void AddMocStepMsBuildTarget( + VCFile sourceFile, + VCFileConfiguration workConfig, + string defines, + string includes, + string description) + { + var baseFileName = sourceFile.Name.Remove(sourceFile.Name.LastIndexOf('.')); + var outputMocFile = GetRelativeMocFilePath(sourceFile.FullPath); + var outputMocPath = Path.GetDirectoryName(outputMocFile); + var stringToReplace = Path.GetFileName(outputMocFile); + var outputMocMacro = outputMocPath + "\\" + + stringToReplace.Replace(baseFileName, ProjectMacros.Name); + + sourceFile.ItemType = QtMoc.ItemTypeName; + qtMsBuild.SetItemProperty(workConfig, + QtMoc.Property.InputFile, ProjectMacros.Path); + qtMsBuild.SetItemProperty(workConfig, + QtMoc.Property.OutputFile, outputMocMacro); + if (!HelperFunctions.IsSourceFile(sourceFile.FullPath)) { + qtMsBuild.SetItemProperty(workConfig, + QtMoc.Property.DynamicSource, "output"); + } else { + qtMsBuild.SetItemProperty(workConfig, + QtMoc.Property.DynamicSource, "input"); + } + qtMsBuild.SetItemProperty(workConfig, + QtMoc.Property.ExecutionDescription, description); + qtMsBuild.SetQtMocCommandLine(workConfig, + QtMoc.ToolExecName + " " + defines + " " + includes, + new VCMacroExpander(workConfig)); + } + + void AddMocStepToConfiguration( + VCFile sourceFile, + VCFileConfiguration workConfig, + CustomTool toolSettings) + { + var workFile = workConfig.File as VCFile; + var mocFileName = GetMocFileName(sourceFile.FullPath); + var mocableIsCPP = HelperFunctions.IsMocFile(mocFileName); + var vcConfig = workConfig.ProjectConfiguration as VCConfiguration; + var platform = vcConfig.Platform as VCPlatform; + var platformName = platform.Name; + + var mocRelPath = GetRelativeMocFilePath( + sourceFile.FullPath, + vcConfig.ConfigurationName, + platformName); + string subfilterName = null; + if (mocRelPath.Contains(vcConfig.ConfigurationName)) + subfilterName = vcConfig.ConfigurationName; + if (mocRelPath.Contains(platformName)) { + if (subfilterName != null) + subfilterName += '_'; + subfilterName += platformName; + } + + VCFile mocFile = null; + if (toolSettings == CustomTool.CustomBuildStep) { + mocFile = GetFileFromProject(mocRelPath); + if (mocFile == null) { + var fi = new FileInfo(VCProject.ProjectDirectory + "\\" + mocRelPath); + if (!fi.Directory.Exists) + fi.Directory.Create(); + mocFile = AddFileInSubfilter(Filters.GeneratedFiles(), subfilterName, + mocRelPath); + } + if (mocFile != null) { + if (mocableIsCPP) + mocFile.ItemType = "None"; + else + AddMocStepSetBuildExclusions(sourceFile, workConfig, mocFile); + } + } + + VCFile cppPropertyFile = null; + if (mocableIsCPP) + cppPropertyFile = sourceFile; + else if (mocFile != null) + cppPropertyFile = GetCppFileForMocStep(sourceFile); + VCFileConfiguration defineIncludeConfig; + if (cppPropertyFile != null) { + defineIncludeConfig = GetVCFileConfigurationByName( + cppPropertyFile, + workConfig.Name); + } else { + // No file specific defines/includes + // but at least the project defines/includes are added + defineIncludeConfig = workConfig; + } + var defines = GetDefines(defineIncludeConfig); + var includes = GetIncludes(defineIncludeConfig); + var description = "Moc'ing %(Identity)..."; + + if (toolSettings == CustomTool.MSBuildTarget) { + AddMocStepMsBuildTarget( + sourceFile, + workConfig, + defines, + includes, + description); + } else { + AddMocStepCustomBuild( + sourceFile, + workConfig, + mocFile, + defines, + includes, + description); + } + } + + public enum CustomTool { CustomBuildStep, MSBuildTarget }; + + /// <summary> + /// Adds a moc step to a given file for this project. + /// </summary> + /// <param name="file">file</param> + public void AddMocStep(VCFile file) + { + if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_Settings) { + file.ItemType = QtMoc.ItemTypeName; + if (HelperFunctions.IsSourceFile(file.FullPath)) { + foreach (VCFileConfiguration config in (IVCCollection)file.FileConfigurations) { + qtMsBuild.SetItemProperty(config, QtMoc.Property.DynamicSource, "input"); + qtMsBuild.SetItemPropertyByName(config, "QtMocFileName", "%(Filename).moc"); + } + } + return; + } + + CustomTool toolSettings = + IsQtMsBuildEnabled() ? CustomTool.MSBuildTarget : CustomTool.CustomBuildStep; + + try { + var mocFileName = GetMocFileName(file.FullPath); + if (mocFileName == null) + return; + + var mocableIsCPP = HelperFunctions.IsMocFile(mocFileName); + + VCFile sourceFile = file; + if (mocableIsCPP && toolSettings != CustomTool.MSBuildTarget) { + string cbtFullPath = Path.ChangeExtension(file.FullPath, ".cbt"); + File.WriteAllText(cbtFullPath, string.Format( + "This is a dummy file needed to create {0}", mocFileName)); + file = AddFileInSubfilter(Filters.GeneratedFiles(), null, cbtFullPath, true); + var mocFileItem = file.Object as ProjectItem; + if (mocFileItem != null) + HelperFunctions.EnsureCustomBuildToolAvailable(mocFileItem); + } + + foreach (VCFileConfiguration config in (IVCCollection)file.FileConfigurations) + AddMocStepToConfiguration(sourceFile, config, toolSettings); + + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotAddMocStep", file.FullPath)); + } + } + + /// <summary> + /// Parses the given file to find an occurrence of a moc.exe generated file include. If + /// the given file is a header file, the function tries to find the corresponding source + /// file to use it instead of the header file. Helper function for AddMocStep. + /// </summary> + /// <param name="vcFile">Header or source file name.</param> + /// <returns> + /// Returns true if the file contains an include of the corresponding moc_xxx.cpp file; + /// otherwise returns false. + /// </returns> + public bool IsMoccedFileIncluded(VCFile vcFile) + { + var fullPath = vcFile.FullPath; + if (HelperFunctions.IsHeaderFile(fullPath)) + fullPath = Path.ChangeExtension(fullPath, ".cpp"); + + if (HelperFunctions.IsSourceFile(fullPath)) { + vcFile = GetFileFromProject(fullPath); + if (vcFile == null) + return false; + + fullPath = vcFile.FullPath; + var mocFile = "moc_" + Path.GetFileNameWithoutExtension(fullPath) + ".cpp"; + +#if TODO + // TODO: Newly created projects need a manual solution rescan if we access the + // code model too early, right now it fails to properly parse the created files. + + // Try reusing the vc file code model, + var projectItem = vcFile.Object as ProjectItem; + if (projectItem != null) { + var vcFileCodeModel = projectItem.FileCodeModel as VCFileCodeModel; + if (vcFileCodeModel != null) { + foreach (VCCodeInclude include in vcFileCodeModel.Includes) { + if (include.FullName == mocFile) + return true; + } + return false; + } + } + + // if we fail, we parse the file on our own... +#endif + CxxStreamReader cxxStream = null; + try { + var line = string.Empty; + cxxStream = new CxxStreamReader(fullPath); + while ((line = cxxStream.ReadLine()) != null) { + if (Regex.IsMatch(line, "#include *(<|\")" + mocFile + "(\"|>)")) + return true; + } + } catch { } finally { + if (cxxStream != null) + cxxStream.Dispose(); + } + } + return false; + } + + public bool HasMocStep(VCFile file, string mocOutDir = null) + { + if (file.ItemType == QtMoc.ItemTypeName) + return true; + + if (HelperFunctions.IsHeaderFile(file.Name)) + return CheckForCommand(file, "moc.exe"); + + if (HelperFunctions.IsSourceFile(file.Name)) { + return (HasCppMocFiles(file)); + } + return false; + } + + public static bool HasUicStep(VCFile file) + { + if (file.ItemType == QtUic.ItemTypeName) + return true; + return CheckForCommand(file, Resources.uic4Command); + } + + private static bool CheckForCommand(VCFile file, string cmd) + { + if (file == null) + return false; + foreach (VCFileConfiguration config in (IVCCollection)file.FileConfigurations) { + var tool = HelperFunctions.GetCustomBuildTool(config); + if (tool == null) + return false; + if (tool.CommandLine != null && tool.CommandLine.Contains(cmd)) + return true; + } + return false; + } + + public void RefreshRccSteps() + { + Messages.Print("\r\n=== Update rcc steps ==="); + var files = GetResourceFiles(); + + var vcFilter = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier); + if (vcFilter != null) { + var filterFiles = GetAllFilesFromFilter(vcFilter); + var filesToDelete = new List<VCFile>(); + foreach (VCFile rmFile in filterFiles) { + if (rmFile.Name.StartsWith("qrc_", StringComparison.OrdinalIgnoreCase)) + filesToDelete.Add(rmFile); + } + foreach (var rmFile in filesToDelete) { + RemoveFileFromFilter(rmFile, vcFilter); + HelperFunctions.DeleteEmptyParentDirs(rmFile); + } + } + + qtMsBuild.BeginSetItemProperties(); + foreach (var file in files) { + Messages.Print("Update rcc step for " + file.Name + "."); + var options = new RccOptions(envPro, file); + UpdateRccStep(file, options); + } + qtMsBuild.EndSetItemProperties(); + + Messages.Print("\r\n=== " + files.Count + " rcc steps updated. ===\r\n"); + } + + public void RefreshRccSteps(string oldRccDir) + { + RefreshRccSteps(); + UpdateCompilerIncludePaths(oldRccDir, QtVSIPSettings.GetRccDirectory(envPro)); + } + + public void UpdateRccStepMsBuild( + VCFileConfiguration vfc, + RccOptions rccOpts, + string filesInQrcFile, + string nameOnly, + string qrcCppFile) + { + var file = vfc.File as VCFile; + if (file != null) + file.ItemType = QtRcc.ItemTypeName; + qtMsBuild.SetItemProperty(vfc, + QtRcc.Property.ExecutionDescription, "Rcc'ing " + ProjectMacros.FileName + "..."); + qtMsBuild.SetItemProperty(vfc, + QtRcc.Property.OutputFile, qrcCppFile.Replace(nameOnly, ProjectMacros.Name)); + } + + public void UpdateRccStepCustomBuild( + VCFileConfiguration vfc, + RccOptions rccOpts, + string filesInQrcFile, + string nameOnly, + string qrcCppFile) + { + var qrcFile = vfc.File as VCFile; + var rccOptsCfg = rccOpts; + var cmdLine = string.Empty; + + var cbt = HelperFunctions.GetCustomBuildTool(vfc); + + cbt.AdditionalDependencies = filesInQrcFile; + + cbt.Description = "Rcc'ing " + ProjectMacros.FileName + "..."; + + cbt.Outputs = qrcCppFile.Replace(nameOnly, ProjectMacros.Name); + + cmdLine += "\"" + Resources.rcc4Command + "\"" + + " -name \"" + ProjectMacros.Name + "\""; + + if (rccOptsCfg == null) + rccOptsCfg = HelperFunctions.ParseRccOptions(cbt.CommandLine, qrcFile); + + if (rccOptsCfg.CompressFiles) { + cmdLine += " -threshold " + rccOptsCfg.CompressThreshold; + cmdLine += " -compress " + rccOptsCfg.CompressLevel; + } else { + cmdLine += " -no-compress"; + } + + cbt.CommandLine = cmdLine + " \"" + ProjectMacros.Path + "\" -o " + cbt.Outputs; + } + + public void UpdateRccStep(VCFile qrcFile, RccOptions rccOpts) + { + if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_Settings) { + qrcFile.ItemType = QtRcc.ItemTypeName; + return; + } + + CustomTool toolSettings = + IsQtMsBuildEnabled() ? CustomTool.MSBuildTarget : CustomTool.CustomBuildStep; + + var vcpro = (VCProject)qrcFile.project; + var dteObject = ((Project)vcpro.Object).DTE; + + var qtPro = Create(vcpro); + var parser = new QrcParser(qrcFile.FullPath); + var filesInQrcFile = ProjectMacros.Path; + + if (parser.parse()) { + var fi = new FileInfo(qrcFile.FullPath); + var qrcDir = fi.Directory.FullName + "\\"; + + foreach (var prfx in parser.Prefixes) { + foreach (var itm in prfx.Items) { + var relativeQrcItemPath = HelperFunctions.GetRelativePath( + vcPro.ProjectDirectory, + qrcDir + itm.Path); + filesInQrcFile += ";" + relativeQrcItemPath; + try { + var addedFile = qtPro.AddFileInFilter( + Filters.ResourceFiles(), + relativeQrcItemPath, true); + ExcludeFromAllBuilds(addedFile); + } catch { /* it's not possible to add all kinds of files */ } + } + } + } + + var nameOnly = HelperFunctions.RemoveFileNameExtension(new FileInfo(qrcFile.FullPath)); + var qrcCppFile = QtVSIPSettings.GetRccDirectory(envPro) + + "\\" + "qrc_" + nameOnly + ".cpp"; + + try { + foreach (VCFileConfiguration vfc in (IVCCollection)qrcFile.FileConfigurations) { + switch (toolSettings) { + case CustomTool.MSBuildTarget: + UpdateRccStepMsBuild( + vfc, + rccOpts, + filesInQrcFile, + nameOnly, + qrcCppFile); + break; + default: + UpdateRccStepCustomBuild( + vfc, + rccOpts, + filesInQrcFile, + nameOnly, + qrcCppFile); + break; + } + } + if (toolSettings == CustomTool.CustomBuildStep) + AddFileInFilter(Filters.GeneratedFiles(), qrcCppFile, true); + } catch (Exception /*e*/) { + Messages.Print("*** WARNING (RCC): Couldn't add rcc step"); + } + } + + static public void ExcludeFromAllBuilds(VCFile file) + { + if (file == null) + return; + foreach (VCFileConfiguration conf in (IVCCollection)file.FileConfigurations) { + if (!conf.ExcludedFromBuild) + conf.ExcludedFromBuild = true; + } + } + + bool IsCppMocFileCustomBuild(VCProject vcProj, VCFile vcFile, VCFile cppFile) + { + var mocFilePath = vcFile.FullPath; + var cppFilePath = cppFile.FullPath; + if (Path.GetDirectoryName(mocFilePath) + != Path.GetDirectoryName(cppFilePath)) { + return false; + } + + if (Path.GetFileNameWithoutExtension(mocFilePath) + != Path.GetFileNameWithoutExtension(cppFilePath)) { + return false; + } + + if (!string.Equals(Path.GetExtension(mocFilePath), ".cbt", + StringComparison.InvariantCultureIgnoreCase)) { + return false; + } + + return true; + } + + List<VCFile> GetCppMocOutputs(List<VCFile> mocFiles) + { + List<VCFile> outputFiles = new List<VCFile>(); + foreach (var mocFile in mocFiles) { + foreach (VCFileConfiguration mocConfig + in (IVCCollection)mocFile.FileConfigurations) { + + var cbtTool = HelperFunctions.GetCustomBuildTool(mocConfig); + if (cbtTool == null) + continue; + foreach (var output in cbtTool.Outputs.Split(new char[] { ';' })) { + string outputExpanded = output; + if (!HelperFunctions.ExpandString(ref outputExpanded, mocConfig)) + continue; + string outputFullPath = ""; + try { + outputFullPath = Path.GetFullPath(Path.Combine( + Path.GetDirectoryName(mocFile.FullPath), + outputExpanded)); + } catch { + continue; + } + var vcFile = GetFileFromProject(outputFullPath); + if (vcFile != null) + outputFiles.Add(vcFile); + } + } + } + return outputFiles; + } + + List<VCFile> GetCppMocFiles(VCFile cppFile) + { + List<VCFile> mocFiles = new List<VCFile>(); + var vcProj = cppFile.project as VCProject; + if (vcProj != null) { + mocFiles.AddRange(from VCFile vcFile + in (IVCCollection)vcProj.Files + where vcFile.ItemType == "CustomBuild" + && IsCppMocFileCustomBuild(vcProj, vcFile, cppFile) + select vcFile); + mocFiles.AddRange(GetCppMocOutputs(mocFiles)); + } + return mocFiles; + } + + bool IsCppMocFileQtMsBuild(VCProject vcProj, VCFile vcFile, VCFile cppFile) + { + foreach (VCFileConfiguration fileConfig in (IVCCollection)vcFile.FileConfigurations) { + string inputFile = qtMsBuild.GetPropertyValue(fileConfig, QtMoc.Property.InputFile); + HelperFunctions.ExpandString(ref inputFile, fileConfig); + if (HelperFunctions.PathIsRelativeTo(inputFile, cppFile.ItemName)) + return true; + } + return false; + } + + bool HasCppMocFiles(VCFile cppFile) + { + if (!IsQtMsBuildEnabled()) { + return File.Exists(Path.ChangeExtension(cppFile.FullPath, ".cbt")); + } else { + var vcProj = cppFile.project as VCProject; + if (vcProj != null) { + foreach (VCFile vcFile in (IVCCollection)vcProj.Files) { + if (vcFile.ItemType == "CustomBuild") { + if (IsCppMocFileCustomBuild(vcProj, vcFile, cppFile)) + return true; + } else if (vcFile.ItemType == QtMoc.ItemTypeName) { + if (IsCppMocFileQtMsBuild(vcProj, vcFile, cppFile)) + return true; + } + } + } + return false; + } + } + + public void RemoveMocStep(VCFile file) + { + if (file.ItemType == QtMoc.ItemTypeName) { + RemoveMocStepQtMsBuild(file); + } else if (HelperFunctions.IsHeaderFile(file.Name)) { + if (file.ItemType == "CustomBuild") + RemoveMocStepCustomBuild(file); + } else { + foreach (VCFile vcFile in (IVCCollection)vcPro.Files) { + if (vcFile.ItemType == QtMoc.ItemTypeName) { + if (IsCppMocFileQtMsBuild(vcPro, vcFile, file)) { + RemoveMocStepQtMsBuild(vcFile); + } + } else if (vcFile.ItemType == "CustomBuild") { + if (IsCppMocFileCustomBuild(vcPro, vcFile, file)) { + RemoveMocStepCustomBuild(file); + return; + } + } + } + } + } + + public void RemoveMocStepQtMsBuild(VCFile file) + { + if (HelperFunctions.IsHeaderFile(file.Name)) { + file.ItemType = "ClInclude"; + } else if (HelperFunctions.IsSourceFile(file.Name)) { + file.ItemType = "ClCompile"; + } else { + file.ItemType = "None"; + } + } + + /// <summary> + /// Removes the custom build step of a given file. + /// </summary> + /// <param name="file">file</param> + public void RemoveMocStepCustomBuild(VCFile file) + { + try { + if (!HasMocStep(file)) + return; + + if (HelperFunctions.IsHeaderFile(file.Name)) { + foreach (VCFileConfiguration config in (IVCCollection)file.FileConfigurations) { + var tool = HelperFunctions.GetCustomBuildTool(config); + if (tool == null) + continue; + + var cmdLine = tool.CommandLine; + if (cmdLine.Length > 0) { + var rex = new Regex(@"(\S*moc.exe|""\S+:\\\.*moc.exe"")"); + while (true) { + var m = rex.Match(cmdLine); + if (!m.Success) + break; + + var start = m.Index; + var end = cmdLine.IndexOf("&&", start, StringComparison.Ordinal); + var a = cmdLine.IndexOf("\r\n", start, StringComparison.Ordinal); + if ((a > -1 && a < end) || (end < 0 && a > -1)) + end = a; + if (end < 0) + end = cmdLine.Length; + + cmdLine = cmdLine.Remove(start, end - start).Trim(); + if (cmdLine.StartsWith("&&", StringComparison.Ordinal)) + cmdLine = cmdLine.Remove(0, 2).Trim(); + } + tool.CommandLine = cmdLine; + } + + var reg = new Regex("Moc'ing .+\\.\\.\\."); + var addDepends = tool.AdditionalDependencies; + addDepends = Regex.Replace(addDepends, + @"(\S*moc.exe|""\S+:\\\.*moc.exe"")", string.Empty); + addDepends = addDepends.Replace(file.RelativePath, string.Empty); + tool.AdditionalDependencies = string.Empty; + tool.Description = reg.Replace(tool.Description, string.Empty); + tool.Description = tool.Description.Replace("MOC " + file.Name, string.Empty); + var baseFileName = file.Name.Remove(file.Name.LastIndexOf('.')); + var pattern = "(\"(.*\\\\" + GetMocFileName(file.FullPath) + + ")\"|(\\S*" + GetMocFileName(file.FullPath) + "))"; + string outputMocFile = null; + var regExp = new Regex(pattern); + tool.Outputs = tool.Outputs.Replace(ProjectMacros.Name, baseFileName); + var matchList = regExp.Matches(tool.Outputs); + if (matchList.Count > 0) { + if (matchList[0].Length > 0) + outputMocFile = matchList[0].ToString(); + else if (matchList[1].Length > 1) + outputMocFile = matchList[1].ToString(); + } + tool.Outputs = Regex.Replace(tool.Outputs, + pattern, string.Empty, RegexOptions.Multiline | RegexOptions.IgnoreCase); + tool.Outputs = Regex.Replace(tool.Outputs, + @"\s*;\s*;\s*", ";", RegexOptions.Multiline); + tool.Outputs = Regex.Replace(tool.Outputs, + @"(^\s*;|\s*;\s*$)", string.Empty, RegexOptions.Multiline); + + if (outputMocFile != null) { + if (outputMocFile.StartsWith("\"", StringComparison.Ordinal)) + outputMocFile = outputMocFile.Substring(1); + if (outputMocFile.EndsWith("\"", StringComparison.Ordinal)) + outputMocFile = outputMocFile.Substring(0, outputMocFile.Length - 1); + HelperFunctions.ExpandString(ref outputMocFile, config); + } + var mocFile = GetFileFromProject(outputMocFile); + if (mocFile != null) + RemoveFileFromFilter(mocFile, Filters.GeneratedFiles()); + } + } else { + foreach (var mocFile in GetCppMocFiles(file)) { + RemoveFileFromFilter(mocFile, Filters.GeneratedFiles()); + } + } + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotRemoveMocStep", file.FullPath)); + } + } + + public List<VCFile> GetResourceFiles() + { + var qrcFiles = new List<VCFile>(); + + foreach (VCFile f in (IVCCollection)VCProject.Files) { + if (HelperFunctions.IsQrcFile(f.Name)) + qrcFiles.Add(f); + } + return qrcFiles; + } + + /// <summary> + /// Returns the file if it can be found, otherwise null. + /// </summary> + /// <param name="filter">filter name</param> + /// <param name="fileName">relative file path to the project</param> + /// <returns></returns> + public VCFile GetFileFromFilter(FakeFilter filter, string fileName) + { + var vcfilter = FindFilterFromGuid(filter.UniqueIdentifier); + + // try with name as well + if (vcfilter == null) + vcfilter = FindFilterFromName(filter.Name); + + if (vcfilter == null) + return null; + + try { + FileInfo fi = null; + if (Path.IsPathRooted(fileName)) + fi = new FileInfo(fileName); + else + fi = new FileInfo(ProjectDir + "\\" + fileName); + + foreach (VCFile file in (IVCCollection)vcfilter.Files) { + if (file.MatchName(fi.FullName, true)) + return file; + } + } catch { } + return null; + } + + /// <summary> + /// Returns the file (VCFile) specified by the file name from a given + /// project. + /// </summary> + /// <param name="fileName">file name (relative path)</param> + /// <returns></returns> + public VCFile GetFileFromProject(string fileName) + { + fileName = HelperFunctions.NormalizeRelativeFilePath(fileName); + + var nf = fileName; + if (!HelperFunctions.IsAbsoluteFilePath(fileName)) + nf = HelperFunctions.NormalizeFilePath(vcPro.ProjectDirectory + "\\" + fileName); + nf = nf.ToLower(); + + foreach (VCFile f in (IVCCollection)vcPro.Files) { + if (f.FullPath.ToLower() == nf) + return f; + } + return null; + } + + /// <summary> + /// Returns the files specified by the file name from a given project as list of VCFile + /// objects. + /// </summary> + /// <param name="fileName">file name (relative path)</param> + /// <returns></returns> + public IEnumerable<VCFile> GetFilesFromProject(string fileName) + { + var fi = new FileInfo(HelperFunctions.NormalizeRelativeFilePath(fileName)); + foreach (VCFile f in (IVCCollection)vcPro.Files) { + if (string.Equals(f.Name, fi.Name, StringComparison.OrdinalIgnoreCase)) + yield return f; + } + } + + private static List<VCFile> GetAllFilesFromFilter(VCFilter filter) + { + var tmpList = ((IVCCollection)filter.Files).Cast<VCFile>().ToList(); + foreach (VCFilter subfilter in (IVCCollection)filter.Filters) + tmpList.AddRange(GetAllFilesFromFilter(subfilter)); + return tmpList; + } + + /// <summary> + /// Adds a file to a filter. If the filter doesn't exist yet, it + /// will be created. (Doesn't check for duplicates) + /// </summary> + /// <param name="filter">fake filter</param> + /// <param name="fileName">relative file name</param> + /// <returns>A VCFile object of the added file.</returns> + public VCFile AddFileInFilter(FakeFilter filter, string fileName) + { + return AddFileInFilter(filter, fileName, false); + } + + public void RemoveItem(ProjectItem item) + { + foreach (ProjectItem tmpFilter in Project.ProjectItems) { + if (tmpFilter.Name == item.Name) { + tmpFilter.Remove(); + return; + } + foreach (ProjectItem tmpItem in tmpFilter.ProjectItems) { + if (tmpItem.Name == item.Name) { + tmpItem.Remove(); + return; + } + } + } + } + + /// <summary> + /// Adds a file to a filter. If the filter doesn't exist yet, it + /// will be created. + /// </summary> + /// <param name="filter">fake filter</param> + /// <param name="fileName">relative file name</param> + /// <param name="checkForDuplicates">true if we don't want duplicated files</param> + /// <returns>A VCFile object of the added file.</returns> + public VCFile AddFileInFilter(FakeFilter filter, string fileName, bool checkForDuplicates) + { + return AddFileInSubfilter(filter, null, fileName, checkForDuplicates); + } + + public VCFile AddFileInSubfilter(FakeFilter filter, string subfilterName, string fileName) + { + return AddFileInSubfilter(filter, subfilterName, fileName, false); + } + + public VCFile AddFileInSubfilter(FakeFilter filter, string subfilterName, string fileName, bool checkForDuplicates) + { + try { + var vfilt = FindFilterFromGuid(filter.UniqueIdentifier); + if (vfilt == null) { + if (!vcPro.CanAddFilter(filter.Name)) { + // check if user already created this filter... then add guid + vfilt = FindFilterFromName(filter.Name); + if (vfilt == null) + throw new QtVSException(SR.GetString("QtProject_CannotAddFilter", filter.Name)); + } else { + vfilt = (VCFilter)vcPro.AddFilter(filter.Name); + } + + vfilt.UniqueIdentifier = filter.UniqueIdentifier; + vfilt.Filter = filter.Filter; + vfilt.ParseFiles = filter.ParseFiles; + } + + if (!string.IsNullOrEmpty(subfilterName)) { + var lowerSubFilterName = subfilterName.ToLower(); + var subfilterFound = false; + foreach (VCFilter subfilt in vfilt.Filters as IVCCollection) { + if (subfilt.Name.ToLower() == lowerSubFilterName) { + vfilt = subfilt; + subfilterFound = true; + break; + } + } + if (subfilterFound) { + // Do filter names differ in upper/lower case? + if (subfilterName != vfilt.Name) { + try { + // Try to rename the filter for aesthetic reasons. + vfilt.Name = subfilterName; + } catch { + // Renaming didn't work. We don't care. + } + } + } + if (!subfilterFound) { + if (!vfilt.CanAddFilter(subfilterName)) + throw new QtVSException(SR.GetString("QtProject_CannotAddFilter", filter.Name)); + +#if !(VS2017 || VS2019 || VS2022) + // TODO: Enable once the freeze gets fixed in VS. + vfilt = (VCFilter)vfilt.AddFilter(subfilterName); + vfilt.Filter = "cpp;moc"; + vfilt.SourceControlFiles = false; +#endif + } + } + + if (checkForDuplicates) { + // check if file exists in filter already + var vcFile = GetFileFromFilter(filter, fileName); + if (vcFile != null) + return vcFile; + } + + if (vfilt.CanAddFile(fileName)) + return (VCFile)(vfilt.AddFile(fileName)); + throw new QtVSException(SR.GetString("QtProject_CannotAddFile", fileName)); + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotAddFile", fileName)); + } + } + + /// <summary> + /// Removes a file from the filter. + /// This file will be deleted! + /// </summary> + /// <param name="file">file</param> + public void RemoveFileFromFilter(VCFile file, FakeFilter filter) + { + try { + var vfilt = FindFilterFromGuid(filter.UniqueIdentifier); + + if (vfilt == null) + vfilt = FindFilterFromName(filter.Name); + + if (vfilt == null) + return; + + RemoveFileFromFilter(file, vfilt); + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotRemoveFile", file.Name)); + } + } + + /// <summary> + /// Removes a file from the filter. + /// This file will be deleted! + /// </summary> + /// <param name="file">file</param> + public void RemoveFileFromFilter(VCFile file, VCFilter filter) + { + try { + filter.RemoveFile(file); + var fi = new FileInfo(file.FullPath); + if (fi.Exists) + fi.Delete(); + } catch { + } + + var subfilters = (IVCCollection)filter.Filters; + for (var i = subfilters.Count; i > 0; i--) { + try { + var subfilter = (VCFilter)subfilters.Item(i); + RemoveFileFromFilter(file, subfilter); + } catch { + } + } + } + + public void MoveFileToDeletedFolder(VCFile vcfile) + { + var srcFile = new FileInfo(vcfile.FullPath); + + if (!srcFile.Exists) + return; + + var destFolder = vcPro.ProjectDirectory + "\\Deleted\\"; + var destName = destFolder + vcfile.Name.Replace(".", "_") + ".bak"; + var fileNr = 0; + + try { + if (!Directory.Exists(destFolder)) + Directory.CreateDirectory(destFolder); + + while (File.Exists(destName)) { + fileNr++; + destName = destName.Substring(0, destName.LastIndexOf('.')) + ".b"; + destName += fileNr.ToString("00"); + } + + srcFile.MoveTo(destName); + } catch (Exception e) { + Messages.DisplayWarningMessage(e, SR.GetString("QtProject_DeletedFolderFullOrProteced")); + } + } + + public VCFilter FindFilterFromName(string filtername) + { + try { + foreach (VCFilter vcfilt in (IVCCollection)vcPro.Filters) { + if (vcfilt.Name.ToLower() == filtername.ToLower()) + return vcfilt; + } + return null; + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotFindFilter")); + } + } + + public VCFilter FindFilterFromGuid(string filterguid) + { + try { + foreach (VCFilter vcfilt in (IVCCollection)vcPro.Filters) { + if (vcfilt.UniqueIdentifier != null + && vcfilt.UniqueIdentifier.ToLower() == filterguid.ToLower()) { + return vcfilt; + } + } + return null; + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotFindFilter")); + } + } + + public VCFilter AddFilterToProject(FakeFilter filter) + { + try { + var vfilt = FindFilterFromGuid(filter.UniqueIdentifier); + if (vfilt == null) { + if (!vcPro.CanAddFilter(filter.Name)) { + vfilt = FindFilterFromName(filter.Name); + if (vfilt == null) + throw new QtVSException(SR.GetString("QtProject_ProjectCannotAddFilter", filter.Name)); + } else { + vfilt = (VCFilter)vcPro.AddFilter(filter.Name); + } + + vfilt.UniqueIdentifier = filter.UniqueIdentifier; + vfilt.Filter = filter.Filter; + vfilt.ParseFiles = filter.ParseFiles; + } + return vfilt; + } catch { + throw new QtVSException(SR.GetString("QtProject_ProjectCannotAddResourceFilter")); + } + } + + public void AddDirectories() + { + try { + // resource directory + var fi = new FileInfo(envPro.FullName); + var dfi = new DirectoryInfo(fi.DirectoryName + "\\" + Resources.resourceDir); + dfi.Create(); + } catch { + throw new QtVSException(SR.GetString("QtProject_CannotCreateResourceDir")); + } + AddFilterToProject(Filters.ResourceFiles()); + } + + public void Finish() + { + try { + var solutionExplorer = dte.Windows.Item(Constants.vsWindowKindSolutionExplorer); + if (solutionExplorer != null) { + var hierarchy = (UIHierarchy)solutionExplorer.Object; + var projects = hierarchy.UIHierarchyItems.Item(1).UIHierarchyItems; + + foreach (UIHierarchyItem itm in projects) { + if (itm.Name == envPro.Name) { + foreach (UIHierarchyItem i in itm.UIHierarchyItems) { + if (i.Name == Filters.GeneratedFiles().Name) + i.UIHierarchyItems.Expanded = false; + } + break; + } + } + } + } catch { } + + ProjectTracker?.AddProject(envPro); + } + + public bool IsDesignerPluginProject() + { + var b = false; + if (Project.Globals.get_VariablePersists("IsDesignerPlugin")) { + var s = (string)Project.Globals["IsDesignerPlugin"]; + try { + b = bool.Parse(s); + } catch { } + } + return b; + } + + /// <summary> + /// Adds a file to a specified filter in a project. + /// </summary> + /// <param name="destName">name of the file in the project (relative to the project directory)</param> + /// <param name="filter">filter</param> + /// <returns>VCFile</returns> + public VCFile AddFileToProject(string destName, FakeFilter filter) + { + VCFile file = null; + if (filter != null) + file = AddFileInFilter(filter, destName); + else + file = (VCFile)vcPro.AddFile(destName); + + if (file == null) + return null; + + if (HelperFunctions.IsHeaderFile(file.Name)) { + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var compiler = CompilerToolWrapper.Create(config); + if (compiler == null) + continue; + + var paths = compiler.GetAdditionalIncludeDirectoriesList(); + var fi = new FileInfo(file.FullPath); + var relativePath = HelperFunctions.GetRelativePath(ProjectDir, fi.Directory.ToString()); + var fixedRelativePath = FixFilePathForComparison(relativePath); + if (!paths.Any(p => FixFilePathForComparison(p) == fixedRelativePath)) + compiler.AddAdditionalIncludeDirectories(relativePath); + } + } + return file; + } + + /// <summary> + /// adjusts the whitespaces, tabs in the given file according to VS settings + /// </summary> + /// <param name="file"></param> + public void AdjustWhitespace(string file) + { + if (!File.Exists(file)) + return; + + // only replace whitespaces in known types + if (!HelperFunctions.IsSourceFile(file) && !HelperFunctions.IsHeaderFile(file) + && !HelperFunctions.IsUicFile(file)) { + return; + } + + try { + var prop = dte.get_Properties("TextEditor", "C/C++"); + var tabSize = Convert.ToInt64(prop.Item("TabSize").Value); + var insertTabs = Convert.ToBoolean(prop.Item("InsertTabs").Value); + + var oldValue = insertTabs ? " " : "\t"; + var newValue = insertTabs ? "\t" : GetWhitespaces(tabSize); + + var list = new List<string>(); + var reader = new StreamReader(file); + var line = reader.ReadLine(); + while (line != null) { + if (line.StartsWith(oldValue, StringComparison.Ordinal)) + line = line.Replace(oldValue, newValue); + list.Add(line); + line = reader.ReadLine(); + } + reader.Close(); + + var writer = new StreamWriter(file); + foreach (var l in list) + writer.WriteLine(l); + writer.Close(); + } catch (Exception e) { + Messages.Print(SR.GetString("QtProject_CannotAdjustWhitespaces", + e.ToString())); + } + } + + private static string GetWhitespaces(long size) + { + var whitespaces = string.Empty; + for (long i = 0; i < size; ++i) + whitespaces += " "; + return whitespaces; + } + + /// <summary> + /// Copy a file to the projects folder. Does not add the file to the project. + /// </summary> + /// <param name="srcFile">full name of the file to add</param> + /// <param name="destFolder">the name of the project folder</param> + /// <param name="destName">name of the file in the project (relative to the project directory)</param> + /// <returns>full name of the destination file</returns> + public static string CopyFileToFolder(string srcFile, string destFolder, string destName) + { + var fullDestName = destFolder + "\\" + destName; + var fi = new FileInfo(fullDestName); + + var replace = true; + if (File.Exists(fullDestName)) { + if (DialogResult.No == MessageBox.Show(SR.GetString("QtProject_FileExistsInProjectFolder", destName) + , SR.GetString("Resources_QtVsTools"), MessageBoxButtons.YesNo, MessageBoxIcon.Question)) { + replace = false; + } + } + + if (replace) { + if (!fi.Directory.Exists) + fi.Directory.Create(); + File.Copy(srcFile, fullDestName, true); + var attribs = File.GetAttributes(fullDestName); + File.SetAttributes(fullDestName, attribs & (~FileAttributes.ReadOnly)); + } + return fi.FullName; + } + + public static void ReplaceTokenInFile(string file, string token, string replacement) + { + var text = string.Empty; + try { + var reader = new StreamReader(file); + text = reader.ReadToEnd(); + reader.Close(); + } catch (Exception e) { + Messages.DisplayErrorMessage( + SR.GetString("QtProject_CannotReplaceTokenRead", token, replacement, e.ToString())); + return; + } + + try { + if (token.ToUpper() == "%PRE_DEF%" && !Char.IsLetter(replacement[0])) + replacement = "_" + replacement; + + text = text.Replace(token, replacement); + var writer = new StreamWriter(file); + writer.Write(text); + writer.Close(); + } catch (Exception e) { + Messages.DisplayErrorMessage( + SR.GetString("QtProject_CannotReplaceTokenWrite", token, replacement, e.ToString())); + } + } + + public void RepairGeneratedFilesStructure() + { + DeleteGeneratedFiles(); + + var files = new ConcurrentBag<VCFile>(); + Task.WaitAll( + Task.Run(() => + Parallel.ForEach(((IVCCollection)vcPro.Files).Cast<VCFile>(), file => + { + var name = file.Name; + if (!HelperFunctions.IsHeaderFile(name) && !HelperFunctions.IsSourceFile(name)) + return; + if (HelperFunctions.HasQObjectDeclaration(file)) + files.Add(file); + }) + ) + ); + + qtMsBuild.BeginSetItemProperties(); + foreach (var file in files) { + RemoveMocStep(file); + AddMocStep(file); + } + qtMsBuild.EndSetItemProperties(); + } + + public void TranslateFilterNames() + { + var filters = vcPro.Filters as IVCCollection; + if (filters == null) + return; + + foreach (VCFilter filter in filters) { + if (filter.Name == "Form Files") + filter.Name = Filters.FormFiles().Name; + if (filter.Name == "Generated Files") + filter.Name = Filters.GeneratedFiles().Name; + if (filter.Name == "Header Files") + filter.Name = Filters.HeaderFiles().Name; + if (filter.Name == "Resource Files") + filter.Name = Filters.ResourceFiles().Name; + if (filter.Name == "Source Files") + filter.Name = Filters.SourceFiles().Name; + } + } + + public static string CreateQrcFile(string projectDir, string className, string destName) + { + var fullDestName = projectDir + "\\" + destName; + + if (!File.Exists(fullDestName)) { + FileStream s = null; + try { + s = File.Open(fullDestName, FileMode.CreateNew); + if (s.CanWrite) { + using (var sw = new StreamWriter(s)) { + s = null; + sw.WriteLine("<RCC>"); + sw.WriteLine(" <qresource prefix=\"" + className + "\">"); + sw.WriteLine(" </qresource>"); + sw.WriteLine("</RCC>"); + } + } + } finally { + if (s != null) + s.Dispose(); + } + var attribs = File.GetAttributes(fullDestName); + File.SetAttributes(fullDestName, attribs & (~FileAttributes.ReadOnly)); + } + + var fi = new FileInfo(fullDestName); + return fi.FullName; + } + + public void AddActiveQtBuildStep(string version, string defFile = null) + { + if (FormatVersion < Resources.qtMinFormatVersion_ClProperties) + return; + + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var idlFile = "\"$(IntDir)/" + envPro.Name + ".idl\""; + var tblFile = "\"$(IntDir)/" + envPro.Name + ".tlb\""; + + var tool = (VCPostBuildEventTool)((IVCCollection)config.Tools).Item("VCPostBuildEventTool"); + var idc = "$(QTDIR)\\bin\\idc.exe \"$(TargetPath)\" /idl " + idlFile + " -version " + version; + var midl = "midl " + idlFile + " /tlb " + tblFile; + var idc2 = "$(QTDIR)\\bin\\idc.exe \"$(TargetPath)\" /tlb " + tblFile; + var idc3 = "$(QTDIR)\\bin\\idc.exe \"$(TargetPath)\" /regserver"; + + tool.CommandLine = idc + "\r\n" + midl + "\r\n" + idc2 + "\r\n" + idc3; + tool.Description = string.Empty; + + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + var librarian = (VCLibrarianTool)((IVCCollection)config.Tools).Item("VCLibrarianTool"); + + if (linker != null) { + linker.Version = version; + linker.ModuleDefinitionFile = defFile ?? envPro.Name + ".def"; + } else { + librarian.ModuleDefinitionFile = defFile ?? envPro.Name + ".def"; + } + } + } + + private void UpdateCompilerIncludePaths(string oldDir, string newDir) + { + var fixedOldDir = FixFilePathForComparison(oldDir); + var dirs = new[] { + FixFilePathForComparison(QtVSIPSettings.GetUicDirectory(envPro)), + FixFilePathForComparison(QtVSIPSettings.GetMocDirectory(envPro)), + FixFilePathForComparison(QtVSIPSettings.GetRccDirectory(envPro)) + }; + + var oldDirIsUsed = dirs.Any(dir => dir == fixedOldDir); + + var incList = new List<string>(); + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var compiler = CompilerToolWrapper.Create(config); + if (compiler == null) + continue; + var paths = compiler.AdditionalIncludeDirectories; + if (paths.Count == 0) + continue; + + if (!oldDirIsUsed) { + for (var i = paths.Count - 1; i >= 0; --i) { + if (FixFilePathForComparison(paths[i]) == fixedOldDir) + paths.RemoveAt(i); + } + } + incList.Clear(); + foreach (var path in paths) { + var tmp = HelperFunctions.NormalizeRelativeFilePath(path); + if (tmp.Length > 0 && !incList.Contains(tmp)) + incList.Add(tmp); + } + var alreadyThere = false; + var fixedNewDir = FixFilePathForComparison(newDir); + foreach (var include in incList) { + if (FixFilePathForComparison(include) == fixedNewDir) { + alreadyThere = true; + break; + } + } + if (!alreadyThere) + incList.Add(HelperFunctions.NormalizeRelativeFilePath(newDir)); + + compiler.AdditionalIncludeDirectories = incList; + } + } + + private static string FixFilePathForComparison(string path) + { + path = HelperFunctions.NormalizeRelativeFilePath(path); + return path.ToLower(); + } + + public void UpdateUicSteps(string oldUicDir, bool update_inc_path) + { + Messages.Print("\r\n=== Update uic steps ==="); + var vcFilter = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier); + if (vcFilter != null) { + var filterFiles = (IVCCollection)vcFilter.Files; + for (var i = filterFiles.Count; i > 0; i--) { + var file = (VCFile)filterFiles.Item(i); + if (file.Name.StartsWith("ui_", StringComparison.OrdinalIgnoreCase)) { + RemoveFileFromFilter(file, vcFilter); + HelperFunctions.DeleteEmptyParentDirs(file); + } + } + } + + var updatedFiles = 0; + var j = 0; + + var files = new VCFile[((IVCCollection)vcPro.Files).Count]; + foreach (VCFile file in (IVCCollection)vcPro.Files) + files[j++] = file; + + qtMsBuild.BeginSetItemProperties(); + foreach (var file in files) { + if (HelperFunctions.IsUicFile(file.Name) && !IsUic3File(file)) { + AddUic4BuildStep(file); + Messages.Print("Update uic step for " + file.Name + "."); + ++updatedFiles; + } + } + qtMsBuild.EndSetItemProperties(); + if (update_inc_path) + UpdateCompilerIncludePaths(oldUicDir, QtVSIPSettings.GetUicDirectory(envPro)); + + Messages.Print("\r\n=== " + updatedFiles + " uic steps updated. ===\r\n"); + } + + private static bool IsUic3File(VCFile file) + { + foreach (VCFileConfiguration config in (IVCCollection)file.FileConfigurations) { + var tool = HelperFunctions.GetCustomBuildTool(config); + if (tool == null) + return false; + if (tool.CommandLine.IndexOf("uic3.exe", StringComparison.OrdinalIgnoreCase) > -1) + return true; + } + return false; + } + + public bool UsePrecompiledHeaders(VCConfiguration config) + { + var compiler = CompilerToolWrapper.Create(config); + return UsePrecompiledHeaders(compiler); + } + + private bool UsePrecompiledHeaders(CompilerToolWrapper compiler) + { + try { + compiler.SetUsePrecompiledHeader(pchOption.pchUseUsingSpecific); + var pcHeaderThrough = GetPrecompiledHeaderThrough(); + if (string.IsNullOrEmpty(pcHeaderThrough)) + pcHeaderThrough = "stdafx.h"; + compiler.SetPrecompiledHeaderThrough(pcHeaderThrough); + var pcHeaderFile = GetPrecompiledHeaderFile(); + if (string.IsNullOrEmpty(pcHeaderFile)) + pcHeaderFile = ".\\$(ConfigurationName)/" + Project.Name + ".pch"; + compiler.SetPrecompiledHeaderFile(pcHeaderFile); + return true; + } catch { + return false; + } + } + + public bool UsesPrecompiledHeaders() + { + foreach (VCConfiguration config in vcPro.Configurations as IVCCollection) { + if (!UsesPrecompiledHeaders(config)) + return false; + } + return true; + } + + public static bool UsesPrecompiledHeaders(VCConfiguration config) + { + var compiler = CompilerToolWrapper.Create(config); + return UsesPrecompiledHeaders(compiler); + } + + private static bool UsesPrecompiledHeaders(CompilerToolWrapper compiler) + { + try { + if (compiler.GetUsePrecompiledHeader() != pchOption.pchNone) + return true; + } catch { } + return false; + } + + public string GetPrecompiledHeaderThrough() + { + foreach (VCConfiguration config in vcPro.Configurations as IVCCollection) { + var header = GetPrecompiledHeaderThrough(config); + if (header != null) + return header; + } + return null; + } + + public static string GetPrecompiledHeaderThrough(VCConfiguration config) + { + var compiler = CompilerToolWrapper.Create(config); + return GetPrecompiledHeaderThrough(compiler); + } + + private static string GetPrecompiledHeaderThrough(CompilerToolWrapper compiler) + { + try { + var header = compiler.GetPrecompiledHeaderThrough(); + if (!string.IsNullOrEmpty(header)) + return header.ToLower(); + } catch { } + return null; + } + + public string GetPrecompiledHeaderFile() + { + foreach (VCConfiguration config in vcPro.Configurations as IVCCollection) { + var file = GetPrecompiledHeaderFile(config); + if (!string.IsNullOrEmpty(file)) + return file; + } + return null; + } + + public static string GetPrecompiledHeaderFile(VCConfiguration config) + { + var compiler = CompilerToolWrapper.Create(config); + return GetPrecompiledHeaderFile(compiler); + } + + private static string GetPrecompiledHeaderFile(CompilerToolWrapper compiler) + { + try { + var file = compiler.GetPrecompiledHeaderFile(); + if (!string.IsNullOrEmpty(file)) + return file; + } catch { } + return null; + } + + public static void SetPCHOption(VCFile vcFile, pchOption option) + { + foreach (VCFileConfiguration config in vcFile.FileConfigurations as IVCCollection) { + var compiler = CompilerToolWrapper.Create(config); + compiler.SetUsePrecompiledHeader(option); + } + } + + private static VCFileConfiguration GetVCFileConfigurationByName(VCFile file, string configName) + { + foreach (VCFileConfiguration cfg in (IVCCollection)file.FileConfigurations) { + if (cfg.Name == configName) + return cfg; + } + return null; + } + + /// <summary> + /// Searches for the generated file inside the "Generated Files" filter. + /// The function looks for the given filename and uses the fileConfig's + /// ConfigurationName and Platform if moc directory contains $(ConfigurationName) + /// and/or $(PlatformName). + /// Otherwise it just uses the "Generated Files" filter + /// </summary> + /// <param name="fileName"></param> + /// <param name="fileConfig"></param> + /// <returns></returns> + private VCFile GetGeneratedMocFile(string fileName, VCFileConfiguration fileConfig) + { + if (QtVSIPSettings.HasDifferentMocFilePerConfig(envPro) + || QtVSIPSettings.HasDifferentMocFilePerPlatform(envPro)) { + var projectConfig = (VCConfiguration)fileConfig.ProjectConfiguration; + var configName = projectConfig.ConfigurationName; + var platformName = ((VCPlatform)projectConfig.Platform).Name; + var generatedFiles = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier); + if (generatedFiles == null) + return null; + foreach (VCFilter filt in (IVCCollection)generatedFiles.Filters) { + if (filt.Name == configName + "_" + platformName || + filt.Name == configName || filt.Name == platformName) { + foreach (VCFile filtFile in (IVCCollection)filt.Files) { + if (HelperFunctions.PathIsRelativeTo(filtFile.FullPath, fileName)) + return filtFile; + } + } + } + + //If a project from the an AddIn prior to 1.1.0 was loaded, the generated files are located directly + //in the generated files filter. + var relativeMocPath = QtVSIPSettings.GetMocDirectory( + envPro, + configName, + platformName, + fileConfig.File as VCFile) + + '\\' + fileName; + //Remove .\ at the beginning of the mocPath + if (relativeMocPath.StartsWith(".\\", StringComparison.Ordinal)) + relativeMocPath = relativeMocPath.Remove(0, 2); + foreach (VCFile filtFile in (IVCCollection)generatedFiles.Files) { + if (HelperFunctions.PathIsRelativeTo(filtFile.FullPath, relativeMocPath)) + return filtFile; + } + } else { + var generatedFiles = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier); + foreach (VCFile filtFile in (IVCCollection)generatedFiles.Files) { + if (HelperFunctions.PathIsRelativeTo(filtFile.FullPath, fileName)) + return filtFile; + } + } + return null; + } + + public void RefreshQtMocIncludePath() + { + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var propsClCompile = config.Rules.Item("CL") as IVCRulePropertyStorage; + var ruleName = GetRuleName(config, QtMoc.ItemTypeName); + var propsQtMoc = config.Rules.Item(ruleName) as IVCRulePropertyStorage; + if (propsClCompile == null || propsQtMoc == null) + continue; + propsQtMoc.SetPropertyValue(QtMoc.Property.IncludePath.ToString(), + propsClCompile.GetUnevaluatedPropertyValue("AdditionalIncludeDirectories")); + } + } + + public void RefreshQtMocDefine() + { + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var propsClCompile = config.Rules.Item("CL") as IVCRulePropertyStorage; + var ruleName = GetRuleName(config, QtMoc.ItemTypeName); + var propsQtMoc = config.Rules.Item(ruleName) as IVCRulePropertyStorage; + if (propsClCompile == null || propsQtMoc == null) + continue; + propsQtMoc.SetPropertyValue(QtMoc.Property.Define.ToString(), + propsClCompile.GetUnevaluatedPropertyValue("PreprocessorDefinitions")); + } + } + + public void RefreshMocSteps() + { + // Ignore when using shared compiler properties + if (GetFormatVersion(vcPro) >= Resources.qtMinFormatVersion_ClProperties) + return; + + var filesCollection = vcPro.Files as IVCCollection; + if (filesCollection == null) + return; + + int progress = 0; + int progressTotal = filesCollection.Count; + var waitDialog = WaitDialog.StartWithProgress(SR.GetString("Resources_QtVsTools"), + SR.GetString("WaitDialogRefreshMoc"), progressTotal, progress++, delay: 5); + qtMsBuild.BeginSetItemProperties(); + foreach (VCFile vcfile in filesCollection) { + RefreshMocStep(vcfile, false); + waitDialog.Update(SR.GetString("WaitDialogRefreshMoc"), progressTotal, progress++); + } + waitDialog.Stop(); + + waitDialog = WaitDialog.Start(SR.GetString("Resources_QtVsTools"), + SR.GetString("WaitDialogRefreshMoc"), delay: 2); + qtMsBuild.EndSetItemProperties(); + waitDialog.Stop(); + } + + public void RefreshMocStep(VCFile vcfile) + { + RefreshMocStep(vcfile, true); + } + + /// <summary> + /// Updates the moc command line for the given header or source file + /// containing the Q_OBJECT macro. + /// If the function is called from a property change for a single file + /// (singleFile = true) we may have to look for the according header + /// file and refresh the moc step for this file, if it contains Q_OBJECT. + /// </summary> + /// <param name="vcfile"></param> + private void RefreshMocStep(VCFile vcfile, bool singleFile) + { + var isHeaderFile = HelperFunctions.IsHeaderFile(vcfile.FullPath); + if (!isHeaderFile && !HelperFunctions.IsSourceFile(vcfile.FullPath)) + return; + + if (mocCmdChecker == null) + mocCmdChecker = new MocCmdChecker(); + + foreach (VCFileConfiguration config in (IVCCollection)vcfile.FileConfigurations) { + try { + string commandLine = ""; + VCCustomBuildTool tool = null; + VCFile mocable = null; + var customBuildConfig = config; + if (isHeaderFile || vcfile.ItemType == QtMoc.ItemTypeName) { + mocable = vcfile; + if (vcfile.ItemType == "CustomBuild") + tool = HelperFunctions.GetCustomBuildTool(config); + } else { + var mocFileName = GetMocFileName(vcfile.FullPath); + var mocFile = GetGeneratedMocFile(mocFileName, config); + if (mocFile == null) + continue; + + var mocFileConfig = GetVCFileConfigurationByName(mocFile, config.Name); + if (vcfile.ItemType == "CustomBuild") + tool = HelperFunctions.GetCustomBuildTool(mocFileConfig); + mocable = mocFile; + // It is possible that the function was called from a source file's property change, it is possible that + // we have to obtain the tool from the according header file + if ((vcfile.ItemType != "CustomBuild" || tool == null) && singleFile) { + var headerName = vcfile.FullPath.Remove(vcfile.FullPath.LastIndexOf('.')) + ".h"; + mocFileName = GetMocFileName(headerName); + mocFile = GetGeneratedMocFile(mocFileName, config); + if (mocFile != null) { + mocable = GetFileFromProject(headerName); + customBuildConfig = GetVCFileConfigurationByName(mocable, config.Name); + if (mocable.ItemType == "CustomBuild") + tool = HelperFunctions.GetCustomBuildTool(customBuildConfig); + } + } + } + + if (mocable.ItemType == "CustomBuild") { + if (tool != null) + commandLine = tool.CommandLine; + } else if (mocable.ItemType == QtMoc.ItemTypeName) { + commandLine = qtMsBuild.GenerateQtMocCommandLine(customBuildConfig); + } else { + continue; + } + + if ((mocable.ItemType == "CustomBuild" && tool == null) + || commandLine.IndexOf( + "moc.exe", + StringComparison.OrdinalIgnoreCase) == -1) + continue; + + VCFile srcMocFile, cppFile; + if (vcfile.ItemType == QtMoc.ItemTypeName + && HelperFunctions.IsSourceFile(vcfile.ItemName)) { + srcMocFile = cppFile = vcfile; + } else { + srcMocFile = GetSourceFileForMocStep(mocable); + cppFile = GetCppFileForMocStep(mocable); + } + if (srcMocFile == null) + continue; + var mocableIsCPP = (srcMocFile == cppFile); + + var cppItemType = (cppFile != null) ? cppFile.ItemType : ""; + if (cppFile != null && cppItemType != "ClCompile") + cppFile.ItemType = "ClCompile"; + + string pchParameters = null; + VCFileConfiguration defineIncludeConfig = null; + CompilerToolWrapper compiler = null; + if (cppFile == null) { + // No file specific defines/includes + // but at least the project defines/includes are added + defineIncludeConfig = config; + compiler = CompilerToolWrapper.Create(config.ProjectConfiguration as VCConfiguration); + } else { + defineIncludeConfig = GetVCFileConfigurationByName(cppFile, config.Name); + compiler = CompilerToolWrapper.Create(defineIncludeConfig); + } + + if (compiler != null && compiler.GetUsePrecompiledHeader() != pchOption.pchNone) + pchParameters = GetPCHMocOptions(srcMocFile, compiler); + + var outputFileName = QtVSIPSettings.GetMocDirectory(envPro) + "\\"; + if (mocableIsCPP) { + outputFileName += ProjectMacros.Name; + outputFileName += ".moc"; + } else { + outputFileName += "moc_"; + outputFileName += ProjectMacros.Name; + outputFileName += ".cpp"; + } + + var newCmdLine = mocCmdChecker.NewCmdLine(commandLine, + GetIncludes(defineIncludeConfig), + GetDefines(defineIncludeConfig), + QtVSIPSettings.GetMocOptions(envPro), srcMocFile.RelativePath, + pchParameters, + outputFileName); + + if (cppFile != null && cppItemType != "ClCompile") + cppFile.ItemType = cppItemType; + + // The tool's command line automatically gets a trailing "\r\n". + // We have to remove it to make the check below work. + var origCommandLine = commandLine; + if (origCommandLine.EndsWith("\r\n", StringComparison.Ordinal)) + origCommandLine = origCommandLine.Substring(0, origCommandLine.Length - 2); + + if (newCmdLine != null && newCmdLine != origCommandLine) { + // We have to delete the old moc file in order to trigger custom build step. + var configName = config.Name.Remove(config.Name.IndexOf('|')); + var platformName = config.Name.Substring(config.Name.IndexOf('|') + 1); + var projectPath = envPro.FullName.Remove(envPro.FullName.LastIndexOf('\\')); + var mocRelPath = GetRelativeMocFilePath(srcMocFile.FullPath, configName, platformName); + var mocPath = Path.Combine(projectPath, mocRelPath); + if (File.Exists(mocPath)) + File.Delete(mocPath); + if (mocable.ItemType == "CustomBuild") { + tool.CommandLine = newCmdLine; + } else { + qtMsBuild.SetQtMocCommandLine( + customBuildConfig, newCmdLine, new VCMacroExpander(config)); + } + } + } catch { + Messages.Print("ERROR: failed to refresh moc step for " + vcfile.ItemName); + } + } + } + + public void OnExcludedFromBuildChanged(VCFile vcFile, VCFileConfiguration vcFileCfg) + { + // Update the ExcludedFromBuild flags of the mocced file + // according to the ExcludedFromBuild flag of the mocable source file. + var moccedFileName = GetMocFileName(vcFile.Name); + if (string.IsNullOrEmpty(moccedFileName)) + return; + + var moccedFile = GetGeneratedMocFile(moccedFileName, vcFileCfg); + + if (moccedFile != null) { + VCFile cppFile = null; + if (HelperFunctions.IsHeaderFile(vcFile.Name)) + cppFile = GetCppFileForMocStep(vcFile); + + var moccedFileConfig = GetVCFileConfigurationByName(moccedFile, vcFileCfg.Name); + if (moccedFileConfig != null) { + if (cppFile != null && IsMoccedFileIncluded(cppFile)) { + if (!moccedFileConfig.ExcludedFromBuild) + moccedFileConfig.ExcludedFromBuild = true; + } else if (moccedFileConfig.ExcludedFromBuild != vcFileCfg.ExcludedFromBuild) { + moccedFileConfig.ExcludedFromBuild = vcFileCfg.ExcludedFromBuild; + } + } + } + } + + /// <summary> + /// Helper function for RefreshMocStep. + /// </summary> + /// <param name="file"></param> + /// <returns></returns> + private VCFile GetSourceFileForMocStep(VCFile file) + { + if (HelperFunctions.IsHeaderFile(file.Name)) + return file; + var fileName = file.Name; + if (HelperFunctions.IsMocFile(fileName)) { + fileName = fileName.Substring(0, fileName.Length - 4) + ".cpp"; + if (fileName.Length > 0) { + foreach (VCFile f in (IVCCollection)vcPro.Files) { + if (f.FullPath.EndsWith("\\" + fileName, StringComparison.Ordinal)) + return f; + } + } + } + return null; + } + + /// <summary> + /// Helper function for Refresh/UpdateMocStep. + /// </summary> + /// <param name="file"></param> + /// <returns></returns> + private VCFile GetCppFileForMocStep(VCFile file) + { + string fileName = file.Name; + if (fileName.EndsWith(".moc.cbt", StringComparison.OrdinalIgnoreCase)) + fileName = fileName.Remove(fileName.LastIndexOf('.')); + if (HelperFunctions.IsHeaderFile(fileName) || HelperFunctions.IsMocFile(fileName)) { + fileName = fileName.Remove(fileName.LastIndexOf('.')) + ".cpp"; + foreach (VCFile f in (IVCCollection)vcPro.Files) { + if (f.FullPath.EndsWith("\\" + fileName, StringComparison.Ordinal)) + return f; + } + } + return null; + } + + public void UpdateMocSteps(string oldMocDir) + { + Messages.Print("\r\n=== Update moc steps ==="); + var orgFiles = new List<VCFile>(); + var abandonedMocFiles = new List<string>(); + var vcFilter = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier); + if (vcFilter != null) { + var generatedFiles = GetAllFilesFromFilter(vcFilter); + for (var i = generatedFiles.Count - 1; i >= 0; i--) { + var file = generatedFiles[i]; + string fileName = null; + if (file.Name.StartsWith("moc_", StringComparison.OrdinalIgnoreCase)) + fileName = file.Name.Substring(4, file.Name.Length - 8) + ".h"; + else if (HelperFunctions.IsMocFile(file.Name)) + fileName = file.Name.Substring(0, file.Name.Length - 4) + ".cpp"; + + if (fileName != null) { + var found = false; + foreach (VCFile f in (IVCCollection)vcPro.Files) { + if (f.FullPath.EndsWith("\\" + fileName, StringComparison.OrdinalIgnoreCase)) { + if (!orgFiles.Contains(f) && HasMocStep(f, oldMocDir)) + orgFiles.Add(f); + found = true; + } + } + if (found) { + RemoveFileFromFilter(file, vcFilter); + HelperFunctions.DeleteEmptyParentDirs(file); + } else { + // We can't find foo.h for moc_foo.cpp or + // we can't find foo.cpp for foo.moc, thus we put the + // filename moc_foo.cpp / foo.moc into an error list. + abandonedMocFiles.Add(file.Name); + } + } + } + } + + UpdateCompilerIncludePaths(oldMocDir, QtVSIPSettings.GetMocDirectory(envPro)); + qtMsBuild.BeginSetItemProperties(); + foreach (var file in orgFiles) { + try { + RemoveMocStep(file); + AddMocStep(file); + } catch (QtVSException e) { + Messages.Print(e.Message); + continue; + } + Messages.Print("Moc step updated successfully for " + file.Name + "."); + } + qtMsBuild.EndSetItemProperties(); + + foreach (var s in abandonedMocFiles) { + Messages.Print("Moc step update failed for " + s + + ". Reason: Could not determine source file for moccing."); + } + Messages.Print("\r\n=== Moc steps updated. Successful: " + orgFiles.Count + + " Failed: " + abandonedMocFiles.Count + " ===\r\n"); + + CleanupFilter(vcFilter); + } + + private void Clean() + { + var solutionConfigs = envPro.DTE.Solution.SolutionBuild.SolutionConfigurations; + var backup = new List<KeyValuePair<SolutionContext, bool>>(); + foreach (SolutionConfiguration config in solutionConfigs) { + var solutionContexts = config.SolutionContexts; + if (solutionContexts == null) + continue; + + foreach (SolutionContext context in solutionContexts) { + backup.Add(new KeyValuePair<SolutionContext, bool>(context, context.ShouldBuild)); + if (envPro.FullName.Contains(context.ProjectName) + && context.PlatformName == envPro.ConfigurationManager.ActiveConfiguration.PlatformName) + context.ShouldBuild = true; + else + context.ShouldBuild = false; + } + } + try { + envPro.DTE.Solution.SolutionBuild.Clean(true); + } catch (System.Runtime.InteropServices.COMException) { + // TODO: Implement some logging mechanism for exceptions. + } + + foreach (var item in backup) + item.Key.ShouldBuild = item.Value; + } + + private void CleanupFilter(VCFilter filter) + { + var subFilters = filter.Filters as IVCCollection; + if (subFilters == null) + return; + + for (var i = subFilters.Count; i > 0; i--) { + var subFilter = subFilters.Item(i) as VCFilter; + var subFilterFilters = subFilter.Filters as IVCCollection; + if (subFilterFilters == null) + continue; + + CleanupFilter(subFilter); + + var filterOrFileFound = false; + foreach (var itemObject in subFilter.Items as IVCCollection) { + if (itemObject is VCFilter || itemObject is VCFile) { + filterOrFileFound = true; + break; + } + } + if (!filterOrFileFound) + filter.RemoveFilter(subFilter); + } + } + + public bool isWinRT() + { + try { + var vcProject = Project.Object as VCProject; + var vcConfigs = vcProject.Configurations as IVCCollection; + var vcConfig = vcConfigs.Item(1) as VCConfiguration; + var appType = vcConfig.GetEvaluatedPropertyValue("ApplicationType"); + if (appType == "Windows Store") + return true; + } catch { } + return false; + } + + public bool PromptChangeQtVersion(string oldVersion, string newVersion) + { + var versionManager = QtVersionManager.The(); + var viOld = versionManager.GetVersionInfo(oldVersion); + var viNew = versionManager.GetVersionInfo(newVersion); + + if (viOld == null || viNew == null) + return true; + + var oldIsWinRt = viOld.isWinRT(); + var newIsWinRt = viNew.isWinRT(); + + if (newIsWinRt == oldIsWinRt || newIsWinRt == isWinRT()) + return true; + + var promptCaption = string.Format("Change Qt Version ({0})", Project.Name); + var promptText = string.Format( + "Changing Qt version from {0} to {1}.\r\n" + + "Project might not build. Are you sure?", + newIsWinRt ? "Win32" : "WinRT", + newIsWinRt ? "WinRT" : "Win32" + ); + + return (MessageBox.Show( + promptText, promptCaption, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) + == DialogResult.Yes); + } + + /// <summary> + /// Changes the Qt version of this project. + /// </summary> + /// <param name="oldVersion">the current Qt version</param> + /// <param name="newVersion">the new Qt version we want to change to</param> + /// <param name="newProjectCreated">is set to true if a new Project object has been created</param> + /// <returns>true, if the operation performed successfully</returns> + public bool ChangeQtVersion(string oldVersion, string newVersion, ref bool newProjectCreated) + { + newProjectCreated = false; + var versionManager = QtVersionManager.The(); + var viNew = versionManager.GetVersionInfo(newVersion); + if (viNew == null) { + Messages.DisplayErrorMessage(SR.GetString("CannotChangeQtVersion")); + return false; + } + string vsPlatformNameNew = viNew.GetVSPlatformName(); + + var viOld = versionManager.GetVersionInfo(oldVersion); + string vsPlatformNameOld = null; + if (viOld != null) + vsPlatformNameOld = viOld.GetVSPlatformName(); + + var refreshMocSteps = (vsPlatformNameNew != vsPlatformNameOld); + var platformChanged = (vsPlatformNameNew != vsPlatformNameOld); + + try { + if (platformChanged) { + if (!SelectSolutionPlatform(vsPlatformNameNew) || !HasPlatform(vsPlatformNameNew)) { + CreatePlatform(vsPlatformNameOld, vsPlatformNameNew, viOld, viNew, ref newProjectCreated); + refreshMocSteps = false; + UpdateMocSteps(QtVSIPSettings.GetMocDirectory(envPro)); + } + } + var configManager = envPro.ConfigurationManager; + if (configManager.ActiveConfiguration.PlatformName != vsPlatformNameNew) { + var projectName = envPro.FullName; + envPro.Save(null); + dte.Solution.Remove(envPro); + envPro = dte.Solution.AddFromFile(projectName, false); + dte = envPro.DTE; + vcPro = envPro.Object as VCProject; + } + } catch { + Messages.DisplayErrorMessage(SR.GetString("CannotChangeQtVersion")); + return false; + } + + // We have to delete the generated files because of major + // differences between the platforms or Qt-Versions. + if (platformChanged || viOld.qtPatch != viNew.qtPatch + || viOld.qtMinor != viNew.qtMinor || viOld.qtMajor != viNew.qtMajor) { + DeleteGeneratedFiles(); + Clean(); + } + + if (refreshMocSteps) + RefreshMocSteps(); + + SetQtEnvironment(newVersion); + UpdateModules(viOld, viNew); + versionManager.SaveProjectQtVersion(envPro, newVersion, vsPlatformNameNew); + return true; + } + + public bool HasPlatform(string platformName) + { + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var platform = (VCPlatform)config.Platform; + if (platform.Name == platformName) + return true; + } + return false; + } + + public bool SelectSolutionPlatform(string platformName) + { + foreach (SolutionConfiguration solutionCfg in dte.Solution.SolutionBuild.SolutionConfigurations) { + var contexts = solutionCfg.SolutionContexts; + for (var i = 1; i <= contexts.Count; ++i) { + SolutionContext ctx = null; + try { + ctx = contexts.Item(i); + } catch (ArgumentException) { + // This may happen if we encounter an unloaded project. + continue; + } + + if (ctx.PlatformName == platformName + && solutionCfg.Name == dte.Solution.SolutionBuild.ActiveConfiguration.Name) { + solutionCfg.Activate(); + return true; + } + } + } + + return false; + } + + public void CreatePlatform(string oldPlatform, string newPlatform, + VersionInformation viOld, VersionInformation viNew, ref bool newProjectCreated) + { + try { + var cfgMgr = envPro.ConfigurationManager; + cfgMgr.AddPlatform(newPlatform, oldPlatform, true); + vcPro.AddPlatform(newPlatform); + newProjectCreated = false; + } catch { + // That stupid ConfigurationManager can't handle platform names + // containing dots (e.g. "Windows Mobile 5.0 Pocket PC SDK (ARMV4I)") + // So we have to do it the nasty way... + var projectFileName = envPro.FullName; + envPro.Save(null); + dte.Solution.Remove(envPro); + AddPlatformToVCProj(projectFileName, oldPlatform, newPlatform); + envPro = dte.Solution.AddFromFile(projectFileName, false); + vcPro = (VCProject)envPro.Object; + newProjectCreated = true; + } + + // update the platform settings + foreach (VCConfiguration config in (IVCCollection)vcPro.Configurations) { + var vcplatform = (VCPlatform)config.Platform; + if (vcplatform.Name == newPlatform) { + if (viOld != null) + RemovePlatformDependencies(config, viOld); + SetupConfiguration(config, viNew); + } + } + + SelectSolutionPlatform(newPlatform); + } + + public static void RemovePlatformDependencies(VCConfiguration config, VersionInformation viOld) + { + var compiler = CompilerToolWrapper.Create(config); + var minuend = new HashSet<string>(compiler.PreprocessorDefinitions); + minuend.ExceptWith(viOld.GetQMakeConfEntry("DEFINES").Split(' ', '\t')); + compiler.SetPreprocessorDefinitions(string.Join(",", minuend)); + } + + public void SetupConfiguration(VCConfiguration config, VersionInformation viNew) + { + var compiler = CompilerToolWrapper.Create(config); + var ppdefs = new HashSet<string>(compiler.PreprocessorDefinitions); + ppdefs.UnionWith(viNew.GetQMakeConfEntry("DEFINES").Split(' ', '\t')); + compiler.SetPreprocessorDefinitions(string.Join(",", ppdefs)); + + var linker = (VCLinkerTool)((IVCCollection)config.Tools).Item("VCLinkerTool"); + if (linker == null) + return; + + linker.SubSystem = subSystemOption.subSystemWindows; + SetTargetMachine(linker, viNew); + } + + private void DeleteGeneratedFiles() + { + var genFilter = Filters.GeneratedFiles(); + var genVCFilter = FindFilterFromGuid(genFilter.UniqueIdentifier); + if (genVCFilter == null) + return; + + var error = false; + error = DeleteFilesFromFilter(genVCFilter); + if (error) + Messages.Print(SR.GetString("DeleteGeneratedFilesError")); + } + + private bool DeleteFilesFromFilter(VCFilter filter) + { + var error = false; + foreach (VCFile f in filter.Files as IVCCollection) { + try { + var fi = new FileInfo(f.FullPath); + if (fi.Exists && fi.Extension != ".cbt") + fi.Delete(); + HelperFunctions.DeleteEmptyParentDirs(fi.Directory.ToString()); + } catch { + error = true; + } + } + foreach (VCFilter filt in filter.Filters as IVCCollection) + error |= DeleteFilesFromFilter(filt); + return error; + } + + public void RemoveGeneratedFiles(string fileName) + { + var fi = new FileInfo(fileName); + var lastIndex = fileName.LastIndexOf(fi.Extension, StringComparison.Ordinal); + var baseName = fi.Name.Remove(lastIndex, fi.Extension.Length); + string delName = null; + if (HelperFunctions.IsHeaderFile(fileName)) + delName = "moc_" + baseName + ".cpp"; + else if (HelperFunctions.IsSourceFile(fileName) && !fileName.StartsWith("moc_", StringComparison.OrdinalIgnoreCase)) + delName = baseName + ".moc"; + else if (HelperFunctions.IsUicFile(fileName)) + delName = "ui_" + baseName + ".h"; + else if (HelperFunctions.IsQrcFile(fileName)) + delName = "qrc_" + baseName + ".cpp"; + + if (delName != null) { + foreach (var delFile in GetFilesFromProject(delName)) + RemoveFileFromFilter(delFile, Filters.GeneratedFiles()); + } + } + + public void RemoveResFilesFromGeneratedFilesFilter() + { + var generatedFiles = FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier); + if (generatedFiles == null) + return; + + var filesToRemove = new List<VCFile>(); + foreach (VCFile filtFile in (IVCCollection)generatedFiles.Files) { + if (filtFile.FullPath.EndsWith(".res", StringComparison.OrdinalIgnoreCase)) + filesToRemove.Add(filtFile); + } + foreach (var resFile in filesToRemove) + resFile.Remove(); + } + + static private void AddPlatformToVCProj(string projectFileName, string oldPlatformName, string newPlatformName) + { + var tempFileName = Path.GetTempFileName(); + var fi = new FileInfo(projectFileName); + fi.CopyTo(tempFileName, true); + + var myXmlDocument = new XmlDocument(); + myXmlDocument.Load(tempFileName); + AddPlatformToVCProj(myXmlDocument, oldPlatformName, newPlatformName); + myXmlDocument.Save(projectFileName); + + fi = new FileInfo(tempFileName); + fi.Delete(); + } + + static private void AddPlatformToVCProj(XmlDocument doc, string oldPlatformName, string newPlatformName) + { + var vsProj = doc.DocumentElement.SelectSingleNode("/VisualStudioProject"); + var platforms = vsProj.SelectSingleNode("Platforms"); + if (platforms == null) { + platforms = doc.CreateElement("Platforms"); + vsProj.AppendChild(platforms); + } + var platform = platforms.SelectSingleNode("Platform[@Name='" + newPlatformName + "']"); + if (platform == null) { + platform = doc.CreateElement("Platform"); + ((XmlElement)platform).SetAttribute("Name", newPlatformName); + platforms.AppendChild(platform); + } + + var configurations = vsProj.SelectSingleNode("Configurations"); + var cfgList = configurations.SelectNodes("Configuration[@Name='Debug|" + oldPlatformName + "'] | " + + "Configuration[@Name='Release|" + oldPlatformName + "']"); + foreach (XmlNode oldCfg in cfgList) { + var newCfg = (XmlElement)oldCfg.Clone(); + newCfg.SetAttribute("Name", oldCfg.Attributes["Name"].Value.Replace(oldPlatformName, newPlatformName)); + configurations.AppendChild(newCfg); + } + + var fileCfgPath = "Files/Filter/File/FileConfiguration"; + var fileCfgList = vsProj.SelectNodes(fileCfgPath + "[@Name='Debug|" + oldPlatformName + "'] | " + + fileCfgPath + "[@Name='Release|" + oldPlatformName + "']"); + foreach (XmlNode oldCfg in fileCfgList) { + var newCfg = (XmlElement)oldCfg.Clone(); + newCfg.SetAttribute("Name", oldCfg.Attributes["Name"].Value.Replace(oldPlatformName, newPlatformName)); + oldCfg.ParentNode.AppendChild(newCfg); + } + } + + static private void SetTargetMachine(VCLinkerTool linker, VersionInformation versionInfo) + { + var qMakeLFlagsWindows = versionInfo.GetQMakeConfEntry("QMAKE_LFLAGS_WINDOWS"); + var rex = new Regex("/MACHINE:(\\S+)"); + var match = rex.Match(qMakeLFlagsWindows); + if (match.Success) { + linker.TargetMachine = HelperFunctions.TranslateMachineType(match.Groups[1].Value); + } else { + var platformName = versionInfo.GetVSPlatformName(); + if (platformName == "Win32") + linker.TargetMachine = machineTypeOption.machineX86; + else if (platformName == "x64") + linker.TargetMachine = machineTypeOption.machineAMD64; + else + linker.TargetMachine = machineTypeOption.machineNotSet; + } + + var subsystemOption = string.Empty; + var linkerOptions = linker.AdditionalOptions ?? string.Empty; + + rex = new Regex("(/SUBSYSTEM:\\S+)"); + match = rex.Match(qMakeLFlagsWindows); + if (match.Success) + subsystemOption = match.Groups[1].Value; + + match = rex.Match(linkerOptions); + if (match.Success) { + linkerOptions = rex.Replace(linkerOptions, subsystemOption); + } else { + if (linkerOptions.Length > 0) + linkerOptions += " "; + linkerOptions += subsystemOption; + } + linker.AdditionalOptions = linkerOptions; + } + + public void CollapseFilter(string filterName) + { + var solutionExplorer = (UIHierarchy)dte.Windows.Item(Constants.vsext_wk_SProjectWindow).Object; + if (solutionExplorer.UIHierarchyItems.Count == 0) + return; + + dte.SuppressUI = true; + var projectItem = FindProjectHierarchyItem(solutionExplorer); + if (projectItem != null) + HelperFunctions.CollapseFilter(projectItem, solutionExplorer, filterName); + dte.SuppressUI = false; + } + + private UIHierarchyItem FindProjectHierarchyItem(UIHierarchy hierarchy) + { + if (hierarchy.UIHierarchyItems.Count == 0) + return null; + + var solution = hierarchy.UIHierarchyItems.Item(1); + UIHierarchyItem projectItem = null; + foreach (UIHierarchyItem solutionItem in solution.UIHierarchyItems) { + projectItem = FindProjectHierarchyItem(solutionItem); + if (projectItem != null) + break; + } + return projectItem; + } + + private UIHierarchyItem FindProjectHierarchyItem(UIHierarchyItem root) + { + UIHierarchyItem projectItem = null; + try { + if (root.Name == envPro.Name) + return root; + + foreach (UIHierarchyItem childItem in root.UIHierarchyItems) { + projectItem = FindProjectHierarchyItem(childItem); + if (projectItem != null) + break; + } + } catch { + } + return projectItem; + } + + /// <summary> + /// Gets the Qt version of the project + /// </summary> + public string GetQtVersion() + { + return QtVersionManager.The().GetProjectQtVersion(envPro); + } + + /// <summary> + /// Sets the Qt environment for the project's Qt version. + /// </summary> + public void SetQtEnvironment() + { + SetQtEnvironment(QtVersionManager.The().GetProjectQtVersion(envPro)); + } + + /// <summary> + /// Sets the Qt environment for the given Qt version. + /// </summary> + public void SetQtEnvironment(string qtVersion) + { + SetQtEnvironment(qtVersion, string.Empty); + } + + /// <summary> + /// Sets the Qt environment for the given Qt version. + /// </summary> + public void SetQtEnvironment(string qtVersion, string solutionConfig, bool build = false) + { + if (string.IsNullOrEmpty(qtVersion)) + return; + + if (FormatVersion >= Resources.qtMinFormatVersion_Settings) + return; + + string qtDir = null; + if (qtVersion != "$(QTDIR)") + qtDir = QtVersionManager.The().GetInstallPath(qtVersion); + HelperFunctions.SetEnvironmentVariableEx("QTDIR", qtDir); + try { + var propertyAccess = (IVCBuildPropertyStorage)vcPro; + var vcprj = envPro.Object as VCProject; + + // Get platform name from given solution configuration + // or if not available take the active configuration + var activePlatformName = string.Empty; + if (string.IsNullOrEmpty(solutionConfig)) { + // First get active configuration cause not given as parameter + var activeConf = envPro.ConfigurationManager.ActiveConfiguration; + solutionConfig = activeConf.ConfigurationName + "|" + activeConf.PlatformName; + activePlatformName = activeConf.PlatformName; + } else { + activePlatformName = solutionConfig.Split('|')[1]; + } + + // Find all configurations for platform and set property for all of them + // This is to get QTDIR property set for all configurations same time so + // we can be sure it is set and is equal between debug and release + foreach (VCConfiguration conf in vcprj.Configurations as IVCCollection) { + var cur_platform = conf.Platform as VCPlatform; + if (cur_platform.Name == activePlatformName) { + var cur_solution = conf.ConfigurationName + "|" + cur_platform.Name; + // If the LocalDebuggerEnvironment property is defined, it + // will be stored in the .user file before the QTDIR property, which is an + // error because there is a dependency. To work around this, first remove + // the property and then add it after QTDIR is defined. + var debuggerEnv = string.Empty; + if (!build) { + debuggerEnv = propertyAccess.GetPropertyValue( + "LocalDebuggerEnvironment", cur_solution, "UserFile"); + if (!string.IsNullOrEmpty(debuggerEnv)) { + var debugSettings = conf.DebugSettings as VCDebugSettings; + if (debugSettings != null) { + //Get original value without expanded properties + debuggerEnv = debugSettings.Environment; + } + propertyAccess.RemoveProperty( + "LocalDebuggerEnvironment", cur_solution, "UserFile"); + } + } + propertyAccess.SetPropertyValue("QTDIR", cur_solution, "UserFile", qtDir); + if (!string.IsNullOrEmpty(debuggerEnv)) + propertyAccess.SetPropertyValue( + "LocalDebuggerEnvironment", cur_solution, "UserFile", debuggerEnv); + } + } + + } catch (Exception) { + Messages.Print(SR.GetString("QtProject_CannotAccessUserFile", vcPro.ItemName)); + } + + HelperFunctions.SetDebuggingEnvironment(envPro); + } + + public class CppConfig + { + public VCConfiguration Config; + public IVCRulePropertyStorage Cpp; + + public string GetUserPropertyValue(string pszPropName) + { + var vcProj = Config.project as VCProject; + var projProps = vcProj as IVCBuildPropertyStorage; + try { + return projProps.GetPropertyValue(pszPropName, Config.Name, "UserFile"); + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return string.Empty; + } + } + + public void SetUserPropertyValue(string pszPropName, string pszPropValue) + { + var vcProj = Config.project as VCProject; + var projProps = vcProj as IVCBuildPropertyStorage; + try { + projProps.SetPropertyValue(pszPropName, Config.Name, "UserFile", pszPropValue); + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + } + + public void RemoveUserProperty(string pszPropName) + { + var vcProj = Config.project as VCProject; + var projProps = vcProj as IVCBuildPropertyStorage; + try { + projProps.RemoveProperty(pszPropName, Config.Name, "UserFile"); + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + } + } + + public static IEnumerable<CppConfig> GetCppConfigs(VCProject vcPro) + { + return ((IVCCollection)vcPro.Configurations).Cast<VCConfiguration>() + .Select(x => new CppConfig + { + Config = x, + Cpp = x.Rules.Item("CL") as IVCRulePropertyStorage, + }) + .Where(x => x.Cpp != null + && x.Config.GetEvaluatedPropertyValue("ApplicationType") != "Linux"); + } + + public static IEnumerable<CppConfig> GetCppDebugConfigs(VCProject vcPro) + { + return GetCppConfigs(vcPro).Where(x => x.Cpp + .GetEvaluatedPropertyValue("PreprocessorDefinitions").Split(new char[] { ';' }) + .Contains("QT_NO_DEBUG") == false); + } + + public static bool IsQtQmlDebugDefined(VCProject vcPro) + { + return (GetCppDebugConfigs(vcPro).Where(x => x.Cpp + .GetEvaluatedPropertyValue("PreprocessorDefinitions").Split(new char[] { ';' }) + .Contains("QT_QML_DEBUG") == false) + .Any() == false); + } + + public static void DefineQtQmlDebug(VCProject vcPro) + { + var configs = GetCppDebugConfigs(vcPro).Where(x => x.Cpp + .GetEvaluatedPropertyValue("PreprocessorDefinitions").Split(new char[] { ';' }) + .Contains("QT_QML_DEBUG") == false) + .Select(x => new + { + x.Cpp, + Macros = x.Cpp.GetUnevaluatedPropertyValue("PreprocessorDefinitions") + }); + + foreach (var config in configs) { + config.Cpp.SetPropertyValue("PreprocessorDefinitions", + string.Format("QT_QML_DEBUG;{0}", config.Macros)); + } + } + + public static void UndefineQtQmlDebug(VCProject vcPro) + { + var configs = GetCppDebugConfigs(vcPro).Where(x => x.Cpp + .GetEvaluatedPropertyValue("PreprocessorDefinitions").Split(new char[] { ';' }) + .Contains("QT_QML_DEBUG") == true) + .Select(x => new + { + x.Cpp, + Macros = x.Cpp.GetUnevaluatedPropertyValue("PreprocessorDefinitions") + .Split(new char[] { ';' }).ToList() + }); + + foreach (var config in configs) { + config.Macros.Remove("QT_QML_DEBUG"); + config.Cpp.SetPropertyValue("PreprocessorDefinitions", + string.Join(";", config.Macros)); + } + } + + public static bool IsQmlJsDebuggerDefined(VCProject vcPro) + { + foreach (var config in GetCppDebugConfigs(vcPro)) { + var qmlDebug = config.GetUserPropertyValue("QmlDebug"); + if (string.IsNullOrEmpty(qmlDebug)) + return false; + var debugArgs = config.GetUserPropertyValue("LocalDebuggerCommandArguments"); + if (string.IsNullOrEmpty(debugArgs)) + return false; + if (!debugArgs.Contains(qmlDebug)) + return false; + } + return true; + } + + public static void DefineQmlJsDebugger(VCProject vcPro) + { + var configs = GetCppDebugConfigs(vcPro) + .Select(x => new + { + Self = x, + QmlDebug = x.GetUserPropertyValue("QmlDebug"), + Args = x.GetUserPropertyValue("LocalDebuggerCommandArguments") + }) + .Where(x => string.IsNullOrEmpty(x.QmlDebug) || !x.Args.Contains(x.QmlDebug)); + + foreach (var config in configs) { + + config.Self.RemoveUserProperty("LocalDebuggerCommandArguments"); + config.Self.RemoveUserProperty("QmlDebug"); + config.Self.RemoveUserProperty("QmlDebugSettings"); + + config.Self.SetUserPropertyValue("QmlDebugSettings", "file:$(ProjectGuid),block"); + config.Self.SetUserPropertyValue("QmlDebug", "-qmljsdebugger=$(QmlDebugSettings)"); + + config.Self.SetUserPropertyValue("LocalDebuggerCommandArguments", + string.Join(" ", new[] { config.Args, "$(QmlDebug)" }).Trim()); + } + } + + public static void UndefineQmlJsDebugger(VCProject vcPro) + { + var configs = GetCppDebugConfigs(vcPro) + .Select(x => new + { + Self = x, + QmlDebug = x.GetUserPropertyValue("QmlDebug"), + Args = x.GetUserPropertyValue("LocalDebuggerCommandArguments") + }) + .Where(x => !string.IsNullOrEmpty(x.QmlDebug) && x.Args.Contains(x.QmlDebug)); + + foreach (var config in configs) { + + config.Self.SetUserPropertyValue("QmlDebug", "##QMLDEBUG##"); + var args = config.Self.GetUserPropertyValue("LocalDebuggerCommandArguments"); + + var newArgs = args.Replace("##QMLDEBUG##", "").Trim(); + if (string.IsNullOrEmpty(newArgs)) + config.Self.RemoveUserProperty("LocalDebuggerCommandArguments"); + else + config.Self.SetUserPropertyValue("LocalDebuggerCommandArguments", newArgs); + + config.Self.RemoveUserProperty("QmlDebug"); + config.Self.SetUserPropertyValue("QmlDebugSettings", "false"); + } + } + + public bool QmlDebug + { + get + { + return IsQtQmlDebugDefined(vcPro) && IsQmlJsDebuggerDefined(vcPro); + } + set + { + bool enabled = (IsQtQmlDebugDefined(vcPro) && IsQmlJsDebuggerDefined(vcPro)); + if (value == enabled) + return; + + if (value) { + DefineQtQmlDebug(vcPro); + DefineQmlJsDebugger(vcPro); + } else { + UndefineQtQmlDebug(vcPro); + UndefineQmlJsDebugger(vcPro); + } + } + } + } + + public class VCPropertyStorageProvider : IPropertyStorageProvider + { + string GetProperty(IVCRulePropertyStorage propertyStorage, string propertyName) + { + if (propertyStorage == null) + return ""; + return propertyStorage.GetUnevaluatedPropertyValue(propertyName); + } + + public string GetProperty(object propertyStorage, string itemType, string propertyName) + { + if (propertyStorage == null) + return ""; + if (propertyStorage is VCFileConfiguration) { + return GetProperty( + (propertyStorage as VCFileConfiguration).Tool + as IVCRulePropertyStorage, + propertyName); + } else if (propertyStorage is VCConfiguration) { + var config = propertyStorage as VCConfiguration; + var ruleName = QtProject.GetRuleName(config, itemType); + return GetProperty(config.Rules.Item(ruleName) + as IVCRulePropertyStorage, + propertyName); + } + return ""; + } + + static bool SetProperty( + IVCRulePropertyStorage propertyStorage, + string propertyName, + string propertyValue) + { + if (propertyStorage == null) + return false; + if (propertyStorage.GetUnevaluatedPropertyValue(propertyName) != propertyValue) + propertyStorage.SetPropertyValue(propertyName, propertyValue); + return true; + } + + public bool SetProperty( + object propertyStorage, + string itemType, + string propertyName, + string propertyValue) + { + if (propertyStorage == null) + return false; + if (propertyStorage is VCFileConfiguration) { + return SetProperty( + (propertyStorage as VCFileConfiguration).Tool + as IVCRulePropertyStorage, + propertyName, + propertyValue); + } else if (propertyStorage is VCConfiguration) { + var config = propertyStorage as VCConfiguration; + var ruleName = QtProject.GetRuleName(config, itemType); + return SetProperty( + config.Rules.Item(ruleName) + as IVCRulePropertyStorage, + propertyName, + propertyValue); + } + return false; + } + + static bool DeleteProperty(IVCRulePropertyStorage propertyStorage, string propertyName) + { + if (propertyStorage == null) + return false; + propertyStorage.DeleteProperty(propertyName); + return true; + } + + public bool DeleteProperty(object propertyStorage, string itemType, string propertyName) + { + if (propertyStorage == null) + return false; + if (propertyStorage is VCFileConfiguration) { + return DeleteProperty( + (propertyStorage as VCFileConfiguration).Tool + as IVCRulePropertyStorage, + propertyName); + } else if (propertyStorage is VCConfiguration) { + var config = propertyStorage as VCConfiguration; + var ruleName = QtProject.GetRuleName(config, itemType); + return DeleteProperty( + config.Rules.Item(ruleName) + as IVCRulePropertyStorage, + propertyName); + } + return false; + } + + public string GetConfigName(object propertyStorage) + { + if (propertyStorage == null) + return ""; + if (propertyStorage is VCFileConfiguration) + return (propertyStorage as VCFileConfiguration).Name; + else if (propertyStorage is VCConfiguration) + return (propertyStorage as VCConfiguration).Name; + return ""; + } + + string GetItemType(VCFileConfiguration propertyStorage) + { + if (propertyStorage == null) + return ""; + VCFile file = propertyStorage.File as VCFile; + if (file == null) + return ""; + return file.ItemType; + } + + public string GetItemType(object propertyStorage) + { + if (propertyStorage == null) + return ""; + if (propertyStorage is VCFileConfiguration) + return GetItemType(propertyStorage as VCFileConfiguration); + return ""; + } + + string GetItemName(VCFileConfiguration propertyStorage) + { + if (propertyStorage == null) + return ""; + VCFile file = propertyStorage.File as VCFile; + if (file == null) + return ""; + return file.Name; + } + + public string GetItemName(object propertyStorage) + { + if (propertyStorage == null) + return ""; + if (propertyStorage is VCFileConfiguration) + return GetItemName(propertyStorage as VCFileConfiguration); + return ""; + } + + object GetParentProject(VCConfiguration propertyStorage) + { + if (propertyStorage == null) + return null; + return propertyStorage.project as VCProject; + } + + object GetParentProject(VCFileConfiguration propertyStorage) + { + if (propertyStorage == null) + return null; + return GetParentProject(propertyStorage.ProjectConfiguration as VCConfiguration); + } + + public object GetParentProject(object propertyStorage) + { + if (propertyStorage == null) + return null; + if (propertyStorage is VCFileConfiguration) + return GetParentProject(propertyStorage as VCFileConfiguration); + else if (propertyStorage is VCConfiguration) + return GetParentProject(propertyStorage as VCConfiguration); + return null; + } + + object GetProjectConfiguration(VCProject project, string configName) + { + if (project == null) + return null; + foreach (VCConfiguration projConfig in (IVCCollection)project.Configurations) { + if (projConfig.Name == configName) + return projConfig; + } + return null; + } + + public object GetProjectConfiguration(object project, string configName) + { + if (project == null) + return null; + return GetProjectConfiguration(project as VCProject, configName); + } + + IEnumerable<object> GetItems(VCProject project, string itemType, string configName = "") + { + if (project == null) + return new List<object>(); + var allItems = project.GetFilesWithItemType(itemType) as IVCCollection; + var items = new List<VCFileConfiguration>(); + foreach (VCFile vcFile in allItems) { + foreach (VCFileConfiguration vcFileConfig + in vcFile.FileConfigurations as IVCCollection) { + if (!string.IsNullOrEmpty(configName) && vcFileConfig.Name != configName) + continue; + items.Add(vcFileConfig); + } + } + return items; + } + + public IEnumerable<object> GetItems( + object project, + string itemType, + string configName = "") + { + if (project == null) + return null; + return GetItems(project as VCProject, itemType, configName); + } + + } + + public class VCMacroExpander : IVSMacroExpander + { + object config; + + public VCMacroExpander(object config) + { + this.config = config; + } + + public string ExpandString(string stringToExpand) + { + HelperFunctions.ExpandString(ref stringToExpand, config); + return stringToExpand; + } + } + + public class QtCustomBuildTool + { + QtMsBuildContainer qtMsBuild; + VCFileConfiguration vcConfig; + VCFile vcFile; + VCCustomBuildTool tool; + VCMacroExpander macros; + + enum FileItemType { Other = 0, CustomBuild, QtMoc, QtRcc, QtRepc, QtUic }; + FileItemType itemType = FileItemType.Other; + public QtCustomBuildTool(VCFileConfiguration vcConfig, QtMsBuildContainer container = null) + { + if (container != null) + qtMsBuild = container; + else + qtMsBuild = new QtMsBuildContainer(new VCPropertyStorageProvider()); + this.vcConfig = vcConfig; + if (vcConfig != null) + vcFile = vcConfig.File as VCFile; + if (vcFile != null) { + if (vcFile.ItemType == "CustomBuild") + itemType = FileItemType.CustomBuild; + else if (vcFile.ItemType == QtMoc.ItemTypeName) + itemType = FileItemType.QtMoc; + else if (vcFile.ItemType == QtRcc.ItemTypeName) + itemType = FileItemType.QtRcc; + else if (vcFile.ItemType == QtRepc.ItemTypeName) + itemType = FileItemType.QtRepc; + else if (vcFile.ItemType == QtUic.ItemTypeName) + itemType = FileItemType.QtUic; + } + if (itemType == FileItemType.CustomBuild) + tool = HelperFunctions.GetCustomBuildTool(vcConfig); + macros = new VCMacroExpander(vcConfig); + } + + public string CommandLine + { + get + { + switch (itemType) { + case FileItemType.CustomBuild: + return (tool != null) ? tool.CommandLine : ""; + case FileItemType.QtMoc: + return qtMsBuild.GenerateQtMocCommandLine(vcConfig); + case FileItemType.QtRcc: + return qtMsBuild.GenerateQtRccCommandLine(vcConfig); + case FileItemType.QtRepc: + return qtMsBuild.GenerateQtRepcCommandLine(vcConfig); + case FileItemType.QtUic: + return qtMsBuild.GenerateQtUicCommandLine(vcConfig); + } + return ""; + } + set + { + switch (itemType) { + case FileItemType.CustomBuild: + if (tool != null) + tool.CommandLine = value; + break; + case FileItemType.QtMoc: + qtMsBuild.SetQtMocCommandLine(vcConfig, value, macros); + break; + case FileItemType.QtRcc: + qtMsBuild.SetQtRccCommandLine(vcConfig, value, macros); + break; + case FileItemType.QtRepc: + qtMsBuild.SetQtRepcCommandLine(vcConfig, value, macros); + break; + case FileItemType.QtUic: + qtMsBuild.SetQtUicCommandLine(vcConfig, value, macros); + break; + } + } + } + + public string Outputs + { + get + { + switch (itemType) { + case FileItemType.CustomBuild: + return (tool != null) ? tool.Outputs : ""; + case FileItemType.QtMoc: + return qtMsBuild.GetPropertyValue(vcConfig, QtMoc.Property.OutputFile); + case FileItemType.QtRcc: + return qtMsBuild.GetPropertyValue(vcConfig, QtRcc.Property.OutputFile); + case FileItemType.QtRepc: + return qtMsBuild.GetPropertyValue(vcConfig, QtRepc.Property.OutputFile); + case FileItemType.QtUic: + return qtMsBuild.GetPropertyValue(vcConfig, QtUic.Property.OutputFile); + } + return ""; + } + } + + } + +} diff --git a/QtVsTools.Core/QtVSException.cs b/QtVsTools.Core/QtVSException.cs new file mode 100644 index 0000000..8d39e29 --- /dev/null +++ b/QtVsTools.Core/QtVSException.cs @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; + +namespace QtVsTools.Core +{ + [Serializable] + public class QtVSException : ApplicationException + { + public QtVSException(string message) + : base(message) + { + } + + public QtVSException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/QtVsTools.Core/QtVSIPSettings.cs b/QtVsTools.Core/QtVSIPSettings.cs new file mode 100644 index 0000000..bbd3a45 --- /dev/null +++ b/QtVsTools.Core/QtVSIPSettings.cs @@ -0,0 +1,566 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.VCProjectEngine; +using Microsoft.Win32; +using QtVsTools.Core.QtMsBuild; +using System; +using System.Collections; + +namespace QtVsTools.Core +{ + public interface IQtVsToolsOptions + { + string QtMsBuildPath { get; } + bool QmlDebuggerEnabled { get; } + int QmlDebuggerTimeout { get; } + bool HelpPreferenceOnline { get; } + } + + public static class QtVSIPSettings + { + public static IQtVsToolsOptions Options { get; set; } + + static Hashtable mocDirCache = new Hashtable(); + static Hashtable uicDirCache = new Hashtable(); + static Hashtable rccDirCache = new Hashtable(); + + public static bool GetDisableAutoMocStepsUpdate() + { + return GetBoolValue(Resources.disableAutoMocStepsUpdateKeyword, false); + } + + public static void SaveDisableAutoMocStepsUpdate(bool b) + { + SetBoolValue(Resources.disableAutoMocStepsUpdateKeyword, b); + } + + public static string GetUicDirectory(EnvDTE.Project project) + { + return GetDirectory(project, Resources.uicDirKeyword); + } + + public static void SaveUicDirectory(EnvDTE.Project project, string directory) + { + if (directory == null) + SaveDirectory(project, Resources.uicDirKeyword, GetDirectory(project, Resources.uicDirKeyword)); + else + SaveDirectory(project, Resources.uicDirKeyword, directory); + } + + public static string GetMocDirectory() + { + return GetDirectory(Resources.mocDirKeyword); + } + + public static string GetMocDirectory(EnvDTE.Project project) + { + return GetDirectory(project, Resources.mocDirKeyword); + } + + public static string GetMocDirectory( + EnvDTE.Project project, + string configName, + string platformName, VCFile vCFile) + { + string filePath = null; + if (vCFile != null) + filePath = vCFile.FullPath; + return GetMocDirectory(project, configName, platformName, filePath); + } + + public static string GetMocDirectory( + EnvDTE.Project project, + string configName, + string platformName, + string filePath = null) + { + var dir = GetDirectory(project, Resources.mocDirKeyword); + if (!string.IsNullOrEmpty(configName) + && !string.IsNullOrEmpty(platformName)) + HelperFunctions.ExpandString(ref dir, project, configName, platformName, filePath); + return dir; + } + + public static bool HasDifferentMocFilePerConfig(EnvDTE.Project project) + { + var mocDir = GetMocDirectory(project); + return mocDir.Contains("$(ConfigurationName)"); + } + + public static bool HasDifferentMocFilePerPlatform(EnvDTE.Project project) + { + var mocDir = GetMocDirectory(project); + return mocDir.Contains("$(PlatformName)"); + } + + public static string GetMocOptions() + { + return GetOption(Resources.mocOptionsKeyword); + } + + public static string GetMocOptions(EnvDTE.Project project) + { + return GetOption(project, Resources.mocOptionsKeyword); + } + + public static bool GetLUpdateOnBuild(EnvDTE.Project project) + { + if (GetProjectQtSetting(project, "QtRunLUpdateOnBuild") == "true") + return true; + return GetBoolValue(project, Resources.lupdateKeyword); + } + + public static string GetLUpdateOptions() + { + return GetOption(Resources.lupdateOptionsKeyword); + } + + static string GetProjectQtSetting(EnvDTE.Project project, string propertyName) + { + var vcProject = project.Object as VCProject; + if (vcProject == null) + return null; + + var vcConfigs = vcProject.Configurations as IVCCollection; + if (vcConfigs == null) + return null; + + var activeConfig = project.ConfigurationManager.ActiveConfiguration; + if (activeConfig == null) + return null; + + var activeConfigId = string.Format("{0}|{1}", + activeConfig.ConfigurationName, activeConfig.PlatformName); + + var props = vcProject as IVCBuildPropertyStorage; + if (props == null) + return null; + + try { + return props.GetPropertyValue(propertyName, activeConfigId, "ProjectFile"); + } catch { + return null; + } + } + + public static string GetLUpdateOptions(EnvDTE.Project project) + { + string qtLUpdateOptions = GetProjectQtSetting(project, "QtLUpdateOptions"); + if (!string.IsNullOrEmpty(qtLUpdateOptions)) + return qtLUpdateOptions; + return GetOption(project, Resources.lupdateOptionsKeyword); + } + + public static string GetLReleaseOptions() + { + return GetOption(Resources.lreleaseOptionsKeyword); + } + + public static string GetLReleaseOptions(EnvDTE.Project project) + { + string qtLReleaseOptions = GetProjectQtSetting(project, "QtLReleaseOptions"); + if (!string.IsNullOrEmpty(qtLReleaseOptions)) + return qtLReleaseOptions; + return GetOption(project, Resources.lreleaseOptionsKeyword); + } + + public static bool GetAskBeforeCheckoutFile() + { + return GetBoolValue(Resources.askBeforeCheckoutFileKeyword, true); + } + + public static void SaveAskBeforeCheckoutFile(bool value) + { + SetBoolValue(Resources.askBeforeCheckoutFileKeyword, value); + } + + public static bool GetDisableCheckoutFiles() + { + return GetBoolValue(Resources.disableCheckoutFilesKeyword, false); + } + + public static void SaveDisableCheckoutFiles(bool value) + { + SetBoolValue(Resources.disableCheckoutFilesKeyword, value); + } + + public static void SaveMocDirectory(EnvDTE.Project project, string directory) + { + if (directory == null) + SaveDirectory(project, Resources.mocDirKeyword, GetDirectory(project, Resources.mocDirKeyword)); + else + SaveDirectory(project, Resources.mocDirKeyword, directory); + } + + public static void SaveMocOptions(EnvDTE.Project project, string options) + { + if (options == null) + options = GetMocOptions(); + SaveOption(project, Resources.mocOptionsKeyword, options); + } + + public static void SaveMocOptions(string options) + { + SaveOption(Resources.mocOptionsKeyword, options); + } + + public static void SaveLUpdateOnBuild(EnvDTE.Project project) + { + SetBoolValue(project, Resources.lupdateKeyword, GetLUpdateOnBuild()); + } + + public static void SaveLUpdateOnBuild(EnvDTE.Project project, bool value) + { + SetBoolValue(project, Resources.lupdateKeyword, value); + } + + public static void SaveLUpdateOptions(EnvDTE.Project project, string options) + { + if (options == null) + options = GetLUpdateOptions(); + + SaveOption(project, Resources.lupdateOptionsKeyword, options); + } + + public static void SaveLUpdateOptions(string options) + { + SaveOption(Resources.lupdateOptionsKeyword, options); + } + + public static void SaveLReleaseOptions(EnvDTE.Project project, string options) + { + if (options == null) + options = GetLReleaseOptions(); + SaveOption(project, Resources.lreleaseOptionsKeyword, options); + } + + public static void SaveLReleaseOptions(string options) + { + SaveOption(Resources.lreleaseOptionsKeyword, options); + } + + public static string GetRccDirectory(EnvDTE.Project project) + { + return GetDirectory(project, Resources.rccDirKeyword); + } + + public static void SaveRccDirectory(string dir) + { + SaveDirectory(Resources.rccDirKeyword, dir); + } + + public static void SaveRccDirectory(EnvDTE.Project project, string directory) + { + if (directory == null) + SaveDirectory(project, Resources.rccDirKeyword, GetDirectory(project, Resources.rccDirKeyword)); + else + SaveDirectory(project, Resources.rccDirKeyword, directory); + } + + private static string GetDirectory(string type) + { + try { + var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath); + if (key != null) { + var path = (string)key.GetValue(type, null); + if (path != null) + return HelperFunctions.NormalizeRelativeFilePath(path); + } + } catch { } + if (type == Resources.mocDirKeyword) + return Resources.generatedFilesDir + "\\$(ConfigurationName)"; + return Resources.generatedFilesDir; + } + + private static string GetOption(string type) + { + try { + var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath); + if (key != null) { + var opt = (string)key.GetValue(type, null); + if (opt != null) + return opt; + } + } catch { } + return null; + } + + public static bool GetLUpdateOnBuild() + { + return GetBoolValue(Resources.lupdateKeyword, false); + } + + public static string GetRccDirectory() + { + return GetDirectory(Resources.rccDirKeyword); + } + + public static string GetUicDirectory() + { + return GetDirectory(Resources.uicDirKeyword); + } + + private static string GetDirectory(EnvDTE.Project project, string type) + { + // check for directory in following order: + // - stored in project + // - stored in cache + // - retrieve from moc/uic steps + // - globally defined default directory + // - fallback on hardcoded directory + if (project != null) { + if (project.Globals.get_VariablePersists(type)) + return HelperFunctions.NormalizeRelativeFilePath((string)project.Globals[type]); + + try { + if (type == Resources.mocDirKeyword && mocDirCache.Contains(project.FullName)) + return (string)mocDirCache[project.FullName]; + if (type == Resources.uicDirKeyword && uicDirCache.Contains(project.FullName)) + return (string)uicDirCache[project.FullName]; + if (type == Resources.rccDirKeyword && rccDirCache.Contains(project.FullName)) + return (string)rccDirCache[project.FullName]; + + QtCustomBuildTool tool = null; + string configName = null; + string platformName = null; + var vcpro = (VCProject)project.Object; + foreach (VCFile vcfile in (IVCCollection)vcpro.Files) { + var name = vcfile.Name; + if ((type == Resources.mocDirKeyword && HelperFunctions.IsHeaderFile(name)) + || (type == Resources.mocDirKeyword && HelperFunctions.IsMocFile(name)) + || (type == Resources.uicDirKeyword && HelperFunctions.IsUicFile(name)) + || (type == Resources.rccDirKeyword && HelperFunctions.IsQrcFile(name))) { + foreach (VCFileConfiguration config in (IVCCollection)vcfile.FileConfigurations) { + tool = new QtCustomBuildTool(config); + configName = config.Name.Remove(config.Name.IndexOf('|')); + var vcConfig = config.ProjectConfiguration as VCConfiguration; + var platform = vcConfig.Platform as VCPlatform; + platformName = platform.Name; + if (tool != null && (tool.CommandLine.IndexOf("moc.exe", StringComparison.OrdinalIgnoreCase) != -1 + || (tool.CommandLine.IndexOf("uic.exe", StringComparison.OrdinalIgnoreCase) != -1) + || (tool.CommandLine.IndexOf("rcc.exe", StringComparison.OrdinalIgnoreCase) != -1))) + break; + tool = null; + } + + if (tool != null) + break; + } + } + + if (tool != null) { + string dir = null; + var lastindex = tool.Outputs.LastIndexOf('\\'); + if (tool.Outputs.LastIndexOf('/') > lastindex) + lastindex = tool.Outputs.LastIndexOf('/'); + + if (lastindex == -1) + dir = "."; + else + dir = tool.Outputs.Substring(0, lastindex); + dir = dir.Replace("\"", ""); + + if (type == Resources.mocDirKeyword) { + int index; + if ((index = dir.IndexOf(configName, StringComparison.OrdinalIgnoreCase)) != -1) + dir = dir.Replace(dir.Substring(index, configName.Length), "$(ConfigurationName)"); + if ((index = dir.IndexOf(platformName, StringComparison.OrdinalIgnoreCase)) != -1) + dir = dir.Replace(dir.Substring(index, platformName.Length), "$(PlatformName)"); + + mocDirCache.Add(project.FullName, HelperFunctions.NormalizeRelativeFilePath(dir)); + } else if (type == Resources.uicDirKeyword) + uicDirCache.Add(project.FullName, HelperFunctions.NormalizeRelativeFilePath(dir)); + else if (type == Resources.rccDirKeyword) + rccDirCache.Add(project.FullName, HelperFunctions.NormalizeRelativeFilePath(dir)); + + cleanUpCache(project); + + return HelperFunctions.NormalizeRelativeFilePath(dir); + } + } catch { } + } + + return GetDirectory(type); + } + + private static string GetOption(EnvDTE.Project project, string type) + { + // check for directory in following order: + // - stored in project + // - globally defined default option + // - empty options + if (project != null && project.Globals.get_VariablePersists(type)) + return (string)project.Globals[type]; + return GetOption(type); + } + + private static bool GetBoolValue(EnvDTE.Project project, string type) + { + // check for directory in following order: + // - stored in project + // - globally defined default option + // - empty options + if (project != null && project.Globals.get_VariablePersists(type)) + return Convert.ToInt32(project.Globals[type] as string) > 0; + return GetBoolValue(type, false); + } + + private static void SaveDirectory(EnvDTE.Project project, string type, string dir) + { + dir = HelperFunctions.NormalizeRelativeFilePath(dir); + project.Globals[type] = dir; + if (!project.Globals.get_VariablePersists(type)) + project.Globals.set_VariablePersists(type, true); + + cleanUpCache(project); + } + + private static void SaveOption(EnvDTE.Project project, string type, string option) + { + project.Globals[type] = option; + if (!project.Globals.get_VariablePersists(type)) + project.Globals.set_VariablePersists(type, true); + } + + private static void SetBoolValue(EnvDTE.Project project, string type, bool value) + { + project.Globals[type] = Convert.ToInt32(value).ToString(); + if (!project.Globals.get_VariablePersists(type)) + project.Globals.set_VariablePersists(type, true); + } + + public static void SaveUicDirectory(string dir) + { + SaveDirectory(Resources.uicDirKeyword, dir); + } + + public static void SaveMocDirectory(string dir) + { + SaveDirectory(Resources.mocDirKeyword, dir); + } + + public static void SaveLUpdateOnBuild(bool val) + { + SetBoolValue(Resources.lupdateKeyword, val); + } + + public static void cleanUpCache(EnvDTE.Project project) + { + try { + var mocEnumerator = mocDirCache.GetEnumerator(); + while (mocEnumerator.MoveNext()) { + if (!HelperFunctions.IsProjectInSolution(project.DTE, (string)mocEnumerator.Key)) { + mocDirCache.Remove(mocEnumerator.Key); + mocEnumerator = mocDirCache.GetEnumerator(); + } + } + + var uicEnumerator = uicDirCache.GetEnumerator(); + while (uicEnumerator.MoveNext()) { + if (!HelperFunctions.IsProjectInSolution(project.DTE, (string)uicEnumerator.Key)) { + uicDirCache.Remove(uicEnumerator.Key); + uicEnumerator = uicDirCache.GetEnumerator(); + } + } + + var rccEnumerator = rccDirCache.GetEnumerator(); + while (rccEnumerator.MoveNext()) { + if (!HelperFunctions.IsProjectInSolution(project.DTE, (string)rccEnumerator.Key)) { + rccDirCache.Remove(rccEnumerator.Key); + rccEnumerator = rccDirCache.GetEnumerator(); + } + } + } catch { } + } + + private static void SaveDirectory(string type, string dir) + { + dir = HelperFunctions.NormalizeRelativeFilePath(dir); + var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + Resources.registryPackagePath); + if (key == null) + return; + key.SetValue(type, dir); + } + + private static void SaveOption(string type, string option) + { + var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + Resources.registryPackagePath); + if (key == null) + return; + if (option == null) + option = ""; + key.SetValue(type, option); + } + + public static bool AutoUpdateUicSteps() + { + if (ValueExists("AutoUpdateUicSteps")) + return GetBoolValue("AutoUpdateUicSteps", true); + return GetBoolValue("AutoUpdateBuildSteps", true); + } + + private static bool GetBoolValue(string key, bool defaultValue) + { + var regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath); + if (regKey == null) + return defaultValue; + return ((int)regKey.GetValue(key, defaultValue ? 1 : 0)) > 0; + } + + private static bool ValueExists(string key) + { + var regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + Resources.registryPackagePath); + if (regKey != null) { + foreach (var s in regKey.GetValueNames()) { + if (s == key) + return true; + } + } + return false; + } + + private static void SetBoolValue(string key, bool val) + { + var regKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + Resources.registryPackagePath); + if (regKey == null) + return; + regKey.SetValue(key, val ? 1 : 0); + } + + public static bool GetQmlDebug(EnvDTE.Project project) + { + return QtProject.Create(project).QmlDebug; + } + + public static void SaveQmlDebug(EnvDTE.Project project, bool enabled) + { + QtProject.Create(project).QmlDebug = enabled; + } + } +} diff --git a/QtVsTools.Core/QtVersionManager.cs b/QtVsTools.Core/QtVersionManager.cs new file mode 100644 index 0000000..000a198 --- /dev/null +++ b/QtVsTools.Core/QtVersionManager.cs @@ -0,0 +1,558 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.VCProjectEngine; +using Microsoft.Win32; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using QtVsTools.VisualStudio; +using EnvDTE; + +namespace QtVsTools.Core +{ + /// <summary> + /// Summary description for QtVersionManager. + /// </summary> + public class QtVersionManager + { + private static QtVersionManager instance; + private string regVersionPath; + private string strVersionKey; + private Hashtable versionCache; + + protected QtVersionManager() + { + strVersionKey = "Versions"; + regVersionPath = Resources.registryVersionPath; + RefreshVersionNames(); + } + + void RefreshVersionNames() + { + var rootKeyPath = "SOFTWARE\\" + Resources.registryRootPath; + try { + using (var rootKey = Registry.CurrentUser.OpenSubKey(rootKeyPath, true)) + using (var versionsKey = rootKey.OpenSubKey(strVersionKey, true)) { + versionsKey.SetValue("VersionNames", string.Join(";", GetVersions())); + } + + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + } + + static EventWaitHandle + packageInit = new EventWaitHandle(false, EventResetMode.ManualReset), + packageInitDone = null; + + static public QtVersionManager The(EventWaitHandle initDone = null) + { + if (initDone == null) { + packageInit.WaitOne(); + packageInitDone.WaitOne(); + } else { + packageInitDone = initDone; + packageInit.Set(); + } + + if (instance == null) + instance = new QtVersionManager(); + return instance; + } + + public VersionInformation GetVersionInfo(string name) + { + if (name == "$(DefaultQtVersion)") + name = GetDefaultVersion(); + if (name == null) + return null; + if (versionCache == null) + versionCache = new Hashtable(); + + var vi = versionCache[name] as VersionInformation; + if (vi == null) { + var qtdir = GetInstallPath(name); + versionCache[name] = vi = VersionInformation.Get(qtdir); + if (vi != null) + vi.name = name; + } + return vi; + } + + public VersionInformation GetVersionInfo(EnvDTE.Project project) + { + return GetVersionInfo(GetProjectQtVersion(project)); + } + + public void ClearVersionCache() + { + if (versionCache != null) + versionCache.Clear(); + } + + public string[] GetVersions() + { + return GetVersions(Registry.CurrentUser); + } + + public string GetQtVersionFromInstallDir(string qtDir) + { + if (qtDir == null) + return null; + + qtDir = qtDir.ToLower(); + var versions = GetVersions(); + foreach (var version in versions) { + var installPath = GetInstallPath(version); + if (installPath == null) + continue; + if (installPath.ToLower() == qtDir) + return version; + } + + return null; + } + + public string[] GetVersions(RegistryKey root) + { + var key = root.OpenSubKey("SOFTWARE\\" + Resources.registryRootPath, false); + if (key == null) + return new string[] { }; + var versionKey = key.OpenSubKey(strVersionKey, false); + if (versionKey == null) + return new string[] { }; + return versionKey.GetSubKeyNames(); + } + + /// <summary> + /// Check if all Qt versions are valid and readable. + /// </summary> + /// Also sets the default Qt version to the newest version, if needed. + /// <param name="errorMessage"></param> + /// <returns>true, if we found an invalid version</returns> + public bool HasInvalidVersions(out string errorMessage) + { + var validVersions = new Dictionary<string, QtConfig>(); + var invalidVersions = new List<string>(); + + foreach (var v in GetVersions()) { + if (v == "$(DefaultQtVersion)") + continue; + + var vPath = GetInstallPath(v); + if (string.IsNullOrEmpty(vPath)) { + invalidVersions.Add(v); + continue; + } + + if (vPath.StartsWith("SSH:") || vPath.StartsWith("WSL:")) + continue; + + var qmakePath = Path.Combine(vPath, "bin", "qmake.exe"); + if (!File.Exists(qmakePath)) + qmakePath = Path.Combine(vPath, "qmake.exe"); + if (!File.Exists(qmakePath)) { + invalidVersions.Add(v); + continue; + } + + validVersions[v] = new QtConfig(vPath); + } + + if (invalidVersions.Count > 0) { + errorMessage = "These Qt version are inaccessible:\n"; + foreach (var invalidVersion in invalidVersions) + errorMessage += invalidVersion + " in " + GetInstallPath(invalidVersion) + "\n"; + errorMessage += "Make sure that you have read access to all files in your Qt directories."; + + // Is the default Qt version invalid? + var isDefaultQtVersionInvalid = false; + var defaultQtVersionName = GetDefaultVersion(); + if (string.IsNullOrEmpty(defaultQtVersionName)) { + isDefaultQtVersionInvalid = true; + } else { + foreach (var name in invalidVersions) { + if (name == defaultQtVersionName) { + isDefaultQtVersionInvalid = true; + break; + } + } + } + + // find the newest valid Qt version that can be used as default version + if (isDefaultQtVersionInvalid && validVersions.Count > 0) { + QtConfig defaultQtVersionConfig = null; + foreach (var vNameConfig in validVersions) { + var vName = vNameConfig.Key; + var v = vNameConfig.Value; + if (defaultQtVersionConfig == null) { + defaultQtVersionConfig = v; + defaultQtVersionName = vName; + continue; + } + if (defaultQtVersionConfig.VersionMajor < v.VersionMajor || + (defaultQtVersionConfig.VersionMajor == v.VersionMajor && (defaultQtVersionConfig.VersionMinor < v.VersionMinor || + (defaultQtVersionConfig.VersionMinor == v.VersionMinor && defaultQtVersionConfig.VersionPatch < v.VersionPatch)))) { + defaultQtVersionConfig = v; + defaultQtVersionName = vName; + } + } + if (defaultQtVersionConfig != null) + SaveDefaultVersion(defaultQtVersionName); + } + + return true; + } + errorMessage = null; + return false; + } + + public string GetInstallPath(string version) + { + if (version == "$(DefaultQtVersion)") + version = GetDefaultVersion(); + return GetInstallPath(version, Registry.CurrentUser); + } + + public string GetInstallPath(string version, RegistryKey root) + { + if (version == "$(DefaultQtVersion)") + version = GetDefaultVersion(root); + if (version == "$(QTDIR)") + return Environment.GetEnvironmentVariable("QTDIR"); + + var key = root.OpenSubKey("SOFTWARE\\" + Resources.registryRootPath, false); + if (key == null) + return null; + var versionKey = key.OpenSubKey(strVersionKey + "\\" + version, false); + if (versionKey == null) + return null; + return (string)versionKey.GetValue("InstallDir"); + } + + public string GetInstallPath(EnvDTE.Project project) + { + var version = GetProjectQtVersion(project); + if (version == "$(DefaultQtVersion)") + version = GetDefaultVersion(); + if (version == null) + return null; + return GetInstallPath(version); + } + + public bool SaveVersion(string versionName, string path, bool checkPath = true) + { + var verName = versionName?.Trim().Replace(@"\", "_"); + if (string.IsNullOrEmpty(verName)) + return false; + var dir = string.Empty; + if (verName != "$(QTDIR)") { + DirectoryInfo di; + try { + di = new DirectoryInfo(path); + } catch { + di = null; + } + if (di?.Exists == true) { + dir = di.FullName; + } else if (!checkPath) { + dir = path; + } else { + return false; + } + } + + string rootKeyPath = "SOFTWARE\\" + Resources.registryRootPath; + string versionKeyPath = strVersionKey + "\\" + verName; + using (var key = Registry.CurrentUser.CreateSubKey(rootKeyPath)) { + if (key == null) { + Messages.Print( + "ERROR: root registry key creation failed"); + return false; + } + using (var versionKey = key.CreateSubKey(versionKeyPath)) { + if (versionKey == null) { + Messages.Print( + "ERROR: version registry key creation failed"); + return false; + } + versionKey.SetValue("InstallDir", dir); + } + } + RefreshVersionNames(); + return true; + } + + public void RemoveVersion(string versionName) + { + var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + regVersionPath, true); + if (key == null) + return; + key.DeleteSubKey(versionName); + key.Close(); + RefreshVersionNames(); + } + + private bool IsVersionAvailable(string version) + { + var versionAvailable = false; + var versions = GetVersions(); + foreach (var ver in versions) { + if (version == ver) { + versionAvailable = true; + break; + } + } + return versionAvailable; + } + + public bool SaveProjectQtVersion(EnvDTE.Project project, string version) + { + return SaveProjectQtVersion(project, version, project.ConfigurationManager.ActiveConfiguration.PlatformName); + } + + public bool SaveProjectQtVersion(EnvDTE.Project project, string version, string platform) + { + if (!IsVersionAvailable(version) && version != "$(DefaultQtVersion)") + return false; + if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) { + var vcPro = project.Object as VCProject; + if (vcPro == null) + return false; + foreach (VCConfiguration3 config in (IVCCollection)vcPro.Configurations) { + config.SetPropertyValue(Resources.projLabelQtSettings, true, + "QtInstall", version); + } + return true; + } + var key = "Qt5Version " + platform; + if (!project.Globals.get_VariableExists(key) || project.Globals[key].ToString() != version) + project.Globals[key] = version; + if (!project.Globals.get_VariablePersists(key)) + project.Globals.set_VariablePersists(key, true); + return true; + } + + public string GetProjectQtVersion(EnvDTE.Project project) + { + EnvDTE.Configuration config = null; + try { + config = project.ConfigurationManager.ActiveConfiguration; + } catch { + // Accessing the ActiveConfiguration property throws an exception + // if there's an "unconfigured" platform in the Solution platform combo box. + config = project.ConfigurationManager.Item(1); + } + var version = GetProjectQtVersion(project, config); + + if (version == null && project.Globals.get_VariablePersists("Qt5Version")) { + version = (string)project.Globals["Qt5Version"]; + ExpandEnvironmentVariablesInQtVersion(ref version); + return VerifyIfQtVersionExists(version) ? version : null; + } + + if (version == null) + version = GetSolutionQtVersion(project.DTE.Solution); + + return version; + } + + public string GetProjectQtVersion(EnvDTE.Project project, EnvDTE.Configuration config) + { + if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) + return QtProject.GetPropertyValue(project, config, "QtInstall"); + + var key = "Qt5Version " + config.PlatformName; + if (!project.Globals.get_VariablePersists(key)) + return null; + var version = (string)project.Globals[key]; + ExpandEnvironmentVariablesInQtVersion(ref version); + return VerifyIfQtVersionExists(version) ? version : null; + } + + public string GetProjectQtVersion(EnvDTE.Project project, string platform) + { + if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) + return GetProjectQtVersion(project); + + var key = "Qt5Version " + platform; + if (!project.Globals.get_VariablePersists(key)) + return null; + var version = (string)project.Globals[key]; + ExpandEnvironmentVariablesInQtVersion(ref version); + return VerifyIfQtVersionExists(version) ? version : null; + } + + private static void ExpandEnvironmentVariablesInQtVersion(ref string version) + { + if (version != "$(QTDIR)" && version != "$(DefaultQtVersion)") { + // Make it possible to specify the version name + // via an environment variable + var regExp = + new System.Text.RegularExpressions.Regex("\\$\\((?<VarName>\\S+)\\)"); + var match = regExp.Match(version); + if (match.Success) { + var env = match.Groups["VarName"].Value; + version = Environment.GetEnvironmentVariable(env); + } + } + } + + public bool SaveSolutionQtVersion(EnvDTE.Solution solution, string version) + { + if (!IsVersionAvailable(version) && version != "$(DefaultQtVersion)") + return false; + solution.Globals["Qt5Version"] = version; + if (!solution.Globals.get_VariablePersists("Qt5Version")) + solution.Globals.set_VariablePersists("Qt5Version", true); + return true; + } + + public string GetSolutionQtVersion(EnvDTE.Solution solution) + { + if (solution == null) + return null; + + if (solution.Globals.get_VariableExists("Qt5Version")) { + var version = (string)solution.Globals["Qt5Version"]; + return VerifyIfQtVersionExists(version) ? version : null; + } + + return null; + } + + public string GetDefaultVersion() + { + return GetDefaultVersion(Registry.CurrentUser); + } + + public string GetDefaultVersion(RegistryKey root) + { + string defaultVersion = null; + try { + var key = root.OpenSubKey("SOFTWARE\\" + regVersionPath, false); + if (key != null) + defaultVersion = (string)key.GetValue("DefaultQtVersion"); + } catch { + Messages.DisplayWarningMessage(SR.GetString("QtVersionManager_CannotLoadQtVersion")); + } + + if (defaultVersion == null) { + MergeVersions(); + var key = root.OpenSubKey("SOFTWARE\\" + regVersionPath, false); + if (key != null) { + var versions = GetVersions(); + if (versions != null && versions.Length > 0) + defaultVersion = versions[versions.Length - 1]; + if (defaultVersion != null) + SaveDefaultVersion(defaultVersion); + } + if (defaultVersion == null) { + // last fallback... try QTDIR + var qtDir = Environment.GetEnvironmentVariable("QTDIR"); + if (qtDir == null) + return null; + var d = new DirectoryInfo(qtDir); + SaveVersion(d.Name, d.FullName); + if (SaveDefaultVersion(d.Name)) + defaultVersion = d.Name; + } + } + return VerifyIfQtVersionExists(defaultVersion) ? defaultVersion : null; + } + + public bool SaveDefaultVersion(string version) + { + if (version == "$(DefaultQtVersion)") + return false; + var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + regVersionPath); + if (key == null) + return false; + key.SetValue("DefaultQtVersion", version); + return true; + } + + private void MergeVersions() + { + var hkcuVersions = GetVersions(); + var hklmVersions = GetVersions(Registry.LocalMachine); + + var hkcuInstDirs = new string[hkcuVersions.Length]; + for (var i = 0; i < hkcuVersions.Length; ++i) + hkcuInstDirs[i] = GetInstallPath(hkcuVersions[i]); + var hklmInstDirs = new string[hklmVersions.Length]; + for (var i = 0; i < hklmVersions.Length; ++i) + hklmInstDirs[i] = GetInstallPath(hklmVersions[i], Registry.LocalMachine); + + for (var i = 0; i < hklmVersions.Length; ++i) { + if (hklmInstDirs[i] == null) + continue; + + var found = false; + for (var j = 0; j < hkcuInstDirs.Length; ++j) { + if (hkcuInstDirs[j] != null + && hkcuInstDirs[j].ToLower() == hklmInstDirs[i].ToLower()) { + found = true; + break; + } + } + if (!found) { + for (var j = 0; j < hkcuVersions.Length; ++j) { + if (hkcuVersions[j] != null + && hkcuVersions[j] == hklmVersions[i]) { + found = true; + break; + } + } + if (!found) + SaveVersion(hklmVersions[i], hklmInstDirs[i]); + } + } + } + + private bool VerifyIfQtVersionExists(string version) + { + if (version == "$(DefaultQtVersion)") + version = GetDefaultVersion(); + if (!string.IsNullOrEmpty(version)) { + var regExp = + new System.Text.RegularExpressions.Regex("\\$\\(.*\\)"); + if (regExp.IsMatch(version)) + return true; + return Directory.Exists(GetInstallPath(version)); + } + + return false; + } + } +} diff --git a/QtVsTools.Core/QtVsTools.Core.csproj b/QtVsTools.Core/QtVsTools.Core.csproj new file mode 100644 index 0000000..c436e03 --- /dev/null +++ b/QtVsTools.Core/QtVsTools.Core.csproj @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <RootNamespace>QtVsTools.Core</RootNamespace> + <AssemblyName>QtVsTools.Core</AssemblyName> + <ProjectGuid>{2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}</ProjectGuid> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>QtVsTools.Core.ico</ApplicationIcon> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + <Reference Include="System" /> + <Reference Include="System.Drawing" /> + <Reference Include="System.Xml" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Windows.Forms" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="Microsoft.VisualStudio.SDK" + Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" /> + <PackageReference Include="Microsoft.VSSDK.BuildTools" + Version="$(Version_Microsoft_VSSDK_BuildTools)" /> + <PackageReference Include="Microsoft.Build" + Version="$(Version_Microsoft_Build)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'"> + <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" + Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'"> + <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" + Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'"> + <Reference Include="Microsoft.VisualStudio.VCProjectEngine" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="..\QtVsTools.RegExpr\QtVsTools.RegExpr.csproj"> + <Project>{a2831b9b-4d3b-46cb-85df-1b5c277c17db}</Project> + <Name>QtVsTools.RegExpr</Name> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <Compile Include="BuildConfig.cs" /> + <Compile Include="CommandLineParser.cs" /> + <Compile Include="Common\EnumExt.cs" /> + <Compile Include="CompilerToolWrapper.cs" /> + <Compile Include="CxxStreamReader.cs" /> + <Compile Include="ExportProjectDialog.cs"> + <SubType>Form</SubType> + </Compile> + <Compile Include="Extensions.cs" /> + <Compile Include="FakeFilter.cs" /> + <Compile Include="FilesToList.cs" /> + <Compile Include="Filters.cs" /> + <Compile Include="HelperFunctions.cs" /> + <Compile Include="ImageButton.cs"> + <SubType>Component</SubType> + </Compile> + <Compile Include="LinkerToolWrapper.cs" /> + <Compile Include="MainWinWrapper.cs" /> + <Compile Include="Messages.cs" /> + <Compile Include="MocCmdChecker.cs" /> + <Compile Include="MsBuildProject.cs" /> + <Compile Include="Observable.cs" /> + <Compile Include="ProFileContent.cs" /> + <Compile Include="ProFileOption.cs" /> + <Compile Include="ProjectExporter.cs" /> + <Compile Include="ProjectImporter.cs" /> + <Compile Include="ProjectMacros.cs" /> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + <Compile Include="ProSolution.cs" /> + <Compile Include="QMake.cs" /> + <Compile Include="QMakeConf.cs" /> + <Compile Include="QMakeQuery.cs" /> + <Compile Include="QrcItem.cs" /> + <Compile Include="QrcParser.cs" /> + <Compile Include="QrcPrefix.cs" /> + <Compile Include="QtConfig.cs" /> + <Compile Include="QtModule.cs" /> + <Compile Include="QtModules.cs" /> + <Compile Include="QtMsBuild.cs" /> + <Compile Include="QtProject.cs" /> + <Compile Include="QtVersionManager.cs" /> + <Compile Include="QtVSException.cs" /> + <Compile Include="QtVSIPSettings.cs" /> + <Compile Include="RccOptions.cs" /> + <Compile Include="Resources.cs" /> + <Compile Include="SR.cs" /> + <Compile Include="TemplateType.cs" /> + <Compile Include="VersionInformation.cs" /> + <Compile Include="VisualStudio\IProjectTracker.cs" /> + <Compile Include="VisualStudio\VsServiceProvider.cs" /> + <Compile Include="WaitDialog.cs" /> + <EmbeddedResource Include="ExportProjectDialog.resx"> + <DependentUpon>ExportProjectDialog.cs</DependentUpon> + </EmbeddedResource> + <Content Include="QtVsTools.Core.ico" /> + <EmbeddedResource Include="Resources.resx"> + </EmbeddedResource> + <EmbeddedResource Include="Resources\delete.png" /> + <EmbeddedResource Include="Resources\delete_d.png" /> + <EmbeddedResource Include="Resources\newitem.png" /> + <EmbeddedResource Include="Resources\newitem_d.png" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> diff --git a/QtVsTools.Core/QtVsTools.Core.ico b/QtVsTools.Core/QtVsTools.Core.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/QtVsTools.Core/QtVsTools.Core.ico Binary files differ diff --git a/QtVsTools.Core/RccOptions.cs b/QtVsTools.Core/RccOptions.cs new file mode 100644 index 0000000..44e8b2e --- /dev/null +++ b/QtVsTools.Core/RccOptions.cs @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.VCProjectEngine; +using System; + +namespace QtVsTools.Core +{ + /// <summary> + /// Summary description for RccOptions. + /// </summary> + public class RccOptions + { + private readonly EnvDTE.Project project; + private readonly string id; + private readonly string name; + private readonly string qrcFileName; + + public RccOptions(EnvDTE.Project pro, VCFile qrcFile) + { + project = pro; + id = qrcFile.RelativePath; + qrcFileName = qrcFile.FullPath; + name = id; + if (id.StartsWith(".\\", StringComparison.Ordinal)) + name = name.Substring(2); + if (HelperFunctions.IsQrcFile(name)) + name = name.Substring(0, name.Length - 4); + } + + public bool CompressFiles + { + get + { + if (project.Globals.get_VariablePersists("RccCompressFiles" + id) + && (string)project.Globals["RccCompressFiles" + id] == "true") + return true; + return false; + } + set + { + if (value) + project.Globals["RccCompressFiles" + id] = "true"; + else + project.Globals["RccCompressFiles" + id] = "false"; + if (!project.Globals.get_VariablePersists("RccCompressFiles" + id)) + project.Globals.set_VariablePersists("RccCompressFiles" + id, true); + } + } + + public int CompressLevel + { + get + { + if (project.Globals.get_VariablePersists("RccCompressLevel" + id)) + return Convert.ToInt32((string)project.Globals["RccCompressLevel" + id], 10); + return 0; + } + set + { + project.Globals["RccCompressLevel" + id] = value.ToString(); + if (!project.Globals.get_VariablePersists("RccCompressLevel" + id)) + project.Globals.set_VariablePersists("RccCompressLevel" + id, true); + } + } + + public int CompressThreshold + { + get + { + if (project.Globals.get_VariablePersists("RccCompressThreshold" + id)) + return Convert.ToInt32((string)project.Globals["RccCompressThreshold" + id], 10); + return 0; + } + set + { + project.Globals["RccCompressThreshold" + id] = value.ToString(); + if (!project.Globals.get_VariablePersists("RccCompressThreshold" + id)) + project.Globals.set_VariablePersists("RccCompressThreshold" + id, true); + } + } + } +} diff --git a/QtVsTools.Core/Resources.cs b/QtVsTools.Core/Resources.cs new file mode 100644 index 0000000..c8db0c9 --- /dev/null +++ b/QtVsTools.Core/Resources.cs @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + /// <summary> + /// Summary description for Resources. + /// </summary> + public static class Resources + { + // export things + public const string exportProHeader = +"# ----------------------------------------------------\r\n" + +"# This file is generated by the Qt Visual Studio Tools.\r\n" + +"# ------------------------------------------------------\r\n" + +"\r\n" + +"# This is a reminder that you are using a generated .pro file.\r\n" + +"# Remove it when you are finished editing this file.\r\n" + +"message(\"You are running qmake on a generated .pro file. This may not work!\")\r\n" + +"\r\n"; + + public const string exportSolutionHeader = +"# ----------------------------------------------------\r\n" + +"# This file is generated by the Qt Visual Studio Tools.\r\n" + +"# ------------------------------------------------------\r\n" + +"\r\n" + +"# This is a reminder that you are using a generated .pro file.\r\n" + +"# Remove it when you are finished editing this file.\r\n" + +"message(\"You are running qmake on a generated .pro file. This may not work!\")\r\n" + +"\r\n"; + + public const string exportPriHeader = +"# ----------------------------------------------------\r\n" + +"# This file is generated by the Qt Visual Studio Tools.\r\n" + +"# ------------------------------------------------------\r\n"; + + public const string ec_Template = "(TEMPLATE) Template."; + public const string ec_Translations = "(TRANSLATIONS) Translation files."; + public const string ec_rcFile = "(win32:RC_FILE) .rc file on windows."; + public const string ec_Target = "(TARGET) Target name."; + public const string ec_DestDir = "(DESTDIR) Destination directory."; + public const string ec_Qt = "(QT) Additional QT options."; + public const string ec_Config = "(CONFIG) Additional CONFIG options."; + public const string ec_IncludePath = "(INCLUDEPATH) Additional include paths."; + public const string ec_Libs = "(LIBS) Additional library dependencies."; + public const string ec_PrecompiledHeader = "(PRECOMPILED_HEADER) Using precompiled headers."; + public const string ec_DependPath = "(DEPENDPATH) Additional paths the project depends on."; + public const string ec_Include = "Included .pri files."; + public const string ec_Resources = "(RESOURCES) Resource files."; + public const string ec_ObjDir = "(OBJECTS_DIR) Location where obj files are placed."; + public const string ec_MocDir = "(MOC_DIR) Location where moc files are placed."; + public const string ec_UiDir = "(UI_DIR) Location where ui files are compiled to."; + public const string ec_RccDir = "(RCC_DIR) Location where qrc files are compiled to."; + public const string ec_Defines = "(DEFINES) Additional project defines."; + + // Old Qt VS project tag + public const string qtProjectV2Keyword = "Qt4VS"; + + // Qt VS project tag and format version + public const string qtProjectKeyword = "QtVS"; + public const int qtProjectFormatVersion = 304; + public static string QtVSVersionTag + => string.Format("{0}_v{1}", qtProjectKeyword, qtProjectFormatVersion); + + // Min. format version for Qt settings as project properties + public const int qtMinFormatVersion_Settings = 300; + + // Min. format version for shared compiler properties + public const int qtMinFormatVersion_ClProperties = 300; + + // Min. format version for global QtMsBuild property + public const int qtMinFormatVersion_GlobalQtMsBuildProperty = 302; + + // Min. format version for correct ordering of property evaluation (QTVSADDINBUG-787) + public const int qtMinFormatVersion_PropertyEval = 303; + + // Project properties labels + public const string projLabelGlobals = "Globals"; + public const string projLabelQtSettings = "QtSettings"; + + public const string uic4Command = "$(QTDIR)\\bin\\uic.exe"; + public const string moc4Command = "$(QTDIR)\\bin\\moc.exe"; + public const string rcc4Command = "$(QTDIR)\\bin\\rcc.exe"; + public const string lupdateCommand = "\\bin\\lupdate.exe"; + public const string lreleaseCommand = "\\bin\\lrelease.exe"; + + // All defined paths have to be relative to the project directory!!! + + public const string resourceDir = "Resources"; + + // If those directories do not equal to the project directory + // they have to be added to the include directories for + // compiling! + public const string generatedFilesDir = "GeneratedFiles"; + + public const string mocDirKeyword = "MocDir"; + public const string mocOptionsKeyword = "MocOptions"; + public const string uicDirKeyword = "UicDir"; + public const string rccDirKeyword = "RccDir"; + public const string lupdateKeyword = "lupdateOnBuild"; + public const string lupdateOptionsKeyword = "lupdateOptions"; + public const string lreleaseOptionsKeyword = "lreleaseOptions"; + public const string askBeforeCheckoutFileKeyword = "askBeforeCheckoutFile"; + public const string disableCheckoutFilesKeyword = "disableCheckoutFiles"; + public const string disableAutoMocStepsUpdateKeyword = "disableAutoMocStepsUpdate"; + + public const string registryRootPath = "Digia"; + +#if (VS2017 || VS2019 || VS2022) + public const string registryPackagePath = registryRootPath + "\\Qt5VS2017"; +#else +#error Unknown Visual Studio version! +#endif + public const string registryVersionPath = registryRootPath + "\\Versions"; + } +} diff --git a/QtVsTools.Core/Resources.resx b/QtVsTools.Core/Resources.resx new file mode 100644 index 0000000..7861f15 --- /dev/null +++ b/QtVsTools.Core/Resources.resx @@ -0,0 +1,306 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="ExportSolution" xml:space="preserve"> + <value>Export Solution</value> + </data> + <data name="OpenSolution" xml:space="preserve"> + <value>Open Solution</value> + </data> + <data name="ProjectExists" xml:space="preserve"> + <value>Project file already exists.</value> + </data> + <data name="ExportProject_EditProjectFileManually" xml:space="preserve"> + <value>qmake has generated a .vcproj file, but it needs to be converted. +To do this you must open the .vcproj file manually. +(Reason: qmake in Qt3 does not support generation of Visual Studio 2005 .vcproj files)</value> + </data> + <data name="ExportProject_ExistsOverwriteQuestion" xml:space="preserve"> + <value>{0} already exists. + +Do you want to overwrite it?</value> + </data> + <data name="ExportProject_ExportPriFile" xml:space="preserve"> + <value>Export to .pri File</value> + </data> + <data name="ExportProject_NoProjectsToExport" xml:space="preserve"> + <value>Cannot find any Qt 4 projects to export.</value> + </data> + <data name="ExportProject_PriFileContainsSpaces" xml:space="preserve"> + <value>The generated .pri file contains paths with spaces. You will not be able to import from this file. +1. Manually edit the generated .pri file. +2. Move your project to a location without spaces in the path. +3. Place the .pri file in another directory.</value> + </data> + <data name="ExportProject_ProjectExistsRegenerateOrReuse" xml:space="preserve"> + <value>{0} already exists. +Select 'Yes' to regenerate the file, and 'No' to use the existing one.</value> + </data> + <data name="ExportProject_ProjectOrSolutionCorrupt" xml:space="preserve"> + <value>{0} +(Maybe the .vcproj or .sln file is corrupt?)</value> + </data> + <data name="ExportProject_SelectQtProjectToAdd" xml:space="preserve"> + <value>Select a Qt Project to Add to the Solution</value> + </data> + <data name="ExportProject_SolutionProFileBuildIn" xml:space="preserve"> + <value>Do you want a solution .pro file to be built in {0}?</value> + </data> + <data name="ExportProject_SubdirsProfileSolutionClose" xml:space="preserve"> + <value>This seems to be a SUBDIRS .pro file. To open this file, the existing + solution needs to be closed (pending changes will be saved).</value> + </data> + <data name="ExportProjectDialog_Cancel" xml:space="preserve"> + <value>&Cancel</value> + </data> + <data name="ExportProjectDialog_OK" xml:space="preserve"> + <value>&OK</value> + </data> + <data name="ExportProjectDialog_CreatePro" xml:space="preserve"> + <value>Create .pro files for:</value> + </data> + <data name="ExportProjectDialog_Project" xml:space="preserve"> + <value>Project &tag:</value> + </data> + <data name="ExportProjectDialog_Open" xml:space="preserve"> + <value>Open Created Files</value> + </data> + <data name="ExportProjectDialog_CreatePri" xml:space="preserve"> + <value>Create .pri File</value> + </data> + <data name="ExportProjectDialog_Title" xml:space="preserve"> + <value>Export Project</value> + </data> + <data name="HelperFunctions_CannotWriteEnvQTDIR" xml:space="preserve"> + <value>Cannot write environment variable QTDIR.</value> + </data> + <data name="Messages_ErrorOccured" xml:space="preserve"> + <value>The following error occurred: +</value> + </data> + <data name="Messages_Warning" xml:space="preserve"> + <value>Warning: +</value> + </data> + <data name="Messages_SolveProblem" xml:space="preserve"> + <value> + +To solve this problem: +</value> + </data> + <data name="QtProject_CannotConstructWithoutValidProject" xml:space="preserve"> + <value>Cannot construct a QtProject object without a valid project.</value> + </data> + <data name="QtProject_CannotAddUicStep" xml:space="preserve"> + <value>Cannot add a uic step to file {0}</value> + </data> + <data name="QtProject_CannotAddMocStep" xml:space="preserve"> + <value>Cannot add a moc step to file {0}</value> + </data> + <data name="QtProject_CannotRemoveMocStep" xml:space="preserve"> + <value>Cannot remove a moc step from file {0}</value> + </data> + <data name="QtProject_CannotAddFilter" xml:space="preserve"> + <value>Project cannot add filter {0}</value> + </data> + <data name="QtProject_CannotAddFile" xml:space="preserve"> + <value>Cannot add file {0} to filter.</value> + </data> + <data name="QtProject_CannotRemoveFile" xml:space="preserve"> + <value>Cannot remove file {0} from filter.</value> + </data> + <data name="QtProject_DeletedFolderFullOrProteced" xml:space="preserve"> + <value>1. Maybe your deleted folder is full. +2. Or maybe it's write protected.</value> + </data> + <data name="QtProject_CannotFindFilter" xml:space="preserve"> + <value>Cannot find filter.</value> + </data> + <data name="QtProject_ProjectCannotAddFilter" xml:space="preserve"> + <value>Project cannot add filter {0}</value> + </data> + <data name="QtProject_ProjectCannotAddResourceFilter" xml:space="preserve"> + <value>Cannot add a resource filter.</value> + </data> + <data name="QtProject_CannotCreateResourceDir" xml:space="preserve"> + <value>Cannot create a resource directory.</value> + </data> + <data name="QtProject_CannotAdjustWhitespaces" xml:space="preserve"> + <value>Cannot adjust whitespace or tabs in file (write). +({0})</value> + </data> + <data name="QtProject_CannotReplaceTokenRead" xml:space="preserve"> + <value>Cannot replace token ({0} -> {1}) in file (read). +({3})</value> + </data> + <data name="QtProject_CannotReplaceTokenWrite" xml:space="preserve"> + <value>Cannot replace token ({0} -> {1}) in file (write). +({3})</value> + </data> + <data name="QtProject_FileExistsInProjectFolder" xml:space="preserve"> + <value>The file {0} exists in your project folder. Qt VS Tools will overwrite the existing file. + +Select 'Yes' to overwrite, select 'No' to keep the existing file and automatically add it to the project.</value> + </data> + <data name="QtVersionManager_CannotLoadQtVersion" xml:space="preserve"> + <value>Cannot load the default Qt version.</value> + </data> + <data name="Resources_QtVsTools" xml:space="preserve"> + <value>Qt VS Tools</value> + </data> + <data name="Resources_SourceFiles" xml:space="preserve"> + <value>Source Files</value> + </data> + <data name="Resources_HeaderFiles" xml:space="preserve"> + <value>Header Files</value> + </data> + <data name="Resources_FormFiles" xml:space="preserve"> + <value>Form Files</value> + </data> + <data name="Resources_ResourceFiles" xml:space="preserve"> + <value>Resource Files</value> + </data> + <data name="Resources_TranslationFiles" xml:space="preserve"> + <value>Translation Files</value> + </data> + <data name="Resources_GeneratedFiles" xml:space="preserve"> + <value>Generated Files</value> + </data> + <data name="QtProject_CannotFindCustomBuildTool" xml:space="preserve"> + <value>Could not find custom build tool for {0}.</value> + </data> + <data name="QtProject_CannotAccessUserFile" xml:space="preserve"> + <value>Could not add QTDIR to {0}'s .user file.</value> + </data> + <data name="ImportProject_CannotConvertProject" xml:space="preserve"> + <value>Could not convert project file {0} to Qt/MSBuild.</value> + </data> + <data name="Resources_OtherFiles" xml:space="preserve"> + <value>Other Files</value> + </data> + <data name="WaitDialogRefreshMoc" xml:space="preserve"> + <value>Updating moc steps...</value> + </data> + <data name="CannotChangeQtVersion" xml:space="preserve"> + <value>The Qt version of at least one project cannot be changed.</value> + </data> + <data name="DeleteGeneratedFilesError" xml:space="preserve"> + <value>The generated files cannot be deleted.</value> + </data> +</root> \ No newline at end of file diff --git a/QtVsTools.Core/Resources/delete.png b/QtVsTools.Core/Resources/delete.png new file mode 100644 index 0000000..e4139af --- /dev/null +++ b/QtVsTools.Core/Resources/delete.png Binary files differ diff --git a/QtVsTools.Core/Resources/delete_d.png b/QtVsTools.Core/Resources/delete_d.png new file mode 100644 index 0000000..9da7f82 --- /dev/null +++ b/QtVsTools.Core/Resources/delete_d.png Binary files differ diff --git a/QtVsTools.Core/Resources/newitem.png b/QtVsTools.Core/Resources/newitem.png new file mode 100644 index 0000000..7e26ea9 --- /dev/null +++ b/QtVsTools.Core/Resources/newitem.png Binary files differ diff --git a/QtVsTools.Core/Resources/newitem_d.png b/QtVsTools.Core/Resources/newitem_d.png new file mode 100644 index 0000000..1b33141 --- /dev/null +++ b/QtVsTools.Core/Resources/newitem_d.png Binary files differ diff --git a/QtVsTools.Core/SR.cs b/QtVsTools.Core/SR.cs new file mode 100644 index 0000000..ef66800 --- /dev/null +++ b/QtVsTools.Core/SR.cs @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Globalization; +using System.Resources; +using System.Threading; + +namespace QtVsTools.Core +{ + internal sealed class SR + { + static SR loader; + readonly ResourceManager resources; + static readonly Object obj = new Object(); + + internal SR() + { + resources = new ResourceManager("QtVsTools.Core.Resources", GetType().Assembly); + } + + private static SR GetLoader() + { + if (loader == null) { + lock (obj) { + if (loader == null) + loader = new SR(); + } + } + + return loader; + } + + private static CultureInfo Culture + { + get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; } + //get { return new CultureInfo("de-DE"); } + } + + public static string LanguageName + { + get { return Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; } + //get { return Culture.TwoLetterISOLanguageName; } + } + + public static string GetString(string name, params object[] args) + { + var sys = GetLoader(); + if (sys == null) + return null; + var res = sys.resources.GetString(name, Culture); + + if (args != null && args.Length > 0) + return string.Format(res, args); + return res; + } + + public static string GetString(string name) + { + var sys = GetLoader(); + if (sys == null) + return null; + return sys.resources.GetString(name, Culture); + } + } +} diff --git a/QtVsTools.Core/TemplateType.cs b/QtVsTools.Core/TemplateType.cs new file mode 100644 index 0000000..12f5782 --- /dev/null +++ b/QtVsTools.Core/TemplateType.cs @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Core +{ + // keep in sync with QtVsTools.Wizards.ProjectTemplateWizard.Options + + public struct TemplateType + { + // project type + public const uint ProjectType = 0x003; // 0011 + public const uint Application = 0x000; // 0000 + public const uint DynamicLibrary = 0x001; // 0001 + public const uint StaticLibrary = 0x002; // 0010 + // subsystem + public const uint GUISystem = 0x004; // 0100 + public const uint ConsoleSystem = 0x008; // 1000 + public const uint PluginProject = 0x100; + } +} diff --git a/QtVsTools.Core/VersionInformation.cs b/QtVsTools.Core/VersionInformation.cs new file mode 100644 index 0000000..fd8cc1a --- /dev/null +++ b/QtVsTools.Core/VersionInformation.cs @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace QtVsTools.Core +{ + [DebuggerDisplay("Name = {name}, Version = {qtMajor}.{qtMinor}.{qtPatch}")] + public class VersionInformation + { + //fields + public string name; + public string qtDir; + public uint qtMajor; // X in version x.y.z + public uint qtMinor; // Y in version x.y.z + public uint qtPatch; // Z in version x.y.z + public bool qt5Version = true; + private QtConfig qtConfig; + private QMakeConf qmakeConf; + private string vsPlatformName; + private static readonly Hashtable _cache = new Hashtable(); + + public static VersionInformation Get(string qtDir) + { + qtDir = qtDir ?? Environment.GetEnvironmentVariable("QTDIR"); + if (qtDir == null) + return null; + + try { + qtDir = new FileInfo(qtDir).FullName.ToUpperInvariant(); + } catch { + return null; + } + var versionInfo = _cache[qtDir] as VersionInformation; + if (versionInfo == null) { + versionInfo = new VersionInformation(qtDir); + _cache.Add(qtDir, versionInfo); + } else if (versionInfo.qtDir == null) { + versionInfo = new VersionInformation(qtDir); + _cache[qtDir] = versionInfo; + } + return versionInfo; + } + + public static void Clear() + { + _cache.Clear(); + } + + Dictionary<string, bool> _IsModuleAvailable; + public bool IsModuleAvailable(string module) + { + return _IsModuleAvailable?[module] ?? false; + } + + public string VC_MinimumVisualStudioVersion { get; private set; } + public string VC_ApplicationTypeRevision { get; private set; } + public string VC_WindowsTargetPlatformMinVersion { get; private set; } + public string VC_WindowsTargetPlatformVersion { get; private set; } + public string VC_Link_TargetMachine { get; private set; } + public string VC_PlatformToolset { get; private set; } + + private VersionInformation(string qtDirIn) + { + qtDir = qtDirIn; + + try { + var qmakeQuery = new QMakeQuery(this); + SetupPlatformSpecificData(qmakeQuery); + + // Find version number + var strVersion = qmakeQuery["QT_VERSION"]; + if (!string.IsNullOrEmpty(strVersion)) { + var versionParts = strVersion.Split('.'); + if (versionParts.Length != 3) { + qtDir = null; + return; + } + qtMajor = uint.Parse(versionParts[0]); + qtMinor = uint.Parse(versionParts[1]); + qtPatch = uint.Parse(versionParts[2]); + } else { + var inF = new StreamReader(Locate_qglobal_h()); + var rgxpVersion = new Regex("#define\\s*QT_VERSION\\s*0x(?<number>\\d+)", RegexOptions.Multiline); + var contents = inF.ReadToEnd(); + inF.Close(); + var matchObj = rgxpVersion.Match(contents); + if (!matchObj.Success) { + qtDir = null; + return; + } + + strVersion = matchObj.Groups[1].ToString(); + var version = Convert.ToUInt32(strVersion, 16); + qtMajor = version >> 16; + qtMinor = (version >> 8) & 0xFF; + qtPatch = version & 0xFF; + } + qt5Version = (qtMajor == 5); + + try { + QtInstallDocs = qmakeQuery["QT_INSTALL_DOCS"]; + } catch { } + } catch { + qtDir = null; + return; + } + + // Get VS project settings + try { + var tempProData = new StringBuilder(); + tempProData.AppendLine("SOURCES = main.cpp"); + + var modules = QtModules.Instance.GetAvailableModules() + .Where((QtModule mi) => mi.Selectable); + + foreach (QtModule mi in modules) { + tempProData.AppendLine(string.Format( + "qtHaveModule({0}): HEADERS += {0}.h", mi.proVarQT)); + } + + var randomName = Path.GetRandomFileName(); + var tempDir = Path.Combine(Path.GetTempPath(), randomName); + Directory.CreateDirectory(tempDir); + + var tempPro = Path.Combine(tempDir, string.Format("{0}.pro", randomName)); + File.WriteAllText(tempPro, tempProData.ToString()); + + var qmake = new QMakeImport(this, tempPro); + qmake.DisableWarnings = true; + qmake.Run(setVCVars: true); + + var tempVcxproj = Path.Combine(tempDir, string.Format("{0}.vcxproj", randomName)); + var msbuildProj = MsBuildProject.Load(tempVcxproj); + + Directory.Delete(tempDir, recursive: true); + + var availableModules = msbuildProj.GetItems("ClInclude") + .Select((string s) => Path.GetFileNameWithoutExtension(s)); + + _IsModuleAvailable = modules.ToDictionary( + (QtModule mi) => mi.proVarQT, + (QtModule mi) => availableModules.Contains(mi.proVarQT)); + + VC_MinimumVisualStudioVersion = + msbuildProj.GetProperty("MinimumVisualStudioVersion"); + VC_ApplicationTypeRevision = + msbuildProj.GetProperty("ApplicationTypeRevision"); + VC_WindowsTargetPlatformVersion = + msbuildProj.GetProperty("WindowsTargetPlatformVersion"); + VC_WindowsTargetPlatformMinVersion = + msbuildProj.GetProperty("WindowsTargetPlatformMinVersion"); + VC_PlatformToolset = + msbuildProj.GetProperty("PlatformToolset"); + VC_Link_TargetMachine = + msbuildProj.GetProperty("Link", "TargetMachine"); + + } catch (Exception e) { + throw new QtVSException("Error reading VS project settings", e); + } + } + + public string QtInstallDocs + { + get; private set; + } + + public string QMakeSpecDirectory + { + get { return qmakeConf.QMakeSpecDirectory; } + } + + public bool IsStaticBuild() + { + if (qtConfig == null) + qtConfig = new QtConfig(qtDir); + return qtConfig.BuildType == BuildType.Static; + } + + public string LibInfix() + { + if (qtConfig == null) + qtConfig = new QtConfig(qtDir); + return qtConfig.LibInfix; + } + + public string Namespace() + { + if (qtConfig == null) + qtConfig = new QtConfig(qtDir); + return qtConfig.Namespace; + } + + public string GetQMakeConfEntry(string entryName) + { + if (qmakeConf == null) + qmakeConf = new QMakeConf(this); + return qmakeConf.Entries[entryName].ToString(); + } + + /// <summary> + /// Returns the platform name in a way Visual Studio understands. + /// </summary> + public string GetVSPlatformName() + { + return vsPlatformName; + } + + /// <summary> + /// Read platform name from qmake.conf. + /// </summary> + private void SetupPlatformSpecificData(QMakeQuery qmakeQuery) + { + if (qmakeConf == null) + qmakeConf = new QMakeConf(this, qmakeQuery); + vsPlatformName = (is64Bit()) ? @"x64" : @"Win32"; + } + + private string Locate_qglobal_h() + { + string[] candidates = {qtDir + "\\include\\qglobal.h", + qtDir + "\\src\\corelib\\global\\qglobal.h", + qtDir + "\\include\\QtCore\\qglobal.h"}; + + foreach (var filename in candidates) { + if (File.Exists(filename)) { + // check whether we look at the real qglobal.h or just a "pointer" + var inF = new StreamReader(filename); + var rgxpVersion = new Regex("#include\\s+\"(.+global.h)\"", RegexOptions.Multiline); + var matchObj = rgxpVersion.Match(inF.ReadToEnd()); + inF.Close(); + if (!matchObj.Success) + return filename; + + if (matchObj.Groups.Count >= 2) { + var origCurrentDirectory = Directory.GetCurrentDirectory(); + Directory.SetCurrentDirectory(filename.Substring(0, filename.Length - 10)); // remove "\\qglobal.h" + var absIncludeFile = Path.GetFullPath(matchObj.Groups[1].ToString()); + Directory.SetCurrentDirectory(origCurrentDirectory); + if (File.Exists(absIncludeFile)) + return absIncludeFile; + } + } + } + + throw new QtVSException("qglobal.h not found"); + } + + public bool is64Bit() + { + if (qtConfig == null) + qtConfig = new QtConfig(qtDir); + return qtConfig.Is64Bit; + } + + public bool isWinRT() + { + var qmakeQuery = new QMakeQuery(this); + string qmakeXSpec; + try { + qmakeXSpec = qmakeQuery["QMAKE_XSPEC"]; + } catch { + throw new QtVSException("Error starting qmake process"); + } + + if (string.IsNullOrEmpty(qmakeXSpec)) + throw new QtVSException("Error: unexpected result of qmake query"); + + return qmakeXSpec.StartsWith("winrt"); + } + } +} diff --git a/QtVsTools.Core/VisualStudio/IProjectTracker.cs b/QtVsTools.Core/VisualStudio/IProjectTracker.cs new file mode 100644 index 0000000..8e13718 --- /dev/null +++ b/QtVsTools.Core/VisualStudio/IProjectTracker.cs @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.VisualStudio +{ + public interface IProjectTracker + { + void AddProject(EnvDTE.Project project); + } +} diff --git a/QtVsTools.Core/VisualStudio/VsServiceProvider.cs b/QtVsTools.Core/VisualStudio/VsServiceProvider.cs new file mode 100644 index 0000000..ed53ed5 --- /dev/null +++ b/QtVsTools.Core/VisualStudio/VsServiceProvider.cs @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.Shell; +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; + +namespace QtVsTools.VisualStudio +{ + using ServiceType = Tuple<Type, Type>; + + public interface IVsServiceProvider + { + I GetService<T, I>() where T : class where I : class; + Task<I> GetServiceAsync<T, I>() where T : class where I : class; + } + + public static class VsServiceProvider + { + public static IVsServiceProvider Instance { get; set; } + + static ConcurrentDictionary<ServiceType, object> services + = new ConcurrentDictionary<ServiceType, object>(); + + public static I GetService<I>() + where I : class + { + return GetService<I, I>(); + } + + public static I GetService<T, I>() + where T : class + where I : class + { + if (Instance == null) + return null; + + object serviceObj; + if (services.TryGetValue(new ServiceType(typeof(T), typeof(I)), out serviceObj)) + return serviceObj as I; + + var serviceInterface = Instance.GetService<T, I>(); + services.TryAdd(new ServiceType(typeof(T), typeof(I)), serviceInterface); + return serviceInterface; + } + + public static async Task<I> GetServiceAsync<I>() + where I : class + { + return await GetServiceAsync<I, I>(); + } + + public static async Task<I> GetServiceAsync<T, I>() + where T : class + where I : class + { + if (Instance == null) + return null; + + object serviceObj; + if (services.TryGetValue(new ServiceType(typeof(T), typeof(I)), out serviceObj)) + return serviceObj as I; + + var serviceInterface = await Instance.GetServiceAsync<T, I>(); + services.TryAdd(new ServiceType(typeof(T), typeof(I)), serviceInterface); + return serviceInterface; + } + } +} diff --git a/QtVsTools.Core/WaitDialog.cs b/QtVsTools.Core/WaitDialog.cs new file mode 100644 index 0000000..cff8851 --- /dev/null +++ b/QtVsTools.Core/WaitDialog.cs @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.VisualStudio; + +namespace QtVsTools.Core +{ + public class WaitDialog : IDisposable + { + static IVsThreadedWaitDialogFactory factory = null; + + public IVsThreadedWaitDialog2 VsWaitDialog { get; private set; } + + public bool Running { get; private set; } + + bool? vsDialogCanceled = null; + + public bool Canceled + { + get + { + if (vsDialogCanceled.HasValue) + return vsDialogCanceled.Value; + + if (VsWaitDialog == null) + return false; + + bool canceled = false; + int res = VsWaitDialog.HasCanceled(out canceled); + if (res != VSConstants.S_OK) + return false; + + return canceled; + } + private set + { + vsDialogCanceled = value; + } + } + + private WaitDialog() { } + + static WaitDialog Create(IVsThreadedWaitDialogFactory dialogFactory) + { + if (factory == null) { + factory = dialogFactory ?? VsServiceProvider + .GetService<SVsThreadedWaitDialogFactory, IVsThreadedWaitDialogFactory>(); + if (factory == null) + return null; + } + + IVsThreadedWaitDialog2 vsWaitDialog = null; + factory.CreateInstance(out vsWaitDialog); + if (vsWaitDialog == null) + return null; + + return new WaitDialog + { + VsWaitDialog = vsWaitDialog, + Running = true, + }; + } + + public static WaitDialog Start( + string caption, + string message, + string progressText = null, + string statusBarText = null, + int delay = 0, + bool isCancelable = false, + bool showMarqueeProgress = true, + IVsThreadedWaitDialogFactory dialogFactory = null) + { + var dialog = Create(dialogFactory); + if (dialog == null) + return null; + + var res = dialog.VsWaitDialog.StartWaitDialog(caption, message, progressText, + null, statusBarText, delay, isCancelable, showMarqueeProgress); + + if (res != VSConstants.S_OK) + return null; + + return dialog; + } + + public static WaitDialog StartWithProgress( + string caption, + string message, + int totalSteps, + int currentStep = 0, + string progressText = null, + string statusBarText = null, + int delay = 0, + bool isCancelable = false, + IVsThreadedWaitDialogFactory dialogFactory = null) + { + var dialog = Create(dialogFactory); + if (dialog == null) + return null; + + var res = dialog.VsWaitDialog.StartWaitDialogWithPercentageProgress( + caption, message, progressText, null, statusBarText, + isCancelable, delay, totalSteps, currentStep); + + if (res != VSConstants.S_OK) + return null; + + return dialog; + } + + public void Update( + string message, + int totalSteps, + int currentStep, + string progressText = null, + string statusBarText = null, + bool disableCancel = false) + { + if (!Running) + return; + + bool canceled = false; + int res = VsWaitDialog.UpdateProgress(message, progressText, + statusBarText, currentStep, totalSteps, disableCancel, out canceled); + + if (res != VSConstants.S_OK) + return; + + if (canceled) + Stop(); + } + + public void Stop() + { + if (!Running) + return; + + Running = false; + int canceled = 0; + VsWaitDialog.EndWaitDialog(out canceled); + Canceled = (canceled != 0); + } + + void IDisposable.Dispose() + { + Stop(); + } + } +} diff --git a/QtVsTools.Package/Common/Concurrent.cs b/QtVsTools.Package/Common/Concurrent.cs new file mode 100644 index 0000000..d1426f4 --- /dev/null +++ b/QtVsTools.Package/Common/Concurrent.cs @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Runtime.Serialization; +using System.Threading; + +namespace QtVsTools +{ + /// <summary> + /// Base class of objects requiring thread-safety features + /// </summary> + /// + [DataContract] + public abstract class Concurrent<TSubClass> + where TSubClass : Concurrent<TSubClass> + { + private static readonly object _StaticCriticalSection = new object(); + protected static object StaticCriticalSection => _StaticCriticalSection; + + private object _CriticalSection = null; + protected object CriticalSection => + StaticThreadSafeInit(() => _CriticalSection, () => _CriticalSection = new object()); + + protected T ThreadSafeInit<T>(Func<T> getValue, Action init) + where T : class + { + return StaticThreadSafeInit(getValue, init, this); + } + + protected static T StaticThreadSafeInit<T>( + Func<T> getValue, + Action init, + Concurrent<TSubClass> _this = null) + where T : class + { + // prevent global lock at every call + T value = getValue(); + if (value != null) + return value; + lock (_this?.CriticalSection ?? StaticCriticalSection) { + // prevent race conditions + value = getValue(); + if (value == null) { + init(); + value = getValue(); + } + return value; + } + } + + protected void EnterCriticalSection() + { + StaticEnterCriticalSection(this); + } + + protected static void StaticEnterCriticalSection(Concurrent<TSubClass> _this = null) + { + Monitor.Enter(_this?.CriticalSection ?? StaticCriticalSection); + } + + protected void LeaveCriticalSection() + { + StaticLeaveCriticalSection(this); + } + + protected static void StaticLeaveCriticalSection(Concurrent<TSubClass> _this = null) + { + if (Monitor.IsEntered(_this?.CriticalSection ?? StaticCriticalSection)) + Monitor.Exit(_this?.CriticalSection ?? StaticCriticalSection); + } + + protected void AbortCriticalSection() + { + StaticAbortCriticalSection(this); + } + + protected static void StaticAbortCriticalSection(Concurrent<TSubClass> _this = null) + { + while (Monitor.IsEntered(_this?.CriticalSection ?? StaticCriticalSection)) + Monitor.Exit(_this?.CriticalSection ?? StaticCriticalSection); + } + + protected void ThreadSafe(Action action) + { + StaticThreadSafe(action, this); + } + + protected static void StaticThreadSafe(Action action, Concurrent<TSubClass> _this = null) + { + lock (_this?.CriticalSection ?? StaticCriticalSection) { + action(); + } + } + + protected T ThreadSafe<T>(Func<T> func) + { + return StaticThreadSafe(func, this); + } + + protected static T StaticThreadSafe<T>(Func<T> func, Concurrent<TSubClass> _this = null) + { + lock (_this?.CriticalSection ?? StaticCriticalSection) { + return func(); + } + } + + protected bool Atomic(Func<bool> test, Action action) + { + return StaticAtomic(test, action, _this: this); + } + + protected bool Atomic(Func<bool> test, Action action, Action actionElse) + { + return StaticAtomic(test, action, actionElse, this); + } + + protected static bool StaticAtomic( + Func<bool> test, + Action action, + Action actionElse = null, + Concurrent<TSubClass> _this = null) + { + bool success = false; + lock (_this?.CriticalSection ?? StaticCriticalSection) { + success = test(); + if (success) + action(); + else if (actionElse != null) + actionElse(); + } + return success; + } + } + + /// <summary> + /// Base class of objects requiring thread-safety features + /// Sub-classes will share the same static critical section + /// </summary> + /// + [DataContract] + public class Concurrent : Concurrent<Concurrent> + { + } + + /// <summary> + /// Allows exclusive access to a wrapped variable. Reading access is always allowed. Concurrent + /// write requests are protected by mutex: only the first requesting thread will be granted + /// access; all other requests will be blocked until the value is reset (i.e. thread with + /// write access sets the variable's default value). + /// </summary> + /// <typeparam name="T">Type of wrapped variable</typeparam> + /// + class Exclusive<T> : Concurrent + { + private T value; + + public void Set(T newValue) + { + EnterCriticalSection(); + if (IsNull(value) && !IsNull(newValue)) { + value = newValue; + + } else if (!IsNull(value) && !IsNull(newValue)) { + value = newValue; + LeaveCriticalSection(); + + } else if (!IsNull(value) && IsNull(newValue)) { + value = default(T); + LeaveCriticalSection(); + LeaveCriticalSection(); + + } else { + LeaveCriticalSection(); + + } + } + + bool IsNull(T value) + { + if (typeof(T).IsValueType) + return value.Equals(default(T)); + else + return value == null; + } + + public void Release() + { + Set(default(T)); + } + + public static implicit operator T(Exclusive<T> _this) + { + return _this.value; + } + } +} diff --git a/QtVsTools.Package/Common/ConcurrentStopwatch.cs b/QtVsTools.Package/Common/ConcurrentStopwatch.cs new file mode 100644 index 0000000..82547e0 --- /dev/null +++ b/QtVsTools.Package/Common/ConcurrentStopwatch.cs @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Diagnostics; + +namespace QtVsTools +{ + public class ConcurrentStopwatch : Concurrent<ConcurrentStopwatch> + { + Stopwatch Stopwatch { get; set; } + + public ConcurrentStopwatch() + { + Stopwatch = new Stopwatch(); + } + + public static ConcurrentStopwatch StartNew() + { + ConcurrentStopwatch s = new ConcurrentStopwatch(); + s.Start(); + return s; + } + + public static long Frequency => Stopwatch.Frequency; + public static bool IsHighResolution => Stopwatch.IsHighResolution; + public bool IsRunning => ThreadSafe(() => Stopwatch.IsRunning); + public TimeSpan Elapsed => ThreadSafe(() => Stopwatch.Elapsed); + public long ElapsedMilliseconds => ThreadSafe(() => Stopwatch.ElapsedMilliseconds); + public long ElapsedTicks => ThreadSafe(() => Stopwatch.ElapsedTicks); + public void Reset() => ThreadSafe(() => Stopwatch.Reset()); + public void Restart() => ThreadSafe(() => Stopwatch.Restart()); + public void Start() => ThreadSafe(() => Stopwatch.Start()); + public void Stop() => ThreadSafe(() => Stopwatch.Stop()); + } +} diff --git a/QtVsTools.Package/Common/Disposable.cs b/QtVsTools.Package/Common/Disposable.cs new file mode 100644 index 0000000..c6c0fc2 --- /dev/null +++ b/QtVsTools.Package/Common/Disposable.cs @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace QtVsTools +{ + /// <summary> + /// Implemented by objects that wish to receive notifications of disposal of other objects + /// </summary> + /// + public interface IDisposableEventSink + { + void NotifyDisposing(IDisposable obj); + void NotifyDisposed(IDisposable obj); + } + + /// <summary> + /// Base class of object that implement the Dispose Pattern in a thread-safe manner + /// cf. https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern + /// </summary> + /// + [DataContract] + abstract class Disposable : Concurrent, IDisposable + { + protected const bool DisposingFrom_ObjectFinalizer = false; + protected const bool DisposingFrom_DisposeInterface = true; + + private HashSet<IDisposableEventSink> eventSinks = null; + private HashSet<IDisposableEventSink> EventSinks + { + get + { + return ThreadSafe(() => eventSinks != null ? eventSinks + : eventSinks = new HashSet<IDisposableEventSink>()); + } + } + + public bool Disposed { get; private set; } + + public bool Disposing { get; private set; } + + public void AdviseDispose(IDisposableEventSink sink) + { + ThreadSafe(() => EventSinks.Add(sink)); + } + + public void UnadviseDispose(IDisposableEventSink sink) + { + ThreadSafe(() => EventSinks.Remove(sink)); + } + + public void Dispose() + { + Dispose(DisposingFrom_DisposeInterface); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Override to dispose managed objects + /// </summary> + /// + protected virtual void DisposeManaged() + { } + + /// <summary> + /// Override to dispose unmanaged resources + /// </summary> + /// + protected virtual void DisposeUnmanaged() + { } + + /// <summary> + /// Override for clean-up procedure at the end of the disposal process + /// </summary> + /// + protected virtual void DisposeFinally() + { } + + /// <summary> + /// Override to block disposal + /// </summary> + /// + public virtual bool CanDispose { get { return true; } } + + protected virtual void Dispose(bool disposingFrom) + { + if (!Atomic(() => CanDispose && !Disposed && !Disposing, () => Disposing = true)) + return; + + ThreadSafe(() => EventSinks.ToList()) + .ForEach(sink => sink.NotifyDisposing(this)); + + if (disposingFrom == DisposingFrom_DisposeInterface) + DisposeManaged(); + + DisposeUnmanaged(); + + Disposed = true; + + DisposeFinally(); + + ThreadSafe(() => EventSinks.ToList()) + .ForEach(sink => sink.NotifyDisposed(this)); + } + } + + /// <summary> + /// Base class of disposable objects that need a finalizer method + /// cf. https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern#finalizable-types + /// </summary> + /// + [DataContract] + abstract class Finalizable : Disposable + { + ~Finalizable() + { + Dispose(DisposingFrom_ObjectFinalizer); + } + } +} diff --git a/QtVsTools.Package/Common/Json/DeferredObject.cs b/QtVsTools.Package/Common/Json/DeferredObject.cs new file mode 100644 index 0000000..9ecc47b --- /dev/null +++ b/QtVsTools.Package/Common/Json/DeferredObject.cs @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace QtVsTools.Json +{ + /// <summary> + /// Public interface of objects that allow deferred deserialization of their data. + /// </summary> + /// <typeparam name="TBase">Base type of deferred data</typeparam> + /// + public interface IDeferrable<TBase> + { + TBase Deserialize(IJsonData jsonData); + } + + /// <summary> + /// Provides deferred deserialization of a wrapped object, given the base class of a + /// prototyped class hierarchy that will be searched for the actual deserialization class. + /// </summary> + /// <typeparam name="TBase">Base of deferrable class hierarchy</typeparam> + /// + [DataContract] + class DeferredObject<TBase> : Disposable, IDeferredObject + where TBase : Prototyped<TBase>, IDeferrable<TBase> + { + private IJsonData jsonData; + + public TBase Object { get; private set; } + + object IDeferredObject.Object + { + get { return Object; } + } + + public bool HasData + { + get { return Object != null; } + } + + /// <summary> + /// This constructor is used when serializing, to directly wrap an existing object. + /// </summary> + /// <param name="obj">Object to wrap</param> + /// + public DeferredObject(TBase obj) + { + Object = obj; + } + + [OnDeserializing] // <-- Invoked by serializer before deserializing this object + void OnDeserializing(StreamingContext context) + { + // Store JSON data corresponding to this object + jsonData = Serializer.GetCurrentJsonData(); + } + + /// <summary> + /// Performs a deferred deserialization to obtain a new wrapped object corresponding to the + /// contents of the stored JSON data. The actual deserialization is delegated to the base + /// prototype of the class hierarchy. This prototype is then responsible to find an + /// appropriate class in the hierarchy and map the JSON data to an instance of the class. + /// </summary> + /// + public void Deserialize() + { + Atomic(() => Object == null && jsonData != null, () => + { + Object = Prototyped<TBase>.BasePrototype.Deserialize(jsonData); + jsonData.Dispose(); + jsonData = null; + }); + } + + protected override void DisposeManaged() + { + if (jsonData != null) + jsonData.Dispose(); + } + + public static implicit operator TBase(DeferredObject<TBase> _this) + { + return _this.Object; + } + } +} diff --git a/QtVsTools.Package/Common/Json/Serializable.cs b/QtVsTools.Package/Common/Json/Serializable.cs new file mode 100644 index 0000000..2d2b990 --- /dev/null +++ b/QtVsTools.Package/Common/Json/Serializable.cs @@ -0,0 +1,339 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace QtVsTools.Json +{ + /// <summary> + /// Classes in an hierarchy derived from Serializable<T> represent objects that can be mapped + /// to and from JSON data using the DataContractJsonSerializer class. When deserializing, the + /// class hierarchy will be searched for the derived class best suited to the data. + /// </summary> + /// <typeparam name="TBase">Base of the class hierarchy</typeparam> + [DataContract] + abstract class Serializable<TBase> : + Prototyped<TBase>, + IDeferrable<TBase>, + IDeferredObjectContainer + where TBase : Serializable<TBase> + { + #region //////////////////// Prototype //////////////////////////////////////////////////// + + protected Serializer Serializer { get; set; } + + protected Serializable() + { } + + protected sealed override void InitializePrototype() + { + System.Diagnostics.Debug.Assert(IsPrototype); + + // Create serializer for this particular type + Serializer = Serializer.Create(GetType()); + } + + /// <summary> + /// Check if this class is suited as target type for deserialization, based on the + /// information already deserialized. Prototypes of derived classes will override this to + /// implement the local type selection rules. + /// </summary> + /// <param name="that">Object containing the data deserialized so far</param> + /// <returns> + /// true ::= class is suitable and can be used as target type for deserialization + /// + /// false ::= class is not suitable for deserialization + /// + /// null ::= a derived class of this class might be suitable; search for target type + /// should be expanded to include all classes derived from this class + /// </returns> + /// + protected virtual bool? IsCompatible(TBase that) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + return null; + } + + /// <summary> + /// Check if this class is marked with the [SkipDeserialization] attribute, which signals + /// that deserialization of this class is to be skipped while traversing the class + /// hierarchy looking for a suitable target type for deserialization. + /// </summary> + /// + bool SkipDeserialization + { + get + { + System.Diagnostics.Debug.Assert(IsPrototype); + + return GetType() + .GetCustomAttributes(typeof(SkipDeserializationAttribute), false).Any(); + } + } + + /// <summary> + /// Perform a deferred deserialization based on this class hierarchy. + /// </summary> + /// <param name="jsonData">Data to deserialize</param> + /// <returns>Deserialized object</returns> + /// + TBase IDeferrable<TBase>.Deserialize(IJsonData jsonData) + { + System.Diagnostics.Debug.Assert(this == BasePrototype); + + return DeserializeClassHierarchy(null, jsonData); + } + + #endregion //////////////////// Prototype ///////////////////////////////////////////////// + + + #region //////////////////// Deferred Objects ///////////////////////////////////////////// + + List<IDeferredObject> deferredObjects = null; + List<IDeferredObject> DeferredObjects + { + get + { + Atomic(() => deferredObjects == null, + () => deferredObjects = new List<IDeferredObject>()); + + return deferredObjects; + } + } + + public IEnumerable<IDeferredObject> PendingObjects + { + get + { + return DeferredObjects.Where(x => !x.HasData); + } + } + + void IDeferredObjectContainer.Add(IDeferredObject item) + { + ThreadSafe(() => DeferredObjects.Add(item)); + } + + protected void Add(IDeferredObject item) + { + ((IDeferredObjectContainer)this).Add(item); + } + + #endregion //////////////////// Deferred Objects ////////////////////////////////////////// + + + /// <summary> + /// Initialize new instance. Derived classes override this to implement their own + /// initializations. + /// </summary> + /// + protected virtual void InitializeObject(object initArgs) + { } + + /// <summary> + /// Serialize object. + /// </summary> + /// <returns>Raw JSON data</returns> + /// + public byte[] Serialize() + { + return ThreadSafe(() => Prototype.Serializer.Serialize(this).GetBytes()); + } + + /// <summary> + /// Deserialize object using this class hierarchy. After selecting the most suitable derived + /// class as target type and deserializing an instance of that class, any deferred objects + /// are also deserialized using their respective class hierarchies. + /// </summary> + /// <param name="initArgs">Additional arguments required for object initialization</param> + /// <param name="data">Raw JSON data</param> + /// <returns>Deserialized object, or null if deserialization failed</returns> + /// + public static TBase Deserialize(object initArgs, byte[] data) + { + TBase obj = DeserializeClassHierarchy(initArgs, Serializer.Parse(data)); + if (obj == null) + return null; + + var toDo = new Queue<IDeferredObjectContainer>(); + if (obj.PendingObjects.Any()) + toDo.Enqueue(obj); + + while (toDo.Count > 0) { + var container = toDo.Dequeue(); + foreach (var defObj in container.PendingObjects) { + defObj.Deserialize(); + var subContainer = defObj.Object as IDeferredObjectContainer; + if (subContainer != null && subContainer.PendingObjects.Any()) + toDo.Enqueue(subContainer); + } + } + return obj; + } + + public static TBase Deserialize(byte[] data) + { + return Deserialize(null, data); + } + + /// <summary> + /// Traverse this class hierarchy looking for the most suitable derived class that can be + /// used as target type for the deserialization of the JSON data provided. + /// </summary> + /// <param name="initArgs">Additional arguments required for object initialization</param> + /// <param name="jsonData">Parsed JSON data</param> + /// <returns>Deserialized object, or null if deserialization failed</returns> + /// + protected static TBase DeserializeClassHierarchy(object initArgs, IJsonData jsonData) + { + // PSEUDOCODE: + // + // Nodes to visit := base of class hierarchy. + // While there are still nodes to visit + // Current node ::= Extract next node to visit. + // Tentative object := Deserialize using current node as target type. + // If deserialization failed + // Skip branch, continue (with next node, if any). + // Else + // Test compatibility of current node with tentative object. + // If not compatible + // Skip branch, continue (with next node, if any). + // If compatible + // If leaf node + // Found suitable node!! + // Return tentative object as final result of deserialization. + // Else + // Save tentative object as last sucessful deserialization. + // Add child nodes to the nodes to visit. + // If inconclusive (i.e. a child node might be compatible) + // Add child nodes to the nodes to visit. + // If no suitable node was found + // Return last sucessful deserialization as final result of deserialization. + + lock (BaseClass.Prototype.CriticalSection) { + + var toDo = new Queue<SubClass>(new[] { BaseClass }); + TBase lastCompatibleObj = null; + + // Traverse class hierarchy tree looking for a compatible leaf node + // i.e. compatible class without any sub-classes + while (toDo.Count > 0) { + var subClass = toDo.Dequeue(); + + // Try to deserialize as sub-class + TBase tryObj; + if (jsonData.IsEmpty()) + tryObj = CreateInstance(subClass.Type); + else + tryObj = subClass.Prototype.Serializer.Deserialize(jsonData) as TBase; + + if (tryObj == null) + continue; // Not deserializable as this type + + tryObj.InitializeObject(initArgs); + + // Test compatbility + var isCompatible = subClass.Prototype.IsCompatible(tryObj); + + if (isCompatible == false) { + // Incompatible + continue; + + } else if (isCompatible == true) { + // Compatible + + if (!subClass.SubTypes.Any()) + return tryObj; // Found compatible leaf node! + + // Non-leaf node; continue searching + lastCompatibleObj = tryObj; + PotentialSubClasses(subClass, tryObj) + .ForEach(x => toDo.Enqueue(x)); + continue; + + } else { + // Maybe has compatible derived class + + if (subClass.SubTypes.Any()) { + // Non-leaf node; continue searching + PotentialSubClasses(subClass, tryObj) + .ForEach(x => toDo.Enqueue(x)); + } + continue; + } + } + + // No compatible leaf node found + // Use last successful (non-leaf) deserializtion, if any + return lastCompatibleObj; + } + } + + /// <summary> + /// Get list of sub-classes of a particular class that are potentially suitable to the + /// deserialized data. Sub-classes marked with the [SkipDeserialization] attribute will not + /// be returned; their own sub-sub-classes will be tested for compatibility and returned in + /// case they are potentially suitable (i.e.: IsCompatible == true || IsCompatible == null) + /// </summary> + /// <param name="subClass">Class whose sub-classes are to be tested</param> + /// <param name="tryObj">Deserialized data</param> + /// <returns>List of sub-classes that are potentially suitable for deserialization</returns> + static List<SubClass> PotentialSubClasses(SubClass subClass, TBase tryObj) + { + if (subClass == null || tryObj == null) + return new List<SubClass>(); + + var potential = new List<SubClass>(); + var toDo = new Queue<SubClass>(subClass.SubClasses); + while (toDo.Count > 0) { + subClass = toDo.Dequeue(); + + if (subClass.Prototype.IsCompatible(tryObj) == false) + continue; + + if (subClass.Prototype.SkipDeserialization && subClass.SubClasses.Any()) { + foreach (var subSubClass in subClass.SubClasses) + toDo.Enqueue(subSubClass); + + continue; + } + + potential.Add(subClass); + } + + return potential; + } + } + + class SkipDeserializationAttribute : Attribute + { } +} diff --git a/QtVsTools.Package/Common/Json/SerializableEnum.cs b/QtVsTools.Package/Common/Json/SerializableEnum.cs new file mode 100644 index 0000000..6493668 --- /dev/null +++ b/QtVsTools.Package/Common/Json/SerializableEnum.cs @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.ComponentModel; +using System.Linq; +using System.Reflection; + +namespace QtVsTools.Json +{ + /// <summary> + /// Provide serialization/deserialization of enum values marked with the [EnumString] attribute + /// </summary> + static class SerializableEnum + { + public static string Serialize<TEnum>(TEnum enumValue) + where TEnum : struct + { + var type = enumValue.GetType(); + if (!type.IsEnum) + return enumValue.ToString(); + + var member = type.GetMember(enumValue.ToString()); + if (member == null || member.Length == 0) + return enumValue.ToString(); + + var attribs = member[0].GetCustomAttributes(typeof(EnumStringAttribute), false); + if (attribs == null || attribs.Length == 0) + return enumValue.ToString(); + + var attrib = attribs + .Where(x => x is EnumStringAttribute) + .FirstOrDefault() + as EnumStringAttribute; + if (attrib == null) + return enumValue.ToString(); + + return attrib.ValueString; + } + + public static TEnum Deserialize<TEnum>(string stringValue) + where TEnum : struct + { + if (!typeof(TEnum).IsEnum) + return default(TEnum); + + var members = typeof(TEnum).GetMembers(); + if (members == null || members.Length == 0) + return default(TEnum); + + var member = members + .Where(x => + { + var attribs = x.GetCustomAttributes(typeof(EnumStringAttribute), false); + if (attribs == null || attribs.Length == 0) + return false; + + var attrib = attribs + .Where(y => y is EnumStringAttribute) + .FirstOrDefault() + as EnumStringAttribute; + if (attrib == null) + return false; + + return attrib.ValueString == stringValue; + }) + .FirstOrDefault(); + + var field = member as FieldInfo; + if (field == null) + return default(TEnum); + + var objValue = field.GetValue(null); + if (!(objValue is TEnum)) + return default(TEnum); + + return (TEnum)objValue; + } + + } + + class EnumStringAttribute : Attribute + { + public string ValueString { get; private set; } + + public EnumStringAttribute(string enumValueString) + { + ValueString = enumValueString; + } + } +} diff --git a/QtVsTools.Package/Common/Json/Serializer.cs b/QtVsTools.Package/Common/Json/Serializer.cs new file mode 100644 index 0000000..d263207 --- /dev/null +++ b/QtVsTools.Package/Common/Json/Serializer.cs @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.CodeDom; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text; +using System.Xml; + +/// <summary> +/// The classes in this namespace provide support to the serialization and deserialization of +/// .NET objects using the JavaScript Object Notation (JSON) format. The transformation of +/// objects to and from JSON data is based on the DataContractJsonSerializer class provided +/// by the .NET framework, as documented in the following page: +/// +/// https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-serialize-and-deserialize-json-data +/// +/// To support the deserialization of polymorphic types, the concept of deferred deserialization +/// is introduced: if a field is marked for deferred deserialization, the corresponding JSON data +/// is not interpreted right way, but is rather stored for later processing, e.g. when the actual +/// type of the field can be determined. +/// </summary> +/// +namespace QtVsTools.Json +{ + /// <summary> + /// Public interface of objects representing JSON serialized data + /// </summary> + /// + public interface IJsonData : IDisposable + { + bool IsEmpty(); + byte[] GetBytes(); + } + + /// <summary> + /// Public interface of types providing deferred deserialization. + /// </summary> + /// + public interface IDeferredObject + { + object Object { get; } + bool HasData { get; } + void Deserialize(); + } + + /// <summary> + /// Public interface of types containing deferred-deserialized objects + /// </summary> + /// + public interface IDeferredObjectContainer + { + void Add(IDeferredObject defObj); + IEnumerable<IDeferredObject> PendingObjects { get; } + } + + /// <summary> + /// A Serializer object allows the serialization and deserialization of objects using the JSON + /// format, by extending the services provided by the DataContractJsonSerializer class. + /// </summary> + /// + class Serializer : Concurrent + { + private DataContractJsonSerializer serializer; + + public static Serializer Create(Type type) + { + var _this = new Serializer(); + return _this.Initialize(type) ? _this : null; + } + + private Serializer() + { } + + private bool Initialize(Type type) + { + var settings = new DataContractJsonSerializerSettings(); + if (settings == null) + return false; + + settings.DataContractSurrogate = new DataContractSurrogate { Serializer = this }; + settings.EmitTypeInformation = EmitTypeInformation.Never; + settings.UseSimpleDictionaryFormat = true; + + serializer = new DataContractJsonSerializer(type, settings); + if (serializer == null) + return false; + + return true; + } + + public IJsonData Serialize(object obj) + { + var stream = new MemoryStream(); + using (var writer = JsonReaderWriterFactory.CreateJsonWriter(stream)) { + try { + serializer.WriteObject(writer, obj); + writer.Close(); + return new JsonData() { Stream = stream }; + } catch (Exception e) { + if (stream != null && stream.CanRead && stream.Length > 0) + stream.Dispose(); + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return null; + } + } + } + + public object Deserialize(IJsonData jsonData) + { + var data = jsonData as JsonData; + if (data == null) + return null; + + if (data.XmlStream == null && !Parse(data)) + return null; + + lock (CriticalSection) { + try { + using (reader = XmlReader.Create(data.XmlStream)) { + var obj = serializer.ReadObject(reader, false); + + var container = obj as IDeferredObjectContainer; + if (container != null) + deferredObjects.ForEach(x => container.Add(x)); + + return obj; + } + + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return null; + + } finally { + reader = null; + deferredObjects.Clear(); + data.XmlStream.Position = 0; + } + } + } + + /// <summary> + /// Parses raw JSON data and returns the corresponding IJsonData object. + /// </summary> + /// <param name="rawJsonData">Raw JSON data</param> + /// <returns>IJsonData object corresponding to the data provided</returns> + /// + public static IJsonData Parse(byte[] rawJsonData) + { + if (rawJsonData == null) + rawJsonData = new byte[0]; + + var data = new JsonData() + { + Stream = new MemoryStream(rawJsonData) + }; + + if (!Parse(data)) { + data.Dispose(); + return null; + } + + return data; + } + + private static bool Parse(JsonData data) + { + try { + var q = new XmlDictionaryReaderQuotas(); + using (var reader = JsonReaderWriterFactory.CreateJsonReader(data.Stream, q)) { + reader.Read(); + var xmlData = Encoding.UTF8.GetBytes(reader.ReadOuterXml()); + reader.Close(); + data.XmlStream = new MemoryStream(xmlData); + } + return true; + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return false; + } + } + + #region //////////////////// JsonData ///////////////////////////////////////////////////// + + private class JsonData : Disposable, IJsonData + { + public MemoryStream Stream { get; set; } + public MemoryStream XmlStream { get; set; } + + byte[] IJsonData.GetBytes() + { + return Stream.ToArray(); + } + + bool IJsonData.IsEmpty() + { + return (Stream == null || !Stream.CanRead || Stream.Length == 0) + && (XmlStream == null || !XmlStream.CanRead || XmlStream.Length == 0); + } + + protected override void DisposeManaged() + { + if (Stream != null) + Stream.Dispose(); + if (XmlStream != null) + XmlStream.Dispose(); + } + } + + #endregion //////////////////// JsonData ////////////////////////////////////////////////// + + + #region //////////////////// Data Contract Surrogate ////////////////////////////////////// + + static Exclusive<Serializer> sharedInstance = new Exclusive<Serializer>(); + private XmlReader reader = null; + private List<IDeferredObject> deferredObjects = new List<IDeferredObject>(); + + public static IJsonData GetCurrentJsonData() + { + Serializer _this = sharedInstance; + try { + var root = new StringBuilder(); + root.Append("<root type=\"object\">"); + while (_this.reader.IsStartElement()) + root.Append(_this.reader.ReadOuterXml()); + root.Append("</root>"); + var xmlData = Encoding.UTF8.GetBytes(root.ToString()); + return new JsonData { XmlStream = new MemoryStream(xmlData) }; + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return null; + } + } + + class DataContractSurrogate : IDataContractSurrogate + { + public Serializer Serializer { get; set; } + + Type IDataContractSurrogate.GetDataContractType(Type type) + { + if (typeof(IDeferredObject).IsAssignableFrom(type)) { + // About to process a deferred object: lock shared serializer + sharedInstance.Set(Serializer); + } + return type; + } + + object IDataContractSurrogate.GetDeserializedObject(object obj, Type targetType) + { + if (typeof(IDeferredObject).IsAssignableFrom(targetType)) { + // Deferred object deserialized: add to list of deferred objects... + Serializer.deferredObjects.Add(obj as IDeferredObject); + + // ...and release shared serializer + sharedInstance.Release(); + } + return obj; + } + + object IDataContractSurrogate.GetObjectToSerialize(object obj, Type targetType) + { + if (obj is IDeferredObject) { + // Deferred object serialized: release shared serializer + sharedInstance.Release(); + + return (obj as IDeferredObject).Object; + } + return obj; + } + + object IDataContractSurrogate.GetCustomDataToExport( + MemberInfo memberInfo, + Type dataContractType) + { throw new NotImplementedException(); } + + object IDataContractSurrogate.GetCustomDataToExport( + Type clrType, + Type dataContractType) + { throw new NotImplementedException(); } + + Type IDataContractSurrogate.GetReferencedTypeOnImport( + string typeName, + string typeNamespace, + object customData) + { throw new NotImplementedException(); } + + CodeTypeDeclaration IDataContractSurrogate.ProcessImportedType( + CodeTypeDeclaration typeDeclaration, + CodeCompileUnit compileUnit) + { throw new NotImplementedException(); } + + void IDataContractSurrogate.GetKnownCustomDataTypes(Collection<Type> customDataTypes) + { throw new NotImplementedException(); } + } + + #endregion //////////////////// Data Contract Surrogate /////////////////////////////////// + + } +} diff --git a/QtVsTools.Package/Common/NativeAPI.cs b/QtVsTools.Package/Common/NativeAPI.cs new file mode 100644 index 0000000..155db09 --- /dev/null +++ b/QtVsTools.Package/Common/NativeAPI.cs @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace QtVsTools +{ + public static class NativeAPI + { + public const int GWL_STYLE = -16; + public const int GWL_EXSTYLE = -20; + public const int WS_VISIBLE = 0x10000000; + public const int WM_CLOSE = 0x10; + public const int WM_STYLECHANGED = 0x007D; + public const int WM_GETICON = 0x007F; + public const int WM_SETICON = 0x0080; + public const int ICON_SMALL = 0; + public const int GCL_HICON = -14; + public const int GCL_HICONSM = -34; + public const int SW_HIDE = 0; + public const int SW_SHOWMINNOACTIVE = 7; + public const int SW_RESTORE = 9; + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool EnumThreadWindows( + uint dwThreadId, + EnumThreadWindowsCallback lpfn, + IntPtr lParam); + + public delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "GetWindowTextW")] + public static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int maxCount); + + public static string GetWindowCaption(IntPtr hwnd) + { + var caption = new StringBuilder(256); + if (GetWindowText(hwnd, caption, caption.Capacity) > 0) + return caption.ToString(); + else + return string.Empty; + } + + [DllImport("user32.dll")] + public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); + + [DllImport("user32.dll", EntryPoint = "GetWindowLongW", CharSet = CharSet.Unicode)] + public static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll", EntryPoint = "GetClassLongW", CharSet = CharSet.Unicode)] + public static extern int GetClassLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + public static extern bool MoveWindow( + IntPtr Handle, + int x, int y, + int w, int h, + bool repaint); + + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); + + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern IntPtr SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + public static extern int SHGetFileInfo( + string pszPath, + int dwFileAttributes, + ref SHFILEINFO psfi, + int cbfileInfo, + SHGFI uFlags); + + [DllImport("user32.dll", SetLastError = true)] + public static extern bool DestroyIcon(IntPtr hIcon); + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct SHFILEINFO + { + /// <summary>Maximal Length of unmanaged Windows-Path-strings</summary> + public const int MAX_PATH = 260; + /// <summary>Maximal Length of unmanaged Typename</summary> + public const int MAX_TYPE = 80; + public SHFILEINFO(bool dummy = true) + { + hIcon = IntPtr.Zero; + iIcon = 0; + dwAttributes = 0; + szDisplayName = ""; + szTypeName = ""; + } + public IntPtr hIcon; + public int iIcon; + public uint dwAttributes; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] + public string szDisplayName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_TYPE)] + public string szTypeName; + }; + + [Flags] + public enum SHGFI : int + { + /// <summary>get icon</summary> + Icon = 0x000000100, + /// <summary>get display name</summary> + DisplayName = 0x000000200, + /// <summary>get type name</summary> + TypeName = 0x000000400, + /// <summary>get attributes</summary> + Attributes = 0x000000800, + /// <summary>get icon location</summary> + IconLocation = 0x000001000, + /// <summary>return exe type</summary> + ExeType = 0x000002000, + /// <summary>get system icon index</summary> + SysIconIndex = 0x000004000, + /// <summary>put a link overlay on icon</summary> + LinkOverlay = 0x000008000, + /// <summary>show icon in selected state</summary> + Selected = 0x000010000, + /// <summary>get only specified attributes</summary> + Attr_Specified = 0x000020000, + /// <summary>get large icon</summary> + LargeIcon = 0x000000000, + /// <summary>get small icon</summary> + SmallIcon = 0x000000001, + /// <summary>get open icon</summary> + OpenIcon = 0x000000002, + /// <summary>get shell size icon</summary> + ShellIconSize = 0x000000004, + /// <summary>pszPath is a pidl</summary> + PIDL = 0x000000008, + /// <summary>use passed dwFileAttribute</summary> + UseFileAttributes = 0x000000010, + /// <summary>apply the appropriate overlays</summary> + AddOverlays = 0x000000020, + /// <summary>Get the index of the overlay in the upper 8 bits of the iIcon</summary> + OverlayIndex = 0x000000040, + } + } +} diff --git a/QtVsTools.Package/Common/PriorityQueue.cs b/QtVsTools.Package/Common/PriorityQueue.cs new file mode 100644 index 0000000..cdc57e7 --- /dev/null +++ b/QtVsTools.Package/Common/PriorityQueue.cs @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace QtVsTools +{ + public class PriorityQueue<T, TPriority> : BasePriorityQueue<T, TPriority> + where TPriority : IComparable<TPriority> + { + public PriorityQueue() : base() + { } + + public PriorityQueue(Func<T, object> getItemKey) : base(getItemKey) + { } + + public new void Enqueue(T item, TPriority priority) + { + base.Enqueue(item, priority); + } + } + + public abstract class BasePriorityQueue<T, TPriority> : Concurrent, IEnumerable<T> + where TPriority : IComparable<TPriority> + { + SortedDictionary<TPriority, T> ItemsByPriority { get; set; } + Dictionary<object, TPriority> ItemPriority { get; set; } + T Head { get; set; } + public int Count { get; private set; } + public bool IsEmpty => (Count == 0); + + IEnumerable<T> Items => ThreadSafe(() => ItemsByPriority.Values.ToList()); + IEnumerator<T> IEnumerable<T>.GetEnumerator() => Items.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Items.GetEnumerator(); + + Func<T, object> GetItemKey { get; set; } + + public BasePriorityQueue() : this(x => x) + { } + + public BasePriorityQueue(Func<T, object> getItemKey) + { + ItemsByPriority = new SortedDictionary<TPriority, T>(); + ItemPriority = new Dictionary<object, TPriority>(); + Head = default(T); + Count = 0; + GetItemKey = getItemKey; + } + + public void Clear() + { + lock (CriticalSection) { + if (Count == 0) + return; + ItemsByPriority.Clear(); + ItemPriority.Clear(); + Head = default(T); + Count = 0; + } + } + + public bool Contains(T item) + { + lock (CriticalSection) { + return ItemPriority.ContainsKey(GetItemKey(item)); + } + } + + // Base Enqueue() is protected to allow specialized implementations to + // hide the concept of priority (e.g. PunisherQueue). + // + protected void Enqueue(T item, TPriority priority) + { + if (item == null) + throw new InvalidOperationException("Item cannot be null."); + lock (CriticalSection) { + T oldItem; + if (ItemsByPriority.TryGetValue(priority, out oldItem) && !item.Equals(oldItem)) + throw new InvalidOperationException("An item with the same priority exists."); + TPriority oldPriority; + if (ItemPriority.TryGetValue(GetItemKey(item), out oldPriority)) { + ItemsByPriority.Remove(oldPriority); + --Count; + } + ItemPriority[GetItemKey(item)] = priority; + ItemsByPriority[priority] = item; + Head = ItemsByPriority.First().Value; + ++Count; + } + } + + public bool TryPeek(out T result) + { + lock (CriticalSection) { + result = Head; + return Count > 0; + } + } + + public T Peek() + { + lock (CriticalSection) { + T result; + if (!TryPeek(out result)) + throw new InvalidOperationException("Queue is empty."); + return result; + } + } + + public void Remove(T item) + { + lock (CriticalSection) { + object key = GetItemKey(item); + if (key == null) + return; + ItemsByPriority.Remove(ItemPriority[key]); + ItemPriority.Remove(key); + --Count; + if (key == GetItemKey(Head)) { + if (IsEmpty) + Head = default(T); + else + Head = ItemsByPriority.First().Value; + } + } + } + + public bool TryDequeue(out T result) + { + lock (CriticalSection) { + result = Head; + if (IsEmpty) + return false; + Remove(Head); + return true; + } + } + + public T Dequeue() + { + lock (CriticalSection) { + T result; + if (!TryDequeue(out result)) + throw new InvalidOperationException("Queue is empty."); + return result; + } + } + } +} diff --git a/QtVsTools.Package/Common/Prototyped.cs b/QtVsTools.Package/Common/Prototyped.cs new file mode 100644 index 0000000..cba7019 --- /dev/null +++ b/QtVsTools.Package/Common/Prototyped.cs @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; + +namespace QtVsTools +{ + /// <summary> + /// Class hierarchies derived from Prototyped<T> will have, for each class in the hierarchy, a + /// special prototype instance that will represent that class. This is especially useful when + /// traversing a class hierarchy looking for an appropriate class, e.g. for deserializing data. + /// + /// To qualify as prototyped, the base class cannot be generic, no class in the hierarchy can + /// be abstract and all classes must define a default constructor. + /// </summary> + /// <typeparam name="TBase">Base class of the prototyped class hierarchy</typeparam> + /// + [DataContract] + abstract class Prototyped<TBase> : Concurrent + where TBase : Prototyped<TBase> + { + protected bool IsPrototype { get; private set; } + + protected static TBase CreateInstance(Type type) + { + if (typeof(TBase).ContainsGenericParameters) + throw new NotSupportedException("Generic base class: " + typeof(TBase).Name); + + if (!typeof(TBase).IsAssignableFrom(type)) + throw new NotSupportedException("Not a derived type: " + type.Name); + + if (type.IsAbstract) + throw new NotSupportedException("Abstract class: " + type.Name); + + if (type.ContainsGenericParameters) + throw new NotSupportedException("Generic class: " + type.Name); + + var ctorInfo = ((TypeInfo)type).DeclaredConstructors + .Where(x => x.GetParameters().Length == 0) + .FirstOrDefault(); + + if (ctorInfo == null) + throw new NotSupportedException("No default constructor: " + type.Name); + + return ctorInfo.Invoke(new object[0]) as TBase; + } + + private static TBase CreatePrototype(Type type) + { + var obj = CreateInstance(type); + obj.IsPrototype = true; + obj.InitializePrototype(); + return obj; + } + + protected virtual void InitializePrototype() + { + System.Diagnostics.Debug.Assert(IsPrototype); + } + + public TBase Prototype + { + get { return ThisClass.Prototype; } + } + + SubClass thisClass = null; + protected SubClass ThisClass + { + get + { + Atomic(() => thisClass == null, () => thisClass = SubClass.Get(GetType())); + return thisClass; + } + } + + protected static SubClass BaseClass + { + get { return SubClass.baseClass; } + } + + public static TBase BasePrototype + { + get { return BaseClass.Prototype; } + } + + #region //////////////////// SubClass ///////////////////////////////////////////////////// + + /// <summary> + /// Each class in the prototyped hierarchy will have a SubClass object that represents it. + /// This object contains the class Type, its sub classes and the prototype instance. + /// </summary> + /// + protected sealed class SubClass + { + public Type Type { get; set; } + public TBase Prototype { get; set; } + public IEnumerable<Type> SubTypes { get; set; } + public IEnumerable<SubClass> SubClasses + { + get + { + return SubTypes.Select(x => Get(x)).Where(x => x != null); + } + } + + static readonly object classCriticalSection = new object(); + + static Dictionary<Type, List<Type>> types = GetTypeHierarchy(typeof(TBase)); + + static Dictionary<Type, List<Type>> GetTypeHierarchy(Type baseType) + { + var subTypes = Assembly.GetExecutingAssembly().GetTypes() + .Where(x => baseType.IsAssignableFrom(x) + && x.IsAbstract == false + && x.ContainsGenericParameters == false) + .ToDictionary(x => x, x => new List<Type>()); + + var toDo = new Queue<Type>(subTypes.Keys); + var seen = new HashSet<Type>(subTypes.Keys); + + while (toDo.Count > 0) { + var type = toDo.Dequeue(); + + if (!typeof(TBase).IsAssignableFrom(type.BaseType)) + continue; + + if (type.BaseType.IsAbstract) + throw new NotSupportedException("Abstract class: " + type.BaseType.Name); + + if (type.BaseType.ContainsGenericParameters) + throw new NotSupportedException("Generic class: " + type.BaseType.Name); + + if (!subTypes.ContainsKey(type.BaseType)) + subTypes.Add(type.BaseType, new List<Type>()); + + subTypes[type.BaseType].Add(type); + + if (seen.Contains(type.BaseType)) + continue; + + toDo.Enqueue(type.BaseType); + seen.Add(type.BaseType); + } + + return subTypes; + } + + static Dictionary<Type, SubClass> classes = types + .ToDictionary(x => x.Key, x => Create(x.Key, x.Value)); + + static SubClass Create(Type type, IEnumerable<Type> subTypes) + { + return new SubClass() + { + Type = type, + SubTypes = subTypes, + Prototype = CreatePrototype(type), + }; + } + + public static SubClass Get(Type type) + { + if (!typeof(TBase).IsAssignableFrom(type)) + return null; + + lock (classCriticalSection) { + + SubClass subClass = null; + if (!classes.TryGetValue(type, out subClass)) { + + var newTypes = GetTypeHierarchy(type) + .Where(x => !classes.ContainsKey(x.Key)); + + foreach (var newType in newTypes) { + var newClass = Create(newType.Key, newType.Value); + classes.Add(newType.Key, newClass); + + if (type == newType.Key) + subClass = newClass; + } + } + + return subClass; + } + } + + public static SubClass baseClass = Get(typeof(TBase)); + } + + #endregion //////////////////// SubClass ////////////////////////////////////////////////// + + } +} diff --git a/QtVsTools.Package/Common/PunisherQueue.cs b/QtVsTools.Package/Common/PunisherQueue.cs new file mode 100644 index 0000000..08abc85 --- /dev/null +++ b/QtVsTools.Package/Common/PunisherQueue.cs @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; + +namespace QtVsTools +{ + public class PunisherQueue<T> : BasePriorityQueue<T, long> + { + public PunisherQueue() : base() + { } + + public PunisherQueue(Func<T, object> getItemKey) : base(getItemKey) + { } + + /// <summary> + /// Enqueue/re-queue moves item to back of the queue, effectively "punishing" items that + /// were already in the queue. + /// </summary> + /// + public void Enqueue(T item) + { + lock (CriticalSection) { + Enqueue(item, Timestamp.Next()); + } + } + } +} diff --git a/QtVsTools.Package/Common/Timestamp.cs b/QtVsTools.Package/Common/Timestamp.cs new file mode 100644 index 0000000..deaf18a --- /dev/null +++ b/QtVsTools.Package/Common/Timestamp.cs @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Diagnostics; + +namespace QtVsTools +{ + public class Timestamp : Concurrent<Timestamp> + { + long LastTimestamp { get; set; } + long GetStrictMonotonicTimestamp() + { + lock (CriticalSection) { + long t = Stopwatch.GetTimestamp(); + if (t <= LastTimestamp) + t = LastTimestamp + 1; + return (LastTimestamp = t); + } + } + + static Timestamp _Instance; + static Timestamp Instance => + StaticThreadSafeInit(() => _Instance, () => _Instance = new Timestamp()); + + public static long Next() + { + return Instance.GetStrictMonotonicTimestamp(); + } + } +} diff --git a/QtVsTools.Package/Common/VsToolsDialogWindow.cs b/QtVsTools.Package/Common/VsToolsDialogWindow.cs new file mode 100644 index 0000000..e32bc4b --- /dev/null +++ b/QtVsTools.Package/Common/VsToolsDialogWindow.cs @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools +{ + public class VsToolsDialogWindow : Microsoft.VisualStudio.PlatformUI.DialogWindow + { + // Workaround to avoid referencing Microsoft.VisualStudio.Shell.14.0 in XAML. + } +} diff --git a/QtVsTools.Package/Editors/Editor.QtDesigner.cs b/QtVsTools.Package/Editors/Editor.QtDesigner.cs new file mode 100644 index 0000000..518bd6c --- /dev/null +++ b/QtVsTools.Package/Editors/Editor.QtDesigner.cs @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell.Interop; +using QtVsTools.QtMsBuild; +using QtVsTools.VisualStudio; + +namespace QtVsTools.Editors +{ + [Guid(GuidString)] + public class QtDesigner : Editor + { + public const string GuidString = "96FE523D-6182-49F5-8992-3BEA5F7E6FF6"; + public const string Title = "Qt Designer"; + + Guid? _Guid; + public override Guid Guid => (_Guid ?? (_Guid = new Guid(GuidString))).Value; + + public override string ExecutableName => "designer.exe"; + + public override Func<string, bool> WindowFilter => + caption => caption.StartsWith(Title); + + protected override string GetTitle(Process editorProcess) + { + return Title; + } + + protected override void OnStart(Process process) + { + base.OnStart(process); + var document = VsShell.GetDocument(Context, ItemId); + if (document == null) + return; + var project = document.ProjectItem?.ContainingProject; + if (project == null || !QtProjectTracker.IsTracked(project)) + return; + string filePath = document.FullName; + string[] itemId = new[] { document.ProjectItem?.Name }; + var lastWriteTime = File.GetLastWriteTime(filePath); + Task.Run(() => + { + while (!process.WaitForExit(1000)) { + var latestWriteTime = File.GetLastWriteTime(filePath); + if (lastWriteTime != latestWriteTime) { + lastWriteTime = latestWriteTime; + QtProjectIntellisense.Refresh(project, selectedFiles: itemId); + } + } + if (lastWriteTime != File.GetLastWriteTime(filePath)) { + QtProjectIntellisense.Refresh(project, selectedFiles: itemId); + } + }); + } + + protected override bool Detached => QtVsToolsPackage.Instance.Options.DesignerDetached; + } +} diff --git a/QtVsTools.Package/Editors/Editor.QtLinguist.cs b/QtVsTools.Package/Editors/Editor.QtLinguist.cs new file mode 100644 index 0000000..e381d26 --- /dev/null +++ b/QtVsTools.Package/Editors/Editor.QtLinguist.cs @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace QtVsTools.Editors +{ + [Guid(GuidString)] + public class QtLinguist : Editor + { + public const string GuidString = "4A1333DC-5C94-4F14-A7BF-DC3D96092234"; + public const string Title = "Qt Linguist"; + + Guid? _Guid; + public override Guid Guid => (_Guid ?? (_Guid = new Guid(GuidString))).Value; + + public override string ExecutableName => "linguist.exe"; + + public override Func<string, bool> WindowFilter => + caption => caption.EndsWith(Title); + + protected override string GetTitle(Process editorProcess) + { + return Title; + } + + protected override bool Detached => QtVsToolsPackage.Instance.Options.LinguistDetached; + } +} diff --git a/QtVsTools.Package/Editors/Editor.QtResourceEditor.cs b/QtVsTools.Package/Editors/Editor.QtResourceEditor.cs new file mode 100644 index 0000000..6c159cd --- /dev/null +++ b/QtVsTools.Package/Editors/Editor.QtResourceEditor.cs @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell.Interop; + +namespace QtVsTools.Editors +{ + [Guid(GuidString)] + public class QtResourceEditor : Editor + { + public const string GuidString = "D0FFB6E6-5829-4DD9-835E-2965449AC6BF"; + public const string Title = "Qt Resource Editor"; + + Guid? _Guid; + public override Guid Guid => (_Guid ?? (_Guid = new Guid(GuidString))).Value; + + public override string ExecutableName => "QrcEditor.exe"; + + protected override string GetToolsPath() + { + return QtVsToolsPackage.Instance?.PkgInstallPath; + } + + public override Func<string, bool> WindowFilter => + caption => caption.StartsWith(Title); + + protected override string GetTitle(Process editorProcess) + { + return Title; + } + + protected override bool Detached => QtVsToolsPackage.Instance.Options.ResourceEditorDetached; + } +} diff --git a/QtVsTools.Package/Editors/Editor.cs b/QtVsTools.Package/Editors/Editor.cs new file mode 100644 index 0000000..754f4ba --- /dev/null +++ b/QtVsTools.Package/Editors/Editor.cs @@ -0,0 +1,525 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Threading; +using System.Windows.Forms; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.Core; +using QtVsTools.VisualStudio; + +namespace QtVsTools.Editors +{ + using static Core.HelperFunctions; + + public abstract class Editor : IVsEditorFactory + { + public abstract Guid Guid { get; } + public abstract string ExecutableName { get; } + + public virtual Func<string, bool> WindowFilter => (caption => true); + + protected virtual string GetTitle(Process editorProcess) + { + return editorProcess.StartInfo.FileName; + } + + protected virtual string GetToolsPath() + { + return GetQtToolsPath() ?? GetDefaultQtToolsPath(); + } + + protected IVsHierarchy Context { get; private set; } + protected uint ItemId { get; private set; } + + string GetQtToolsPath() + { + var project = VsShell.GetProject(Context); + if (project == null) + return null; + + var vcProject = project.Object as VCProject; + if (vcProject == null) + return null; + + var vcConfigs = vcProject.Configurations as IVCCollection; + if (vcConfigs == null) + return null; + + var activeConfig = project.ConfigurationManager?.ActiveConfiguration; + if (activeConfig == null) + return null; + + var activeConfigId = string.Format("{0}|{1}", + activeConfig.ConfigurationName, activeConfig.PlatformName); + var vcConfig = vcConfigs.Item(activeConfigId) as VCConfiguration; + if (vcConfig == null) + return null; + + var qtToolsPath = vcConfig.GetEvaluatedPropertyValue("QtToolsPath"); + if (string.IsNullOrEmpty(qtToolsPath)) + return null; + + return qtToolsPath; + } + + string GetDefaultQtToolsPath() + { + var versionMgr = QtVersionManager.The(); + if (versionMgr == null) + return null; + + var defaultVersion = versionMgr.GetDefaultVersion(); + var defaultVersionInfo = versionMgr.GetVersionInfo(defaultVersion); + if (defaultVersionInfo == null || string.IsNullOrEmpty(defaultVersionInfo.qtDir)) + return null; + + return Path.Combine(defaultVersionInfo.qtDir, "bin"); + } + + [EnvironmentPermission(SecurityAction.Demand, Unrestricted = true)] + public virtual int CreateEditorInstance( + uint grfCreateDoc, + string pszMkDocument, + string pszPhysicalView, + IVsHierarchy pvHier, + uint itemid, + IntPtr punkDocDataExisting, + out IntPtr ppunkDocView, + out IntPtr ppunkDocData, + out string pbstrEditorCaption, + out Guid pguidCmdUI, + out int pgrfCDW) + { + // Initialize to null + ppunkDocView = IntPtr.Zero; + ppunkDocData = IntPtr.Zero; + pguidCmdUI = Guid; + pgrfCDW = 0; + pbstrEditorCaption = null; + + // Validate inputs + if ((grfCreateDoc & (VSConstants.CEF_OPENFILE | VSConstants.CEF_SILENT)) == 0) { + return VSConstants.E_INVALIDARG; + } + if (punkDocDataExisting != IntPtr.Zero) { + return VSConstants.VS_E_INCOMPATIBLEDOCDATA; + } + + Context = pvHier; + ItemId = itemid; + + var toolsPath = GetToolsPath(); + if (string.IsNullOrEmpty(toolsPath)) + return VSConstants.VS_E_INCOMPATIBLEDOCDATA; + + // Create the Document (editor) + EditorPane newEditor = new EditorPane(this, toolsPath); + ppunkDocView = Marshal.GetIUnknownForObject(newEditor); + ppunkDocData = Marshal.GetIUnknownForObject(newEditor); + pbstrEditorCaption = ""; + pgrfCDW = (int)(_VSRDTFLAGS.RDT_CantSave | _VSRDTFLAGS.RDT_DontAutoOpen); + + return VSConstants.S_OK; + } + + public virtual int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp) + { + return VSConstants.S_OK; + } + + public virtual int Close() + { + return VSConstants.S_OK; + } + + public virtual int MapLogicalView(ref Guid rguidLogicalView, out string pbstrPhysicalView) + { + pbstrPhysicalView = null; // initialize out parameter + + // we support only a single physical view + if (VSConstants.LOGVIEWID_Primary == rguidLogicalView) { + // primary view uses NULL as pbstrPhysicalView + return VSConstants.S_OK; + } else { + // you must return E_NOTIMPL for any unrecognized rguidLogicalView values + return VSConstants.E_NOTIMPL; + } + } + + protected virtual ProcessStartInfo GetStartInfo( + string filePath, + string qtToolsPath, + bool hideWindow) + { + return new ProcessStartInfo + { + FileName = Path.GetFullPath(Path.Combine(qtToolsPath, ExecutableName)), + Arguments = SafePath(filePath), + WindowStyle = hideWindow ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal + }; + } + + public virtual Process Start( + string filePath = "", + string qtToolsPath = null, + bool hideWindow = true) + { + if (string.IsNullOrEmpty(qtToolsPath)) + qtToolsPath = GetDefaultQtToolsPath(); + try { + return Process.Start(GetStartInfo(filePath, qtToolsPath, hideWindow)); + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return null; + } + } + + protected virtual void OnStart(Process process) + { + } + + protected virtual void OnExit(Process process) + { + } + + protected virtual bool Detached => false; + + private class EditorPane : WindowPane, IVsPersistDocData + { + public Editor Editor { get; private set; } + public string QtToolsPath { get; private set; } + + public TableLayoutPanel EditorContainer { get; private set; } + public Label EditorTitle { get; private set; } + public LinkLabel EditorDetachButton { get; private set; } + public Panel EditorControl { get; private set; } + public override IWin32Window Window => EditorContainer; + + public Process EditorProcess { get; private set; } + public IntPtr EditorWindow { get; private set; } + public int EditorWindowStyle { get; private set; } + public int EditorWindowStyleExt { get; private set; } + public IntPtr EditorIcon { get; private set; } + + public EditorPane(Editor editor, string qtToolsPath) + { + Editor = editor; + QtToolsPath = qtToolsPath; + + var titleBar = new FlowLayoutPanel + { + AutoSize = true, + Dock = DockStyle.Fill, + BackColor = Color.FromArgb(201, 221, 201), + }; + titleBar.Controls.Add(EditorTitle = new Label + { + Text = Editor.ExecutableName, + ForeColor = Color.FromArgb(9, 16, 43), + Font = new Font("Segoe UI", 8F, FontStyle.Bold, GraphicsUnit.Point), + AutoSize = true, + Margin = new Padding(8), + }); + titleBar.Controls.Add(EditorDetachButton = new LinkLabel + { + Text = "Detach", + Font = new Font("Segoe UI", 8F, FontStyle.Regular, GraphicsUnit.Point), + AutoSize = true, + Margin = new Padding(0, 8, 8, 8), + }); + EditorDetachButton.Click += EditorDetachButton_Click; + + EditorControl = new Panel + { + BackColor = SystemColors.Window, + Dock = DockStyle.Fill + }; + EditorControl.Resize += EditorControl_Resize; + + EditorContainer = new TableLayoutPanel + { + ColumnCount = 1, + RowCount = 2, + }; + EditorContainer.ColumnStyles.Add(new ColumnStyle()); + EditorContainer.RowStyles.Add(new RowStyle()); + EditorContainer.RowStyles.Add(new RowStyle()); + EditorContainer.Controls.Add(titleBar, 0, 0); + EditorContainer.Controls.Add(EditorControl, 0, 1); + } + + protected override void Dispose(bool disposing) + { + try { + if (disposing) { + EditorContainer?.Dispose(); + EditorContainer = null; + GC.SuppressFinalize(this); + } + } finally { + base.Dispose(disposing); + } + } + + int IVsPersistDocData.GetGuidEditorType(out Guid pClassID) + { + pClassID = Editor.Guid; + return VSConstants.S_OK; + } + + int IVsPersistDocData.LoadDocData(string pszMkDocument) + { + var solution = GetService(typeof(SVsSolution)) as IVsSolution; + EditorProcess = Editor.Start(pszMkDocument, QtToolsPath, + hideWindow: !Editor.Detached); + if (EditorProcess == null) + return VSConstants.E_FAIL; + if (Editor.Detached) { + Editor.OnStart(EditorProcess); + CloseParentFrame(); + return VSConstants.S_OK; + } + + EditorTitle.Text = Editor.GetTitle(EditorProcess); + + EditorProcess.WaitForInputIdle(); + EditorProcess.EnableRaisingEvents = true; + EditorProcess.Exited += EditorProcess_Exited; + + var t = Stopwatch.StartNew(); + while (EditorWindow == IntPtr.Zero && t.ElapsedMilliseconds < 5000) { + var windows = new Dictionary<IntPtr, string>(); + foreach (ProcessThread thread in EditorProcess.Threads) { + NativeAPI.EnumThreadWindows( + dwThreadId: (uint)thread.Id, + lParam: IntPtr.Zero, + lpfn: (hWnd, lParam) => + { + windows.Add(hWnd, NativeAPI.GetWindowCaption(hWnd)); + return true; + }); + } + + EditorWindow = windows + .Where(w => Editor.WindowFilter(w.Value)) + .Select(w => w.Key) + .FirstOrDefault(); + + if (EditorWindow == IntPtr.Zero) + Thread.Sleep(100); + } + + if (EditorWindow == IntPtr.Zero) { + EditorProcess.Kill(); + EditorProcess = null; + return VSConstants.E_FAIL; + } + + // Save editor window styles and icon + EditorWindowStyle = NativeAPI.GetWindowLong( + EditorWindow, NativeAPI.GWL_STYLE); + EditorWindowStyleExt = NativeAPI.GetWindowLong( + EditorWindow, NativeAPI.GWL_EXSTYLE); + EditorIcon = NativeAPI.SendMessage( + EditorWindow, NativeAPI.WM_GETICON, NativeAPI.ICON_SMALL, 0); + if (EditorIcon == IntPtr.Zero) + EditorIcon = (IntPtr)NativeAPI.GetClassLong(EditorWindow, NativeAPI.GCL_HICON); + if (EditorIcon == IntPtr.Zero) + EditorIcon = (IntPtr)NativeAPI.GetClassLong(EditorWindow, NativeAPI.GCL_HICONSM); + + // Move editor window inside VS + if (NativeAPI.SetParent( + EditorWindow, EditorControl.Handle) == IntPtr.Zero) { + EditorWindow = IntPtr.Zero; + EditorProcess.Kill(); + EditorProcess = null; + return VSConstants.E_FAIL; + } + if (NativeAPI.SetWindowLong( + EditorWindow, NativeAPI.GWL_STYLE, NativeAPI.WS_VISIBLE) == 0) { + EditorWindow = IntPtr.Zero; + EditorProcess.Kill(); + EditorProcess = null; + return VSConstants.E_FAIL; + } + if (!NativeAPI.MoveWindow( + EditorWindow, 0, 0, EditorControl.Width, EditorControl.Height, true)) { + EditorWindow = IntPtr.Zero; + EditorProcess.Kill(); + EditorProcess = null; + return VSConstants.E_FAIL; + } + + Editor.OnStart(EditorProcess); + return VSConstants.S_OK; + } + + void CloseParentFrame() + { + EditorProcess = null; + EditorWindow = IntPtr.Zero; + var parentFrame = GetService(typeof(SVsWindowFrame)) as IVsWindowFrame; + parentFrame?.CloseFrame((uint)__FRAMECLOSE.FRAMECLOSE_NoSave); + } + + private void EditorProcess_Exited(object sender, EventArgs e) + { + CloseParentFrame(); + Editor.OnExit(EditorProcess); + } + + void EditorControl_Resize(object sender, EventArgs e) + { + if (EditorControl != null && EditorWindow != IntPtr.Zero) { + NativeAPI.MoveWindow( + EditorWindow, 0, 0, EditorControl.Width, EditorControl.Height, true); + } + } + + private void EditorDetachButton_Click(object sender, EventArgs e) + { + if (EditorProcess != null) { + var editorWindow = EditorWindow; + DetachEditorWindow(); + CloseParentFrame(); + NativeAPI.ShowWindow(editorWindow, NativeAPI.SW_RESTORE); + NativeAPI.SetForegroundWindow(editorWindow); + } + } + + public void DetachEditorWindow() + { + NativeAPI.ShowWindow(EditorWindow, + NativeAPI.SW_HIDE); + NativeAPI.SetParent( + EditorWindow, IntPtr.Zero); + NativeAPI.SetWindowLong( + EditorWindow, NativeAPI.GWL_STYLE, EditorWindowStyle); + NativeAPI.SetWindowLong( + EditorWindow, NativeAPI.GWL_EXSTYLE, EditorWindowStyleExt); + NativeAPI.SendMessage( + EditorWindow, NativeAPI.WM_SETICON, NativeAPI.ICON_SMALL, EditorIcon); + NativeAPI.MoveWindow( + EditorWindow, 0, 0, EditorControl.Width, EditorControl.Height, true); + NativeAPI.ShowWindow(EditorWindow, + NativeAPI.SW_SHOWMINNOACTIVE); + } + + int IVsPersistDocData.Close() + { + if (EditorProcess != null) { + DetachEditorWindow(); + var editorProcess = EditorProcess; + EditorProcess = null; + var editorWindow = EditorWindow; + EditorWindow = IntPtr.Zero; + + // Close editor window + System.Threading.Tasks.Task.Run(() => + { + NativeAPI.SendMessage(editorWindow, NativeAPI.WM_CLOSE, 0, 0); + if (!editorProcess.WaitForExit(500)) { + NativeAPI.ShowWindow(editorWindow, + NativeAPI.SW_RESTORE); + NativeAPI.SetForegroundWindow(editorWindow); + } + }); + } + + if (EditorContainer == null) { + if (EditorContainer != null) { + EditorContainer.Dispose(); + EditorContainer = null; + } + } + return VSConstants.S_OK; + } + + int IVsPersistDocData.RenameDocData( + uint grfAttribs, + IVsHierarchy pHierNew, + uint itemidNew, + string pszMkDocumentNew) + { + return VSConstants.E_NOTIMPL; + } + + int IVsPersistDocData.IsDocDataDirty(out int pfDirty) + { + pfDirty = 0; + return VSConstants.S_OK; + } + + int IVsPersistDocData.SetUntitledDocPath(string pszDocDataPath) + { + return VSConstants.S_OK; + } + + int IVsPersistDocData.SaveDocData( + VSSAVEFLAGS dwSave, + out string pbstrMkDocumentNew, + out int pfSaveCanceled) + { + pbstrMkDocumentNew = string.Empty; + pfSaveCanceled = 0; + return VSConstants.E_NOTIMPL; + } + + int IVsPersistDocData.OnRegisterDocData( + uint docCookie, + IVsHierarchy pHierNew, + uint itemidNew) + { + return VSConstants.S_OK; + } + + int IVsPersistDocData.IsDocDataReloadable(out int pfReloadable) + { + pfReloadable = 0; + return VSConstants.S_OK; + } + + int IVsPersistDocData.ReloadDocData(uint grfFlags) + { + return VSConstants.E_NOTIMPL; + } + } + } +} diff --git a/QtVsTools.Package/Language/LICENSE.APACHE b/QtVsTools.Package/Language/LICENSE.APACHE new file mode 100644 index 0000000..c64a815 --- /dev/null +++ b/QtVsTools.Package/Language/LICENSE.APACHE @@ -0,0 +1,13 @@ +Copyright 2016 Mads Kristensen + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/QtVsTools.Package/Language/qml.qmlproject.tmLanguage b/QtVsTools.Package/Language/qml.qmlproject.tmLanguage new file mode 100644 index 0000000..5773bd8 --- /dev/null +++ b/QtVsTools.Package/Language/qml.qmlproject.tmLanguage @@ -0,0 +1,317 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>fileTypes</key> + <array> + <string>qml</string> + <string>qmlproject</string> + </array> + <key>name</key> + <string>QML</string> + <key>patterns</key> + <array> + <dict> + <key>begin</key> + <string>/\*(?!/)</string> + <key>comment</key> + <string>Block comment.</string> + <key>end</key> + <string>\*/</string> + <key>name</key> + <string>comment.block.documentation.qml</string> + </dict> + <dict> + <key>comment</key> + <string>Line comment.</string> + <key>match</key> + <string>//.*$</string> + <key>name</key> + <string>comment.line.double-slash.qml</string> + </dict> + <dict> + <key>begin</key> + <string>\b(import)\s+</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.other.import.qml</string> + </dict> + </dict> + <key>comment</key> + <string>import statement.</string> + <key>end</key> + <string>$</string> + <key>name</key> + <string>meta.import.qml</string> + <key>patterns</key> + <array> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>entity.name.class.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>constant.numeric.qml</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>keyword.other.import.qml</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>entity.name.class.qml</string> + </dict> + </dict> + <key>comment</key> + <string>import Namespace VersionMajor.VersionMinor [as SingletonTypeIdentifier]</string> + <key>match</key> + <string>([\w\d\.]+)\s+(\d+\.\d+)(?:\s+(as)\s+([A-Z][\w\d]*))?</string> + <key>name</key> + <string>meta.import.namespace.qml</string> + </dict> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>string.quoted.double.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>keyword.other.import.qml</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>entity.name.class.qml</string> + </dict> + </dict> + <key>comment</key> + <string>import <string> [as Script]</string> + <key>match</key> + <string>(\"[^\"]+\")(?:\s+(as)\s+([A-Z][\w\d]*))?</string> + <key>name</key> + <string>meta.import.dirjs.qml</string> + </dict> + </array> + </dict> + <dict> + <key>comment</key> + <string>Capitalized word (class or enum).</string> + <key>match</key> + <string>\b[A-Z]\w*\b</string> + <key>name</key> + <string>support.class.qml</string> + </dict> + <dict> + <key>comment</key> + <string>onSomething - handler.</string> + <key>match</key> + <string>(((^|\{)\s*)|\b)on[A-Z]\w*\b</string> + <key>name</key> + <string>support.class.qml</string> + </dict> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.other.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>storage.modifier.qml</string> + </dict> + </dict> + <key>comment</key> + <string>id: <something></string> + <key>match</key> + <string>(?:^|\{)\s*(id)\s*\:\s*([^;\s]+)\b</string> + <key>name</key> + <string>meta.id.qml</string> + </dict> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.other.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>keyword.other.qml</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>keyword.other.qml</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>storage.type.qml</string> + </dict> + <key>5</key> + <dict> + <key>name</key> + <string>entity.other.attribute-name.qml</string> + </dict> + </dict> + <key>comment</key> + <string>property definition.</string> + <key>match</key> + <string>^\s*(?:(default|readonly)\s+)?(property)\s+(?:(alias)|([\w\<\>]+))\s+(\w+)</string> + <key>name</key> + <string>meta.propertydef.qml</string> + </dict> + <dict> + <key>begin</key> + <string>\b(signal)\s+(\w+)\s*</string> + <key>beginCaptures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>keyword.other.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>support.function.qml</string> + </dict> + </dict> + <key>comment</key> + <string>signal <signalName>[([<type> <parameter>[, ...]])]</string> + <key>end</key> + <string>;|(?=/)|$</string> + <key>name</key> + <string>meta.signal.qml</string> + <key>patterns</key> + <array> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>storage.type.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>variable.parameter.qml</string> + </dict> + </dict> + <key>match</key> + <string>(\w+)\s+(\w+)</string> + <key>name</key> + <string>meta.signal.parameters.qml</string> + </dict> + </array> + </dict> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>constant.language.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>storage.type.qml</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>keyword.control.qml</string> + </dict> + </dict> + <key>comment</key> + <string>js keywords.</string> + <key>match</key> + <string>(?:\b|\s+)(?:(true|false|null|undefined)|(var|void)|(on|as|enum|connect|break|case|catch|continue|debugger|default|delete|do|else|finally|for|if|in|instanceof|new|return|switch|this|throw|try|typeof|while|with))\b</string> + <key>name</key> + <string>meta.keyword.qml</string> + </dict> + <dict> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>storage.type.qml</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.function.untitled</string> + </dict> + </dict> + <key>comment</key> + <string>function definition.</string> + <key>match</key> + <string>\b(function)\s+([\w_]+)\s*(?=\()</string> + <key>name</key> + <string>meta.function.qml</string> + </dict> + <dict> + <key>comment</key> + <string>function call.</string> + <key>match</key> + <string>\b[\w_]+\s*(?=\()</string> + <key>name</key> + <string>support.function.qml</string> + </dict> + <dict> + <key>comment</key> + <string>property (property: <something>).</string> + <key>match</key> + <string>(?:^|\{|;)\s*[a-z][\w\.]*\s*(?=\:)</string> + <key>name</key> + <string>entity.other.attribute-name.qml</string> + </dict> + <dict> + <key>comment</key> + <string>property of the variable (name.property).</string> + <key>match</key> + <string>(?<=\.)\b\w*</string> + <key>name</key> + <string>entity.other.attribute-name.qml</string> + </dict> + <dict> + <key>comment</key> + <string>All non colored words are assumed to be variables.</string> + <key>match</key> + <string>\b([a-z_]\w*)\b</string> + <key>name</key> + <string>variable.parameter</string> + </dict> + <dict> + <key>include</key> + <string>source.js</string> + </dict> + </array> + <key>scopeName</key> + <string>source.qml</string> + <key>uuid</key> + <string>13a281e0-0507-45b4-bb6c-a57177630f10</string> + </dict> +</plist> diff --git a/QtVsTools.Package/Marketplace/Overview.html_TT b/QtVsTools.Package/Marketplace/Overview.html_TT new file mode 100644 index 0000000..7f83418 --- /dev/null +++ b/QtVsTools.Package/Marketplace/Overview.html_TT @@ -0,0 +1,162 @@ +<!DOCTYPE html> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2017 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="html" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> +<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> +<head> + <meta charset="utf-8" /> + <title></title> +</head> +<body> +<!-- BEGIN Visual Studio Marketplace Extension Page: Overview --> +<!-- CUT HERE vvv CUT HERE vvv CUT HERE vvv CUT HERE vvv CUT HERE vvv CUT HERE vvv CUT HERE vvv --> +<p><h1>Qt VS Tools for Visual Studio <#=VS_NAME #></h1></p> + +<p> + Qt Visual Studio Tools integrate the Qt development tools into Microsoft Visual Studio + <#=VS_NAME #>. This enables developers to use the standard Windows development environment + without having to worry about Qt-related build steps or tools. +</p> +<p> + See also: + <ul> + <#if (VS_NAME !="2022" ) {#> + <li> + <a title="Qt VS Tools for Visual Studio 2022" +href="https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022"> + Qt VS Tools for Visual Studio 2019 + </a> + </li> + <#}#> + <#if (VS_NAME !="2019" ) {#> + <li> + <a title="Qt VS Tools for Visual Studio 2019" +href="https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019"> + Qt VS Tools for Visual Studio 2019 + </a> + </li> + <#}#> + <#if (VS_NAME !="2017" ) {#> + <li> + <a title="Qt VS Tools for Visual Studio 2017" +href="https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools-19123"> + Qt VS Tools for Visual Studio 2017 + </a> + </li> + <#}#> + </ul> +</p> +<p><h3>Documentation</h3></p> +<ul> + <li> + <a title="Qt VS Tools Manual" + href="http://doc.qt.io/qtvstools/qtvstools-getting-started.html"> + Qt VS Tools Manual + </a> + </li> + <li> + <a title="Changelog" + href="https://code.qt.io/cgit/qt-labs/vstools.git/tree/Changelog?h=v<#=QT_VS_TOOLS_VERSION#>"> + Changelog + </a> + </li> +</ul> +<p><h3><span>The main features of Qt VS Tools are:</span></h3></p> +<ul> + <li> + Wizards for creating new Qt projects and classes. + </li> + <li> + Automated build setup for the <a href="http://doc.qt.io/qt-5/moc.html"> + Meta-Object Compiler (moc) + </a>, <a href="http://doc.qt.io/qt-5/uic.html"> + User Interface Compiler (uic) + </a>, and + <a href="http://doc.qt.io/qt-5/rcc.html">Resource Compiler (rcc)</a>. + </li> + <li> + Import and export of Qt project files (<code>.pro</code>) and project include files + (<code>.pri</code>). + </li> + <li> + Automated conversion of a Qt VS Tools project to a + <a href="http://doc.qt.io/qt-5/qmake-manual.html">qmake</a> project, or the other way + around. + </li> + <li> + Integrated Qt resource management. + </li> + <li> + Integrated Qt documentation. + </li> + <li> + Debugging extensions for Qt data types. + </li> +</ul> +<p><h3>How to set up F1 help</h3></p> +<ol class="1" type="1"> + <li> + Select <strong>Tools</strong> > <strong>Options</strong> > + <strong>Environment</strong> > <strong>Keyboard</strong> + </li> + <li> + In the <strong>Show commands containing</strong> field, enter <code>Help.F1QtHelp</code> + </li> + <li> + Select the <strong>Press shortcut keys</strong> field and press your preferred keys to + add them to the field. The default shortcut is set to <strong>Alt+F1</strong> + </li> + <li> + Select <strong>Assign</strong>, and then select <strong>OK</strong>. + </li> +</ol> +<p><h3>How to report bugs and contribute code?</h3></p> +<ul> + <li> + <a title="Bug reports" href="https://bugreports.qt.io/browse/QTVSADDINBUG">Bug reports</a> + </li> + <li> + <a title="Source code" href="https://code.qt.io/cgit/qt-labs/vstools.git">Source code</a> + </li> +</ul> +<p> + You are welcome to join the discussions in + <a href="http://lists.qt-project.org/mailman/listinfo"> the Qt Project mailing lists</a>, + <a href="https://forum.qt.io/"> development forums</a> and to + <a href="https://wiki.qt.io/Qt_Contribution_Guidelines"> contribute to Qt</a>. +</p> +<!-- CUT HERE ^^^ CUT HERE ^^^ CUT HERE ^^^ CUT HERE ^^^ CUT HERE ^^^ CUT HERE ^^^ CUT HERE ^^^ --> +<!-- END Visual Studio Marketplace Extension Page: Overview --> + +</body> +</html> diff --git a/QtVsTools.Package/Options/QtLegacyOptionsPage.cs b/QtVsTools.Package/Options/QtLegacyOptionsPage.cs new file mode 100644 index 0000000..5ee9532 --- /dev/null +++ b/QtVsTools.Package/Options/QtLegacyOptionsPage.cs @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.ComponentModel; +using Microsoft.Win32; +using Microsoft.VisualStudio.Shell; +using QtVsTools.Core; + +namespace QtVsTools.Options +{ + public class QtLegacyOptionsPage : DialogPage + { + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Source control: Ask before checking out files")] + public bool CheckoutPrompt { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Source control: Enable file check-out")] + public bool Checkout { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Linguist: Default lrelease options")] + public string DefaultLReleaseOptions { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Linguist: Default lupdate options")] + public string DefaultLUpdateOptions { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Linguist: Run lupdate during build")] + public bool EnableLUpdateOnBuild { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Meta-Object Compiler: Default moc generated files directory")] + public string DefaultMocDir { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Meta-Object Compiler: Default additional moc options ")] + public string AdditionalMocOptions { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Meta-Object Compiler: Enable automatic moc")] + public bool AutoMoc { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Resource Compiler: Default rcc generated files directory")] + public string DefaultRccDir { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("User Interface Compiler: Default uic generated files directory")] + public string DefaultUicDir { get; set; } + + [Category(@"Qt VS Project Format v2 (Qt tools integrated via custom-build steps)")] + [DisplayName("Build: Run pre-build setup")] + public bool PreBuildSetup { get; set; } + + const string VALUENAME_LegacyPreBuild = "LegacyPreBuild"; + + public override void ResetSettings() + { + CheckoutPrompt = true; + Checkout = true; + DefaultLReleaseOptions = ""; + DefaultLUpdateOptions = ""; + EnableLUpdateOnBuild = false; + DefaultMocDir = ""; + AdditionalMocOptions = ""; + AutoMoc = true; + DefaultRccDir = ""; + DefaultUicDir = ""; + PreBuildSetup = false; + } + + public override void LoadSettingsFromStorage() + { + ResetSettings(); + try { + using (var key = Registry.CurrentUser + .OpenSubKey(@"SOFTWARE\" + Resources.registryPackagePath, writable: false)) { + if (key == null) + return; + if (key.GetValue(Resources.askBeforeCheckoutFileKeyword) is int checkoutPrompt) + CheckoutPrompt = (checkoutPrompt != 0); + if (key.GetValue(Resources.disableCheckoutFilesKeyword) is int disableCheckout) + Checkout = (disableCheckout == 0); + if (key.GetValue(Resources.lreleaseOptionsKeyword) is string lreleaseOptions) + DefaultLReleaseOptions = lreleaseOptions; + if (key.GetValue(Resources.lupdateOptionsKeyword) is string lupdateOptions) + DefaultLUpdateOptions = lupdateOptions; + if (key.GetValue(Resources.lupdateKeyword) is int lupdateOnBuild) + EnableLUpdateOnBuild = (lupdateOnBuild != 0); + if (key.GetValue(Resources.mocDirKeyword) is string mocDir) + DefaultMocDir = mocDir; + if (key.GetValue(Resources.mocOptionsKeyword) is string mocOptions) + AdditionalMocOptions = mocOptions; + if (key.GetValue(Resources.disableAutoMocStepsUpdateKeyword) is int autoMocOff) + AutoMoc = (autoMocOff == 0); + if (key.GetValue(Resources.rccDirKeyword) is string rccDir) + DefaultRccDir = rccDir; + if (key.GetValue(Resources.uicDirKeyword) is string uicDir) + DefaultUicDir = uicDir; + if (key.GetValue(VALUENAME_LegacyPreBuild) is int preBuild) + PreBuildSetup = (preBuild != 0); + } + } catch (Exception exception) { + Messages.Print( + exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace); + } + } + + public override void SaveSettingsToStorage() + { + try { + using (var key = Registry.CurrentUser + .CreateSubKey(@"SOFTWARE\" + Resources.registryPackagePath)) { + if (key == null) + return; + key.SetValue(Resources.askBeforeCheckoutFileKeyword, CheckoutPrompt ? 1 : 0); + key.SetValue(Resources.disableCheckoutFilesKeyword, Checkout ? 0 : 1); + key.SetValue(Resources.lreleaseOptionsKeyword, DefaultLReleaseOptions); + key.SetValue(Resources.lupdateOptionsKeyword, DefaultLUpdateOptions); + key.SetValue(Resources.lupdateKeyword, EnableLUpdateOnBuild ? 1 : 0); + key.SetValue(Resources.mocDirKeyword, DefaultMocDir); + key.SetValue(Resources.mocOptionsKeyword, AdditionalMocOptions); + key.SetValue(Resources.disableAutoMocStepsUpdateKeyword, AutoMoc ? 0 : 1); + key.SetValue(Resources.rccDirKeyword, DefaultRccDir); + key.SetValue(Resources.uicDirKeyword, DefaultUicDir); + key.SetValue(VALUENAME_LegacyPreBuild, PreBuildSetup ? 1 : 0); + } + } catch (Exception exception) { + Messages.Print( + exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace); + } + } + } +} diff --git a/QtVsTools.Package/Options/QtOptionsPage.cs b/QtVsTools.Package/Options/QtOptionsPage.cs new file mode 100644 index 0000000..5035285 --- /dev/null +++ b/QtVsTools.Package/Options/QtOptionsPage.cs @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using Microsoft.Win32; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Build.Framework; +using EnvDTE; +using QtVsTools.Core; +using QtVsTools.Common; +using QtVsTools.VisualStudio; +using System.Reflection; +using System.Linq.Expressions; + +namespace QtVsTools.Options +{ + using static EnumExt; + + public class QtOptionsPage : DialogPage, IQtVsToolsOptions + { + public enum QtMsBuild + { + [String("QtMsBuild_Path")] Path + } + + public enum QmlDebug + { + [String("QMLDebug_Enable")] Enable, + [String("QMLDebug_Timeout")] Timeout + } + + public enum Help + { + [String("Help_Preference")] Preference, + [String("Help_TryOnF1Pressed")] TryOnF1Pressed + } + + public enum Designer + { + [String("Designer_Detached")] Detached, + } + + public enum Linguist + { + [String("Linguist_Detached")] Detached, + } + + public enum ResEditor + { + [String("ResourceEditor_Detached")] Detached, + } + + public enum BkgBuild + { + [String("BkgBuild_ProjectTracking")] ProjectTracking, + [String("BkgBuild_RunQtTools")] RunQtTools, + [String("BkgBuild_DebugInfo")] DebugInfo, + [String("BkgBuild_LoggerVerbosity")] LoggerVerbosity, + } + + public enum Timeout : uint { Disabled = 0 } + + class TimeoutConverter : EnumConverter + { + public TimeoutConverter(Type t) : base(t) + { } + + public override bool GetStandardValuesSupported(ITypeDescriptorContext c) + => true; + + public override bool GetStandardValuesExclusive(ITypeDescriptorContext c) + => false; + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext c) + => new StandardValuesCollection(new[] { Timeout.Disabled }); + + public override object ConvertFrom( + ITypeDescriptorContext context, + CultureInfo culture, + object value) + { + uint n = 0; + try { + n = Convert.ToUInt32(value); + } catch { } + return (Timeout)n; + } + + public override object ConvertTo( + ITypeDescriptorContext context, + CultureInfo culture, + object value, + Type destinationType) + { + if (destinationType == typeof(string)) + return value.ToString(); + return base.ConvertTo(context, culture, value, destinationType); + } + } + + class EnableDisableConverter : BooleanConverter + { + public override object ConvertFrom( + ITypeDescriptorContext context, + CultureInfo culture, + object value) + { + return string + .Equals(value as string, "Enable", StringComparison.InvariantCultureIgnoreCase); + } + + public override object ConvertTo( + ITypeDescriptorContext context, + CultureInfo culture, + object value, + Type destinationType) + { + if (value.GetType() == typeof(bool) && destinationType == typeof(string)) + return ((bool)value) ? "Enable" : "Disable"; + return base.ConvertTo(context, culture, value, destinationType); + } + } + + [Category("Qt/MSBuild")] + [DisplayName("Path to Qt/MSBuild files")] + [Description("Corresponds to the QTMSBUILD environment variable")] + public string QtMsBuildPath { get; set; } + + [Category("QML Debugging")] + [DisplayName("Process debug events")] + [Description("Set to false to turn off processing of all debug events by the QML debug engine, effectively excluding it from the debugging environment. Disabling the QML debug engine will skip debugging of QML code for all projects.")] + public bool QmlDebuggerEnabled { get; set; } + + [Category("QML Debugging")] + [DisplayName("Runtime connection timeout (msecs)")] + [TypeConverter(typeof(TimeoutConverter))] + public Timeout QmlDebuggerTimeout { get; set; } + int IQtVsToolsOptions.QmlDebuggerTimeout => (int)QmlDebuggerTimeout; + + [Category("Help")] + [DisplayName("Keyboard shortcut")] + [Description("To change keyboard mapping, go to: Tools > Options > Keyboard")] + [ReadOnly(true)] + public string QtHelpKeyBinding { get; set; } + + [Category("Help")] + [DisplayName("Preferred source")] + public QtHelp.SourcePreference HelpPreference { get; set; } + bool IQtVsToolsOptions.HelpPreferenceOnline + => (HelpPreference == QtHelp.SourcePreference.Online); + + [Category("Help")] + [DisplayName("Try Qt documentation when F1 is pressed")] + public bool TryQtHelpOnF1Pressed { get; set; } + + [Category("Qt Designer")] + [DisplayName("Run in detached window")] + public bool DesignerDetached { get; set; } + + [Category("Qt Linguist")] + [DisplayName("Run in detached window")] + public bool LinguistDetached { get; set; } + + [Category("Qt Resource Editor")] + [DisplayName("Run in detached window")] + public bool ResourceEditorDetached { get; set; } + + [Category("IntelliSense")] + [DisplayName("Auto project tracking")] + [Description( + "Enable this option to automatically keep track of project changes and trigger a" + + " background build of Qt targets if required to keep IntelliSense updated.")] + [TypeConverter(typeof(EnableDisableConverter))] + public bool ProjectTracking { get; set; } + + [Category("IntelliSense")] + [DisplayName("Run Qt tools in background build")] + [Description( + "Enable this option to allow all Qt tools (e.g. moc, uic) to be invoked during a" + + " background update of IntelliSense information. If disabled, only qmake will be" + + " invoked during background builds, to update a minimal set of Qt build properties.")] + [TypeConverter(typeof(EnableDisableConverter))] + public bool BuildRunQtTools { get; set; } + + [Category("IntelliSense")] + [DisplayName("Show debug information")] + [Description("Enable this option to display debug information about IntelliSense updates.")] + [TypeConverter(typeof(EnableDisableConverter))] + public bool BuildDebugInformation { get; set; } + + [Category("IntelliSense")] + [DisplayName("Verbosity of background build log")] + [Description("Configure verbosity level of background build log.")] + public LoggerVerbosity BuildLoggerVerbosity { get; set; } + + public override void ResetSettings() + { + QtMsBuildPath = ""; + QmlDebuggerEnabled = true; + QmlDebuggerTimeout = (Timeout)60000; + HelpPreference = QtHelp.SourcePreference.Online; + TryQtHelpOnF1Pressed = true; + DesignerDetached = LinguistDetached = ResourceEditorDetached = false; + + BuildRunQtTools = ProjectTracking = true; + BuildDebugInformation = false; + BuildLoggerVerbosity = LoggerVerbosity.Quiet; + + //////// + // Get Qt Help keyboard shortcut + // + var dte = VsServiceProvider.GetService<SDTE, DTE>(); + var f1QtHelpBindings = dte.Commands.Item("QtVSTools.F1QtHelp")?.Bindings as Array; + var binding = f1QtHelpBindings.Cast<string>() + .Select(x => x.Split(new[] { "::" }, StringSplitOptions.None)) + .Select(x => new { Scope = x.FirstOrDefault(), Shortcut = x.LastOrDefault() }) + .FirstOrDefault(); + if (binding != null) + QtHelpKeyBinding = string.Format("[{0}] {1}", binding.Scope, binding.Shortcut); + else + QtHelpKeyBinding = ""; + } + + public override void LoadSettingsFromStorage() + { + ResetSettings(); + try { + QtMsBuildPath = Environment.GetEnvironmentVariable("QTMSBUILD"); + + using (var key = Registry.CurrentUser + .OpenSubKey(@"SOFTWARE\" + Resources.registryPackagePath, writable: false)) { + if (key == null) + return; + Load(() => QmlDebuggerEnabled, key, QmlDebug.Enable); + Load(() => QmlDebuggerTimeout, key, QmlDebug.Timeout); + Load(() => HelpPreference, key, Help.Preference); + Load(() => TryQtHelpOnF1Pressed, key, Help.TryOnF1Pressed); + Load(() => DesignerDetached, key, Designer.Detached); + Load(() => LinguistDetached, key, Linguist.Detached); + Load(() => ResourceEditorDetached, key, ResEditor.Detached); + Load(() => ProjectTracking, key, BkgBuild.ProjectTracking); + Load(() => BuildRunQtTools, key, BkgBuild.RunQtTools); + Load(() => BuildDebugInformation, key, BkgBuild.DebugInfo); + Load(() => BuildLoggerVerbosity, key, BkgBuild.LoggerVerbosity); + } + } catch (Exception exception) { + Messages.Print( + exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace); + } + } + + public override void SaveSettingsToStorage() + { + try { + if (!string.IsNullOrEmpty(QtMsBuildPath) + && QtMsBuildPath != Environment.GetEnvironmentVariable("QTMSBUILD")) { + Environment.SetEnvironmentVariable( + "QTMSBUILD", QtMsBuildPath, EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable( + "QTMSBUILD", QtMsBuildPath, EnvironmentVariableTarget.Process); + } + + using (var key = Registry.CurrentUser + .CreateSubKey(@"SOFTWARE\" + Resources.registryPackagePath)) { + if (key == null) + return; + Save(QmlDebuggerEnabled, key, QmlDebug.Enable); + Save(QmlDebuggerTimeout, key, QmlDebug.Timeout); + Save(HelpPreference, key, Help.Preference); + Save(TryQtHelpOnF1Pressed, key, Help.Preference); + Save(DesignerDetached, key, Designer.Detached); + Save(LinguistDetached, key, Linguist.Detached); + Save(ResourceEditorDetached, key, ResEditor.Detached); + Save(ProjectTracking, key, BkgBuild.ProjectTracking); + Save(BuildRunQtTools, key, BkgBuild.RunQtTools); + Save(BuildDebugInformation, key, BkgBuild.DebugInfo); + Save(BuildLoggerVerbosity, key, BkgBuild.LoggerVerbosity); + } + } catch (Exception exception) { + Messages.Print( + exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace); + } + } + + void Save<T>(T property, RegistryKey key, Enum name) + { + object value = property; + if (Equals<T, bool>()) + value = ((bool)(object)property) ? 1 : 0; + else if (Equals<T, Timeout>()) + value = Convert.ToInt32(property); + else if (typeof(T).IsEnum) + value = Enum.GetName(typeof(T), property); + key.SetValue(name.Cast<string>(), value); + } + + void Load<T>(Expression<Func<T>> propertyByRef, RegistryKey key, Enum name) + { + var propertyExpr = (MemberExpression)propertyByRef.Body; + var property = (PropertyInfo)propertyExpr.Member; + var regValue = key.GetValue(name.Cast<string>()); + if (Equals<T, bool>() && regValue is int numValue) + property.SetValue(this, numValue == 1); + else if (Equals<T, Timeout>() && regValue is int timeout) + property.SetValue(this, (Timeout)timeout); + else if (typeof(T).IsEnum && regValue is string enumValue) + property.SetValue(this, Enum.Parse(typeof(T), enumValue)); + else if (regValue is T value) + property.SetValue(this, value); + } + + bool Equals<T1, T2>() + { + return typeof(T1) == typeof(T2); + } + } +} diff --git a/QtVsTools.Package/Options/QtVersionsPage.cs b/QtVsTools.Package/Options/QtVersionsPage.cs new file mode 100644 index 0000000..f28e8ee --- /dev/null +++ b/QtVsTools.Package/Options/QtVersionsPage.cs @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using QtVsTools.Core; + +namespace QtVsTools.Options +{ + public class QtVersionsPage : UIElementDialogPage + { + QtVersionManager VersionManager => QtVersionManager.The(); + + QtVersionsTable _VersionsTable; + QtVersionsTable VersionsTable => _VersionsTable + ?? (_VersionsTable = new QtVersionsTable()); + + protected override UIElement Child => VersionsTable; + + public override void LoadSettingsFromStorage() + { + var versions = new List<QtVersionsTable.Row>(); + foreach (var versionName in VersionManager.GetVersions()) { + var versionPath = VersionManager.GetInstallPath(versionName); + BuildHost host = BuildHost.Windows; + string compiler = "msvc"; + if (versionPath.StartsWith("SSH:") || versionPath.StartsWith("WSL:")) { + var linuxPaths = versionPath.Split(':'); + versionPath = linuxPaths[1]; + if (linuxPaths[0] == "SSH") + host = BuildHost.LinuxSSH; + else + host = BuildHost.LinuxWSL; + compiler = "g++"; + if (linuxPaths.Length > 2 && !string.IsNullOrEmpty(linuxPaths[2])) + compiler = linuxPaths[2]; + } + var defaultVersion = VersionManager.GetDefaultVersion(); + versions.Add(new QtVersionsTable.Row() + { + IsDefault = (versionName == defaultVersion), + VersionName = versionName, + Path = versionPath, + Host = host, + Compiler = compiler, + }); + } + VersionsTable.UpdateVersions(versions); + } + + public override void SaveSettingsToStorage() + { + foreach (var versionName in VersionManager.GetVersions()) { + try { + VersionManager.RemoveVersion(versionName); + } catch (Exception exception) { + Messages.Print( + exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace); + } + } + foreach (var version in VersionsTable.Versions) { + try { + if (version.Host == BuildHost.Windows) { + var versionInfo = VersionInformation.Get(version.Path); + var generator = versionInfo.GetQMakeConfEntry("MAKEFILE_GENERATOR"); + if (generator != "MSVC.NET" && generator != "MSBUILD") + throw new Exception(SR.GetString( + "AddQtVersionDialog_IncorrectMakefileGenerator", generator)); + VersionManager.SaveVersion(version.VersionName, version.Path); + } else { + string name = version.VersionName; + string access = + (version.Host == BuildHost.LinuxSSH) ? "SSH" : "WSL"; + string path = version.Path; + string compiler = version.Compiler; + if (compiler == "g++") + compiler = string.Empty; + path = string.Format("{0}:{1}:{2}", access, path, compiler); + VersionManager.SaveVersion(name, path, checkPath: false); + } + } catch (Exception exception) { + Messages.Print( + exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace); + } + } + try { + var defaultVersion = VersionsTable.Versions + .Where(version => version.IsDefault) + .FirstOrDefault(); + if (defaultVersion != null) + VersionManager.SaveDefaultVersion(defaultVersion.VersionName); + } catch (Exception exception) { + Messages.Print( + exception.Message + "\r\n\r\nStacktrace:\r\n" + exception.StackTrace); + } + } + + protected override void OnApply(PageApplyEventArgs e) + { + var errorMessages = VersionsTable.GetErrorMessages(); + if (errorMessages == null || !errorMessages.Any()) { + base.OnApply(e); + return; + } + e.ApplyBehavior = ApplyKind.Cancel; + VersionsTable.Focus(); + string errorMessage = string.Format("Invalid Qt versions:\r\n{0}", + string.Join("\r\n", errorMessages.Select(errMsg => " * " + errMsg))); + VsShellUtilities.ShowMessageBox(QtVsToolsPackage.Instance, + errorMessage, "Qt VS Tools", OLEMSGICON.OLEMSGICON_WARNING, + OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); + } + } +} diff --git a/QtVsTools.Package/Options/QtVersionsTable.cs b/QtVsTools.Package/Options/QtVersionsTable.cs new file mode 100644 index 0000000..dc6f9c5 --- /dev/null +++ b/QtVsTools.Package/Options/QtVersionsTable.cs @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Microsoft.Win32; +using QtVsTools.Common; + +namespace QtVsTools.Options +{ + using static EnumExt; + + public enum BuildHost + { + [String("Windows")] Windows, + [String("Linux SSH")] LinuxSSH, + [String("Linux WSL")] LinuxWSL, + } + + public partial class QtVersionsTable : UserControl + { + public QtVersionsTable() + { + InitializeComponent(); + } + + public class Field + { + public string Value { get; set; } + public Control Control { get; set; } + public DataGridCell Cell { get; set; } + public string ValidationError { get; set; } + public bool IsValid => string.IsNullOrEmpty(ValidationError); + public ToolTip ToolTip + => IsValid ? null : new ToolTip() { Content = ValidationError }; + public int SelectionStart { get; set; } + } + + public class Row + { + public enum FieldNames { IsDefault, VersionName, Host, Path, Compiler } + + public Dictionary<FieldNames, Field> _Fields; + public Dictionary<FieldNames, Field> Fields => _Fields + ?? (_Fields = GetValues<FieldNames>() + .Select(field => new KeyValuePair<FieldNames, Field>(field, null)) + .ToDictionary(keyValue => keyValue.Key, keyValue => keyValue.Value)); + + public Field FieldDefault => Fields[FieldNames.IsDefault] + ?? (Fields[FieldNames.IsDefault] = new Field()); + public bool IsDefault + { + get => (FieldDefault.Value == true.ToString()); + set => FieldDefault.Value = value.ToString(); + } + + public Field FieldVersionName => Fields[FieldNames.VersionName] + ?? (Fields[FieldNames.VersionName] = new Field()); + public string VersionName + { + get => FieldVersionName.Value; + set => FieldVersionName.Value = value; + } + + public Field FieldHost => Fields[FieldNames.Host] + ?? (Fields[FieldNames.Host] = new Field()); + public BuildHost Host + { + get => FieldHost.Value.Cast(defaultValue: BuildHost.Windows); + set => FieldHost.Value = value.Cast<string>(); + } + + public Field FieldPath => Fields[FieldNames.Path] + ?? (Fields[FieldNames.Path] = new Field()); + public string Path + { + get => FieldPath.Value; + set => FieldPath.Value = value; + } + + public Field FieldCompiler => Fields[FieldNames.Compiler] + ?? (Fields[FieldNames.Compiler] = new Field()); + public string Compiler + { + get => FieldCompiler.Value; + set => FieldCompiler.Value = value; + } + + public bool LastRow { get; set; } + + public bool DefaultEnabled => !IsDefault && !LastRow; + public bool NameEnabled => !LastRow; + public bool CompilerEnabled => (Host != BuildHost.Windows); + public Visibility RowVisibility + => LastRow ? Visibility.Hidden : Visibility.Visible; + public Visibility ButtonAddVisibility + => LastRow ? Visibility.Visible : Visibility.Hidden; + public Visibility ButtonBrowseVisibility + => (!LastRow && Host == BuildHost.Windows) ? Visibility.Visible : Visibility.Hidden; + public Thickness PathMargin + => new Thickness(((Host == BuildHost.Windows) ? 22 : 2), 0, 2, 0); + public FontWeight FontWeight + => IsDefault ? FontWeights.Bold : FontWeights.Normal; + + public static ImageSource _ExplorerIcon; + public static ImageSource ExplorerIcon => _ExplorerIcon + ?? (_ExplorerIcon = GetExplorerIcon()); + } + + public bool IsValid { get; private set; } + + Field FocusedField { get; set; } + + List<Row> _Rows; + List<Row> Rows => _Rows ?? (_Rows = new List<Row>()); + public IEnumerable<Row> Versions => Rows.TakeWhile(item => !item.LastRow); + + public void UpdateVersions(IEnumerable<Row> versions) + { + Rows.Clear(); + Rows.AddRange(versions); + Rows.Add(new Row { LastRow = true }); + DataGrid.ItemsSource = Rows; + IsValid = true; + FocusedField = null; + Validate(true); + } + + public IEnumerable<string> GetErrorMessages() + { + Validate(true); + return Versions + .SelectMany(v => v.Fields.Values.Select(f => f.ValidationError)) + .Where(s => !string.IsNullOrEmpty(s)) + .Distinct(); + } + + void Validate(bool mustRefresh) + { + ///////////////////////// + // Automatic cell values + foreach (var version in Versions) { + if (version.Host != BuildHost.Windows && version.Compiler == "msvc") { + version.Compiler = "g++"; + version.FieldCompiler.SelectionStart = version.Compiler.Length; + mustRefresh = true; + } else if (version.Host == BuildHost.Windows && version.Compiler != "msvc") { + version.Compiler = "msvc"; + version.FieldCompiler.SelectionStart = version.Compiler.Length; + mustRefresh = true; + } + } + + //////////////////////// + // Validate cell values + string previousValidation; + bool wasValid = IsValid; + IsValid = true; + foreach (var version in Versions) { + + ////////////////////// + // Default validation + previousValidation = version.FieldDefault.ValidationError; + version.FieldDefault.ValidationError = null; + if (version.IsDefault && version.Host != BuildHost.Windows) { + version.FieldDefault.ValidationError = "Default version: host must be Windows"; + IsValid = false; + } + if (previousValidation != version.FieldDefault.ValidationError) + mustRefresh = true; + + /////////////////// + // Name validation + previousValidation = version.FieldVersionName.ValidationError; + version.FieldVersionName.ValidationError = null; + if (string.IsNullOrEmpty(version.VersionName)) { + version.FieldVersionName.ValidationError = "Name cannot be empty"; + IsValid = false; + } else if (Versions + .Where(otherVersion => otherVersion != version + && otherVersion.VersionName == version.VersionName) + .Any()) { + version.FieldVersionName.ValidationError = "Duplicate version names"; + IsValid = false; + } + if (previousValidation != version.FieldVersionName.ValidationError) + mustRefresh = true; + + /////////////////// + // Host validation + previousValidation = version.FieldHost.ValidationError; + version.FieldHost.ValidationError = null; + if (version.IsDefault && version.Host != BuildHost.Windows) { + version.FieldHost.ValidationError = "Default version: host must be Windows"; + IsValid = false; + } + if (previousValidation != version.FieldHost.ValidationError) + mustRefresh = true; + + /////////////////// + // Path validation + previousValidation = version.FieldPath.ValidationError; + version.FieldPath.ValidationError = null; + if (string.IsNullOrEmpty(version.Path)) { + version.FieldPath.ValidationError = "Path cannot be empty"; + IsValid = false; + } else if (version.Host == BuildHost.Windows && !Directory.Exists(version.Path)) { + version.FieldPath.ValidationError = "Path does not exist"; + IsValid = false; + } + if (previousValidation != version.FieldPath.ValidationError) + mustRefresh = true; + + /////////////////////// + // Compiler validation + previousValidation = version.FieldCompiler.ValidationError; + version.FieldCompiler.ValidationError = null; + if (string.IsNullOrEmpty(version.Compiler)) { + version.FieldCompiler.ValidationError = "Compiler cannot be empty"; + IsValid = false; + } + if (previousValidation != version.FieldCompiler.ValidationError) + mustRefresh = true; + } + + ////////////////////////////////////// + // Refresh versions table if required + mustRefresh |= (wasValid != IsValid); + if (mustRefresh) { + // Reset bindings + foreach (var version in Versions) { + foreach (var field in version.Fields.Values) { + field.Control = null; + field.Cell = null; + } + } + // Refresh UI + DataGrid.Items.Refresh(); + } + } + + static readonly Brush InvalidCellBackground = new DrawingBrush + { + TileMode = TileMode.Tile, + Viewport = new Rect(0.0, 0.0, 10.0, 10.0), + ViewportUnits = BrushMappingMode.Absolute, + Viewbox = new Rect(0.0, 0.0, 10.0, 10.0), + ViewboxUnits = BrushMappingMode.Absolute, + Drawing = new DrawingGroup + { + Children = new DrawingCollection + { + new GeometryDrawing + { + Brush = Brushes.Red, + Geometry = new RectangleGeometry(new Rect(5, 0, 5, 10)) + } + } + }, + Transform = new RotateTransform + { + Angle = -135.0, + CenterX = 0.5, + CenterY = 0.5 + }, + Opacity = 0.25 + }; + + void Control_Loaded(object sender, RoutedEventArgs e) + { + if (sender is Control control && GetBinding(control) is Row version) { + if (version.LastRow) + return; + + Row.FieldNames field; + if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out field)) + return; + + var fieldBinding = version.Fields[field]; + fieldBinding.Control = control; + fieldBinding.Cell = FindContainingCell(control); + if (fieldBinding.Cell != null) { + fieldBinding.Cell.Background = + fieldBinding.IsValid ? Brushes.Transparent : InvalidCellBackground; + } + if (fieldBinding == FocusedField) + control.Focus(); + if (control is TextBox textBox && fieldBinding.SelectionStart >= 0) + textBox.Select(fieldBinding.SelectionStart, 0); + } + } + + void ComboBox_Loaded(object sender, RoutedEventArgs e) + { + if (sender is ComboBox comboBox && GetBinding(comboBox) is Row version) { + comboBox.IsEnabled = false; + var hosts = GetValues<string>(typeof(BuildHost)); + comboBox.ItemsSource = hosts; + comboBox.Text = version.Host.Cast<string>(); + comboBox.SelectedIndex = (int)version.Host; + comboBox.IsEnabled = true; + } + Control_Loaded(sender, e); + } + + void Control_GotFocus(object sender, RoutedEventArgs e) + { + if (sender is Control control && GetBinding(control) is Row version) { + Row.FieldNames field; + if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out field)) + return; + var fieldBinding = version.Fields[field]; + if (fieldBinding.Control != control) + return; + FocusedField = fieldBinding; + } + } + + void Control_LostFocus(object sender, RoutedEventArgs e) + { + if (sender is Control control && GetBinding(control) is Row version) { + Row.FieldNames field; + if (string.IsNullOrEmpty(control.Name) || !control.Name.TryCast(out field)) + return; + var fieldBinding = version.Fields[field]; + if (fieldBinding != FocusedField || fieldBinding.Control != control) + return; + FocusedField = null; + } + } + + void TextBox_SelectionChanged(object sender, RoutedEventArgs e) + { + if (sender is TextBox textBox && GetBinding(textBox) is Row version) { + Row.FieldNames field; + if (string.IsNullOrEmpty(textBox.Name) || !textBox.Name.TryCast(out field)) + return; + var fieldBinding = version.Fields[field]; + if (fieldBinding.Control != textBox) + return; + fieldBinding.SelectionStart = textBox.SelectionStart; + } + } + + void TextBox_TextChanged(object sender, TextChangedEventArgs e) + { + if (sender is TextBox textBox && GetBinding(textBox) is Row version) { + Row.FieldNames field; + if (string.IsNullOrEmpty(textBox.Name) || !textBox.Name.TryCast(out field)) + return; + var fieldBinding = version.Fields[field]; + if (fieldBinding == null + || fieldBinding.Control != textBox + || fieldBinding.Value == textBox.Text) + return; + fieldBinding.SelectionStart = textBox.SelectionStart; + fieldBinding.Value = textBox.Text; + Validate(false); + } + } + + void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (sender is ComboBox comboBox && GetBinding(comboBox) is Row version) { + if (!comboBox.IsEnabled || comboBox.SelectedIndex < 0) + return; + string comboBoxValue = comboBox.Items[comboBox.SelectedIndex] as string; + string controlName = comboBox.Name; + Row.FieldNames field; + if (string.IsNullOrEmpty(controlName) || !controlName.TryCast(out field)) + return; + var fieldBinding = version.Fields[field]; + if (fieldBinding == null + || fieldBinding.Control != comboBox + || fieldBinding.Value == comboBoxValue) + return; + fieldBinding.Value = comboBoxValue; + Validate(false); + } + } + + void Default_Click(object sender, RoutedEventArgs e) + { + if (sender is CheckBox checkBox && GetBinding(checkBox) is Row version) { + var defaultVersion = Rows.Where(row => row.IsDefault).FirstOrDefault(); + if (defaultVersion != null) + defaultVersion.IsDefault = false; + version.IsDefault = true; + Validate(true); + } + } + + void Add_Click(object sender, RoutedEventArgs e) + { + var version = new Row() + { + IsDefault = !Versions.Any(), + Host = BuildHost.Windows, + Path = "", + Compiler = "msvc", + LastRow = false + }; + Rows.Insert(Rows.Count - 1, version); + FocusedField = version.FieldVersionName; + Validate(true); + } + + void Remove_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && GetBinding(button) is Row version) { + Rows.Remove(version); + if (version.IsDefault && Versions.Any()) + Versions.First().IsDefault = true; + Validate(true); + } + } + + void Explorer_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && GetBinding(button) is Row version) { + var openFileDialog = new OpenFileDialog() + { + AddExtension = false, + CheckFileExists = true, + CheckPathExists = true, + Filter = "qmake Executable|qmake.exe", + Title = "Qt VS Tools - Select qmake.exe" + }; + if (openFileDialog.ShowDialog() == true) { + var qmakePath = openFileDialog.FileName; + var qmakeDir = Path.GetDirectoryName(qmakePath); + if (Path.GetFileName(qmakeDir) + .Equals("bin", StringComparison.InvariantCultureIgnoreCase)) { + qmakeDir = Path.GetDirectoryName(qmakeDir); + version.Path = qmakeDir; + } else { + version.Path = qmakePath; + } + if (string.IsNullOrEmpty(version.VersionName)) { + version.VersionName = string.Format("{0}_{1}", + Path.GetFileName(Path.GetDirectoryName(qmakeDir)), + Path.GetFileName(qmakeDir)) + .Replace(" ", "_"); + } + Validate(true); + } + } + } + + static ImageSource GetExplorerIcon() + { + var pathWindowsExplorer = string.Format(@"{0}\explorer.exe", + Environment.GetFolderPath(Environment.SpecialFolder.Windows)); + + NativeAPI.SHFILEINFO shellFileInfo = new NativeAPI.SHFILEINFO(); + NativeAPI.SHGetFileInfo(pathWindowsExplorer, + 0, ref shellFileInfo, Marshal.SizeOf(shellFileInfo), + NativeAPI.SHGFI.Icon | NativeAPI.SHGFI.SmallIcon); + if (shellFileInfo.hIcon == IntPtr.Zero) + return null; + + var iconImageSource = Imaging.CreateBitmapSourceFromHIcon( + shellFileInfo.hIcon, Int32Rect.Empty, + BitmapSizeOptions.FromEmptyOptions()); + + NativeAPI.DestroyIcon(shellFileInfo.hIcon); + return iconImageSource; + } + + static object GetBinding(FrameworkElement control) + { + if (control == null + || control.BindingGroup == null + || control.BindingGroup.Items == null + || control.BindingGroup.Items.Count == 0) { + return null; + } + return control.BindingGroup.Items[0]; + } + + static DataGridCell FindContainingCell(DependencyObject control) + { + while (control != null) { + if (control is ContentPresenter contentPresenter + && contentPresenter.Parent is DataGridCell cell) { + return cell; + } + control = VisualTreeHelper.GetParent(control); + } + return null; + } + } +} diff --git a/QtVsTools.Package/Options/QtVersionsTable.xaml b/QtVsTools.Package/Options/QtVersionsTable.xaml new file mode 100644 index 0000000..1b147ff --- /dev/null +++ b/QtVsTools.Package/Options/QtVersionsTable.xaml @@ -0,0 +1,286 @@ +<!-- +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<UserControl x:Class="QtVsTools.Options.QtVersionsTable" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:QtVsTools.Options" + mc:Ignorable="d" + d:DesignHeight="450" + d:DesignWidth="800"> + <DataGrid Margin="0,0,0,0" + Name="DataGrid" + AutoGenerateColumns="False" + IsReadOnly="True" + BorderThickness="1" + BorderBrush="LightGray" + GridLinesVisibility="All" + HorizontalGridLinesBrush="LightGray" + VerticalGridLinesBrush="LightGray" + CanUserReorderColumns="False" + CanUserSortColumns="False" + CanUserResizeRows="False" + RowHeaderWidth="0" + FrozenColumnCount="0" + Grid.Row="0"> + <DataGrid.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="Transparent" /> + <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" + Color="Transparent" /> + <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" + Color="Transparent" /> + </DataGrid.Resources> + <DataGrid.CellStyle> + <Style TargetType="DataGridCell"> + <Setter Property="BorderThickness" + Value="0" /> + <Setter Property="FocusVisualStyle" + Value="{x:Null}" /> + </Style> + </DataGrid.CellStyle> + <DataGrid.Columns> + <DataGridTemplateColumn Header="Default" + CanUserResize="False"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <!--//// Default ////--> + <CheckBox x:Name="IsDefault" + IsChecked="{Binding IsDefault}" + Focusable="{Binding DefaultEnabled}" + IsHitTestVisible="{Binding DefaultEnabled}" + Visibility="{Binding RowVisibility}" + BorderThickness="1" + Background="Transparent" + VerticalAlignment="Center" + HorizontalAlignment="Center" + Click="Default_Click" + Loaded="Control_Loaded" + ToolTip="{Binding FieldDefault.ToolTip}"></CheckBox> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Version"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <!--//// Name ////--> + <Grid> + <Button Cursor="Hand" + Visibility="{Binding RowVisibility}" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Click="Remove_Click"> + <Button.Template> + <ControlTemplate TargetType="Button"> + <Grid HorizontalAlignment="Center" + VerticalAlignment="Center" + Margin="4,0"> + <Ellipse Fill="White" + Width="15" + Height="15" /> + <Ellipse Fill="Red" + Width="13" + Height="13" /> + <Rectangle Width="8" + Height="2" + Fill="White" /> + </Grid> + </ControlTemplate> + </Button.Template> + </Button> + <Button Cursor="Hand" + Visibility="{Binding ButtonAddVisibility}" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Click="Add_Click"> + <Button.Template> + <ControlTemplate TargetType="Button"> + <Grid HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Margin="4,0" + Background="Transparent"> + <Grid HorizontalAlignment="Left" + VerticalAlignment="Center"> + <Ellipse Fill="White" + Width="15" + Height="15" /> + <Ellipse Fill="#FF36B31A" + Width="13" + Height="13" /> + <Rectangle Width="8" + Height="2" + Fill="White" /> + <Rectangle Width="2" + Height="8" + Fill="White" /> + </Grid> + <TextBlock Text="<add new Qt version>" + Foreground="SlateGray" + Margin="20,4,2,4" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" /> + </Grid> + </ControlTemplate> + </Button.Template> + </Button> + <TextBox x:Name="VersionName" + Text="{Binding VersionName}" + Visibility="{Binding RowVisibility}" + IsEnabled="{Binding NameEnabled}" + FontWeight="{Binding FontWeight}" + Margin="20,4,2,4" + Background="Transparent" + BorderThickness="0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Loaded="Control_Loaded" + GotFocus="Control_GotFocus" + LostFocus="Control_LostFocus" + TextChanged="TextBox_TextChanged" + SelectionChanged="TextBox_SelectionChanged" + ToolTip="{Binding FieldName.ToolTip}"> + <TextBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + </TextBox.Resources> + </TextBox> + </Grid> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Host"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <!--//// Host ////--> + <ComboBox x:Name="Host" + Visibility="{Binding RowVisibility}" + IsEditable="True" + IsReadOnly="True" + BorderThickness="0" + Background="Transparent" + Margin="2,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + FrameworkElement.Loaded="ComboBox_Loaded" + SelectionChanged="ComboBox_SelectionChanged" + ToolTip="{Binding FieldHost.ToolTip}"> + <ComboBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" + Color="Transparent" /> + </ComboBox.Resources> + </ComboBox> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Path"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <Grid> + <!--//// Path ////--> + <Button Cursor="Hand" + Visibility="{Binding ButtonBrowseVisibility}" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Margin="2,0" + Width="18" + Height="18" + Click="Explorer_Click" + Background="White"> + <Button.Template> + <ControlTemplate TargetType="Button"> + <Grid HorizontalAlignment="Center" + VerticalAlignment="Center"> + <Rectangle Fill="White" + RadiusX="5" + RadiusY="5" + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch" /> + <Image Source="{Binding ExplorerIcon}" + Margin="2" /> + </Grid> + </ControlTemplate> + </Button.Template> + </Button> + <TextBox x:Name="Path" + Text="{Binding Path}" + Visibility="{Binding RowVisibility}" + BorderThickness="0" + Background="Transparent" + Margin="{Binding PathMargin}" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Loaded="Control_Loaded" + GotFocus="Control_GotFocus" + LostFocus="Control_LostFocus" + TextChanged="TextBox_TextChanged" + SelectionChanged="TextBox_SelectionChanged" + ToolTip="{Binding FieldPath.ToolTip}"> + <TextBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + </TextBox.Resources> + </TextBox> + </Grid> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Compiler"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <!--//// Compiler ////--> + <TextBox x:Name="Compiler" + Text="{Binding Compiler}" + Visibility="{Binding RowVisibility}" + IsEnabled="{Binding CompilerEnabled}" + BorderThickness="0" + Background="Transparent" + Margin="2,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Loaded="Control_Loaded" + GotFocus="Control_GotFocus" + LostFocus="Control_LostFocus" + TextChanged="TextBox_TextChanged" + SelectionChanged="TextBox_SelectionChanged" + ToolTip="{Binding FieldCompiler.ToolTip}"> + <TextBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + </TextBox.Resources> + </TextBox> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + </DataGrid.Columns> + </DataGrid> +</UserControl> diff --git a/QtVsTools.Package/Package/AddTranslationDialog.cs b/QtVsTools.Package/Package/AddTranslationDialog.cs new file mode 100644 index 0000000..ee83e82 --- /dev/null +++ b/QtVsTools.Package/Package/AddTranslationDialog.cs @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Windows.Forms; + +namespace QtVsTools +{ + /// <summary> + /// Summary description for AddTranslationDialog. + /// </summary> + public class AddTranslationDialog : Form + { + private Label langLabel; + private ComboBox langComboBox; + private Label label1; + private Button okButton; + private Button cancelButton; + private TextBox fileTextBox; + private readonly EnvDTE.Project project; + private Panel panel1; + + public AddTranslationDialog(EnvDTE.Project pro) + { + project = pro; + // + // Required for Windows Form Designer support + // + InitializeComponent(); + ShowInTaskbar = false; + + langLabel.Text = SR.GetString("AddTranslationDialog_Language"); + cancelButton.Text = SR.GetString(SR.Cancel); + okButton.Text = SR.GetString(SR.OK); + label1.Text = SR.GetString("AddTranslationDialog_FileName"); + + FormBorderStyle = FormBorderStyle.FixedDialog; + KeyPress += AddTranslationDialog_KeyPress; + Shown += AddTranslationDialog_Shown; + } + + private void AddTranslationDialog_Shown(object sender, EventArgs e) + { + Text = SR.GetString("AddTranslationDialog_Title"); + } + + void AddTranslationDialog_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == 27) { + DialogResult = DialogResult.Cancel; + Close(); + } + } + + public string TranslationFile + { + get { return fileTextBox.Text; } + } + + #region Windows Form Designer generated code + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + langLabel = new Label(); + langComboBox = new ComboBox(); + cancelButton = new Button(); + okButton = new Button(); + label1 = new Label(); + fileTextBox = new TextBox(); + panel1 = new Panel(); + panel1.SuspendLayout(); + SuspendLayout(); + // + // langLabel + // + langLabel.Anchor = ((AnchorStyles)(((AnchorStyles.Top | AnchorStyles.Left) + | AnchorStyles.Right))); + langLabel.Location = new System.Drawing.Point(8, 8); + langLabel.Name = "langLabel"; + langLabel.Size = new System.Drawing.Size(72, 21); + langLabel.TabIndex = 0; + langLabel.Text = "Language"; + // + // langComboBox + // + langComboBox.Anchor = ((AnchorStyles)((AnchorStyles.Top | AnchorStyles.Right))); + langComboBox.AutoCompleteMode = AutoCompleteMode.Suggest; + langComboBox.AutoCompleteSource = AutoCompleteSource.ListItems; + langComboBox.DropDownStyle = ComboBoxStyle.DropDownList; + langComboBox.Location = new System.Drawing.Point(80, 8); + langComboBox.Name = "langComboBox"; + langComboBox.Size = new System.Drawing.Size(192, 21); + langComboBox.Sorted = true; + langComboBox.TabIndex = 1; + langComboBox.SelectedIndexChanged += langComboBox_SelectedIndexChanged; + // + // cancelButton + // + cancelButton.Anchor = ((AnchorStyles)((AnchorStyles.Bottom | AnchorStyles.Right))); + cancelButton.DialogResult = DialogResult.Cancel; + cancelButton.Location = new System.Drawing.Point(200, 72); + cancelButton.Name = "cancelButton"; + cancelButton.Size = new System.Drawing.Size(72, 24); + cancelButton.TabIndex = 1; + cancelButton.Text = "Cancel"; + // + // okButton + // + okButton.Anchor = ((AnchorStyles)((AnchorStyles.Bottom | AnchorStyles.Right))); + okButton.DialogResult = DialogResult.OK; + okButton.Location = new System.Drawing.Point(120, 72); + okButton.Name = "okButton"; + okButton.Size = new System.Drawing.Size(72, 24); + okButton.TabIndex = 0; + okButton.Text = "OK"; + // + // label1 + // + label1.Anchor = ((AnchorStyles)(((AnchorStyles.Top | AnchorStyles.Left) + | AnchorStyles.Right))); + label1.Location = new System.Drawing.Point(8, 32); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(72, 24); + label1.TabIndex = 3; + label1.Text = "Filename"; + // + // fileTextBox + // + fileTextBox.Anchor = ((AnchorStyles)((AnchorStyles.Top | AnchorStyles.Right))); + fileTextBox.Location = new System.Drawing.Point(80, 32); + fileTextBox.Name = "fileTextBox"; + fileTextBox.Size = new System.Drawing.Size(192, 20); + fileTextBox.TabIndex = 4; + // + // panel1 + // + panel1.Anchor = ((AnchorStyles)(((AnchorStyles.Top | AnchorStyles.Left) + | AnchorStyles.Right))); + panel1.Controls.Add(langLabel); + panel1.Controls.Add(label1); + panel1.Controls.Add(langComboBox); + panel1.Controls.Add(fileTextBox); + panel1.Location = new System.Drawing.Point(0, 0); + panel1.Name = "panel1"; + panel1.Size = new System.Drawing.Size(280, 64); + panel1.TabIndex = 5; + // + // AddTranslationDialog + // + AcceptButton = okButton; + AutoScaleBaseSize = new System.Drawing.Size(5, 13); + CancelButton = cancelButton; + ClientSize = new System.Drawing.Size(282, 104); + Controls.Add(panel1); + Controls.Add(okButton); + Controls.Add(cancelButton); + KeyPreview = true; + MaximizeBox = false; + MinimizeBox = false; + Name = "AddTranslationDialog"; + SizeGripStyle = SizeGripStyle.Hide; + StartPosition = FormStartPosition.CenterParent; + Text = "Add Translation"; + Load += AddTranslationDialog_Load; + panel1.ResumeLayout(false); + panel1.PerformLayout(); + ResumeLayout(false); + + } + #endregion + + private void AddTranslationDialog_Load(object sender, System.EventArgs e) + { + var cultures = TranslationItem.GetTranslationItems(); + langComboBox.Items.AddRange(cultures); + langComboBox.SelectedItem = TranslationItem.SystemLanguage(); + } + + private void langComboBox_SelectedIndexChanged(object sender, System.EventArgs e) + { + var country = ((TranslationItem)langComboBox.SelectedItem).TwoLetterISOLanguageName; + fileTextBox.Text = project.Name.ToLower() + "_" + country + ".ts"; + } + } +} diff --git a/QtVsTools.Package/Package/AddTranslationDialog.resx b/QtVsTools.Package/Package/AddTranslationDialog.resx new file mode 100644 index 0000000..ff31a6d --- /dev/null +++ b/QtVsTools.Package/Package/AddTranslationDialog.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/QtVsTools.Package/Package/ChangeFor.cs b/QtVsTools.Package/Package/ChangeFor.cs new file mode 100644 index 0000000..5128c82 --- /dev/null +++ b/QtVsTools.Package/Package/ChangeFor.cs @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools +{ + public enum ChangeFor { Solution, Project } +} diff --git a/QtVsTools.Package/Package/DteEventsHandler.cs b/QtVsTools.Package/Package/DteEventsHandler.cs new file mode 100644 index 0000000..c7304c0 --- /dev/null +++ b/QtVsTools.Package/Package/DteEventsHandler.cs @@ -0,0 +1,586 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using EnvDTE80; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.Core; +using QtVsTools.Core.QtMsBuild; +using QtVsTools.QtMsBuild; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace QtVsTools +{ + class DteEventsHandler + { + private DTE dte; + private SolutionEvents solutionEvents; + private BuildEvents buildEvents; + private DocumentEvents documentEvents; + private ProjectItemsEvents projectItemsEvents; + private vsBuildAction currentBuildAction = vsBuildAction.vsBuildActionBuild; + private VCProjectEngineEvents vcProjectEngineEvents; + private CommandEvents debugStartEvents; + private CommandEvents debugStartWithoutDebuggingEvents; + private CommandEvents f1HelpEvents; + private int dispId_VCFileConfiguration_ExcludedFromBuild; + private int dispId_VCCLCompilerTool_UsePrecompiledHeader; + private int dispId_VCCLCompilerTool_PrecompiledHeaderThrough; + private int dispId_VCCLCompilerTool_PreprocessorDefinitions; + private int dispId_VCCLCompilerTool_AdditionalIncludeDirectories; + + public DteEventsHandler(DTE _dte) + { + dte = _dte; + var events = dte.Events as Events2; + + buildEvents = events.BuildEvents; + buildEvents.OnBuildBegin += buildEvents_OnBuildBegin; + buildEvents.OnBuildProjConfigBegin += OnBuildProjConfigBegin; + + documentEvents = events.get_DocumentEvents(null); + documentEvents.DocumentSaved += DocumentSaved; + + projectItemsEvents = events.ProjectItemsEvents; + projectItemsEvents.ItemAdded += ProjectItemsEvents_ItemAdded; + projectItemsEvents.ItemRemoved += ProjectItemsEvents_ItemRemoved; + projectItemsEvents.ItemRenamed += ProjectItemsEvents_ItemRenamed; + + solutionEvents = events.SolutionEvents; + solutionEvents.ProjectAdded += SolutionEvents_ProjectAdded; + solutionEvents.ProjectRemoved += SolutionEvents_ProjectRemoved; + solutionEvents.Opened += SolutionEvents_Opened; + solutionEvents.AfterClosing += SolutionEvents_AfterClosing; + + var debugCommandsGUID = "{5EFC7975-14BC-11CF-9B2B-00AA00573819}"; + debugStartEvents = events.get_CommandEvents(debugCommandsGUID, 295); + debugStartEvents.BeforeExecute += debugStartEvents_BeforeExecute; + + debugStartWithoutDebuggingEvents = events.get_CommandEvents(debugCommandsGUID, 368); + debugStartWithoutDebuggingEvents.BeforeExecute += debugStartWithoutDebuggingEvents_BeforeExecute; + + f1HelpEvents = events.get_CommandEvents( + typeof(VSConstants.VSStd97CmdID).GUID.ToString("B"), + (int)VSConstants.VSStd97CmdID.F1Help); + f1HelpEvents.BeforeExecute += F1HelpEvents_BeforeExecute; + + dispId_VCFileConfiguration_ExcludedFromBuild = GetPropertyDispId(typeof(VCFileConfiguration), "ExcludedFromBuild"); + dispId_VCCLCompilerTool_UsePrecompiledHeader = GetPropertyDispId(typeof(VCCLCompilerTool), "UsePrecompiledHeader"); + dispId_VCCLCompilerTool_PrecompiledHeaderThrough = GetPropertyDispId(typeof(VCCLCompilerTool), "PrecompiledHeaderThrough"); + dispId_VCCLCompilerTool_PreprocessorDefinitions = GetPropertyDispId(typeof(VCCLCompilerTool), "PreprocessorDefinitions"); + dispId_VCCLCompilerTool_AdditionalIncludeDirectories = GetPropertyDispId(typeof(VCCLCompilerTool), "AdditionalIncludeDirectories"); + InitializeVCProjects(); + } + + private void F1HelpEvents_BeforeExecute( + string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + { + if (QtVsToolsPackage.Instance.Options.TryQtHelpOnF1Pressed && QtHelp.QueryEditorContextHelp()) + CancelDefault = true; + } + + void debugStartEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + { + var debugger = dte.Debugger; + if (debugger != null && debugger.CurrentMode != dbgDebugMode.dbgDesignMode) + return; + var selectedProject = HelperFunctions.GetSelectedQtProject(dte); + if (selectedProject != null) { + if (QtProject.GetFormatVersion(selectedProject) >= Resources.qtMinFormatVersion_Settings) + return; + var qtProject = QtProject.Create(selectedProject); + if (qtProject != null) { + qtProject.SetQtEnvironment(); + + var qtVersion = qtProject.GetQtVersion(); + var versionInfo = QtVersionManager.The().GetVersionInfo(qtVersion); + if (!string.IsNullOrEmpty(versionInfo.Namespace())) + QtVsToolsPackage.Instance.CopyNatvisFiles(versionInfo.Namespace()); + } + } + } + + void debugStartWithoutDebuggingEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault) + { + var selectedProject = HelperFunctions.GetSelectedQtProject(dte); + if (selectedProject != null) { + if (QtProject.GetFormatVersion(selectedProject) >= Resources.qtMinFormatVersion_Settings) + return; + var qtProject = QtProject.Create(selectedProject); + if (qtProject != null) + qtProject.SetQtEnvironment(); + } + } + + public void Disconnect() + { + if (buildEvents != null) { + buildEvents.OnBuildBegin -= buildEvents_OnBuildBegin; + buildEvents.OnBuildProjConfigBegin -= OnBuildProjConfigBegin; + } + + if (documentEvents != null) + documentEvents.DocumentSaved -= DocumentSaved; + + if (projectItemsEvents != null) { + projectItemsEvents.ItemAdded -= ProjectItemsEvents_ItemAdded; + projectItemsEvents.ItemRemoved -= ProjectItemsEvents_ItemRemoved; + projectItemsEvents.ItemRenamed -= ProjectItemsEvents_ItemRenamed; + } + + if (solutionEvents != null) { + solutionEvents.ProjectAdded -= SolutionEvents_ProjectAdded; + solutionEvents.ProjectRemoved -= SolutionEvents_ProjectRemoved; + solutionEvents.Opened -= SolutionEvents_Opened; + solutionEvents.AfterClosing -= SolutionEvents_AfterClosing; + } + + if (debugStartEvents != null) + debugStartEvents.BeforeExecute -= debugStartEvents_BeforeExecute; + + if (debugStartWithoutDebuggingEvents != null) + debugStartWithoutDebuggingEvents.BeforeExecute -= debugStartWithoutDebuggingEvents_BeforeExecute; + + if (vcProjectEngineEvents != null) + vcProjectEngineEvents.ItemPropertyChange -= OnVCProjectEngineItemPropertyChange; + } + + public void OnBuildProjConfigBegin(string projectName, string projectConfig, string platform, string solutionConfig) + { + if (!QtVsToolsPackage.Instance.LegacyOptions.PreBuildSetup) + return; + + if (currentBuildAction != vsBuildAction.vsBuildActionBuild && + currentBuildAction != vsBuildAction.vsBuildActionRebuildAll) { + return; // Don't do anything, if we're not building. + } + + Project project = null; + foreach (var p in HelperFunctions.ProjectsInSolution(dte)) { + if (p.UniqueName == projectName) { + project = p; + break; + } + } + if (project == null || !HelperFunctions.IsQtProject(project)) + return; + + if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) + return; + + var qtpro = QtProject.Create(project); + var versionManager = QtVersionManager.The(); + var qtVersion = versionManager.GetProjectQtVersion(project, platform); + if (qtVersion == null) { + Messages.DisplayCriticalErrorMessage(SR.GetString("ProjectQtVersionNotFoundError", projectName, projectConfig, platform)); + dte.ExecuteCommand("Build.Cancel", ""); + return; + } + + if (!QtVSIPSettings.GetDisableAutoMocStepsUpdate()) { + if (qtpro.ConfigurationRowNamesChanged) + qtpro.UpdateMocSteps(QtVSIPSettings.GetMocDirectory(project)); + } + + // Solution config is given to function to get QTDIR property + // set correctly also during batch build + qtpro.SetQtEnvironment(qtVersion, solutionConfig, true); + if (QtVSIPSettings.GetLUpdateOnBuild(project)) + Translation.RunlUpdate(project); + } + + void buildEvents_OnBuildBegin(vsBuildScope Scope, vsBuildAction Action) + { + currentBuildAction = Action; + } + + public void DocumentSaved(Document document) + { + var qtPro = QtProject.Create(document.ProjectItem.ContainingProject); + + if (!HelperFunctions.IsQtProject(qtPro.VCProject)) + return; + + var file = (VCFile)((IVCCollection)qtPro.VCProject.Files).Item(document.FullName); + + if (HelperFunctions.IsUicFile(file.Name)) { + if (QtVSIPSettings.AutoUpdateUicSteps() && !QtProject.HasUicStep(file)) + qtPro.AddUic4BuildStep(file); + return; + } + + if (!HelperFunctions.IsSourceFile(file.Name) && !HelperFunctions.IsHeaderFile(file.Name)) + return; + + if (HelperFunctions.HasQObjectDeclaration(file)) { + if (!qtPro.HasMocStep(file)) + qtPro.AddMocStep(file); + } else { + if (qtPro.HasMocStep(file)) + qtPro.RemoveMocStep(file); + } + + if (HelperFunctions.IsSourceFile(file.Name)) { + var moccedFileName = "moc_" + file.Name; + + if (qtPro.IsMoccedFileIncluded(file)) { + foreach (var moccedFile in qtPro.GetFilesFromProject(moccedFileName)) + QtProject.ExcludeFromAllBuilds(moccedFile); + } else { + var moccedFiles = qtPro.GetFilesFromProject(moccedFileName); + if (moccedFiles.Any()) { + var hasDifferentMocFilesPerConfig = QtVSIPSettings.HasDifferentMocFilePerConfig(qtPro.Project); + var hasDifferentMocFilesPerPlatform = QtVSIPSettings.HasDifferentMocFilePerPlatform(qtPro.Project); + var generatedFiles = qtPro.FindFilterFromGuid(Filters.GeneratedFiles().UniqueIdentifier); + foreach (VCFile fileInFilter in (IVCCollection)generatedFiles.Files) { + if (fileInFilter.Name == moccedFileName) { + foreach (VCFileConfiguration config in (IVCCollection)fileInFilter.FileConfigurations) { + var exclude = true; + var vcConfig = config.ProjectConfiguration as VCConfiguration; + if (hasDifferentMocFilesPerConfig && hasDifferentMocFilesPerPlatform) { + var platform = vcConfig.Platform as VCPlatform; + if (fileInFilter.RelativePath.ToLower().Contains(vcConfig.ConfigurationName.ToLower()) + && fileInFilter.RelativePath.ToLower().Contains(platform.Name.ToLower())) + exclude = false; + } else if (hasDifferentMocFilesPerConfig) { + if (fileInFilter.RelativePath.ToLower().Contains(vcConfig.ConfigurationName.ToLower())) + exclude = false; + } else if (hasDifferentMocFilesPerPlatform) { + var platform = vcConfig.Platform as VCPlatform; + var platformName = platform.Name; + if (fileInFilter.RelativePath.ToLower().Contains(platformName.ToLower())) + exclude = false; + } else { + exclude = false; + } + if (config.ExcludedFromBuild != exclude) + config.ExcludedFromBuild = exclude; + } + } + } + foreach (VCFilter filt in (IVCCollection)generatedFiles.Filters) { + foreach (VCFile f in (IVCCollection)filt.Files) { + if (f.Name == moccedFileName) { + foreach (VCFileConfiguration config in (IVCCollection)f.FileConfigurations) { + var vcConfig = config.ProjectConfiguration as VCConfiguration; + var filterToLookFor = string.Empty; + if (hasDifferentMocFilesPerConfig) + filterToLookFor = vcConfig.ConfigurationName; + if (hasDifferentMocFilesPerPlatform) { + var platform = vcConfig.Platform as VCPlatform; + if (!string.IsNullOrEmpty(filterToLookFor)) + filterToLookFor += '_'; + filterToLookFor += platform.Name; + } + if (filt.Name == filterToLookFor) { + if (config.ExcludedFromBuild) + config.ExcludedFromBuild = false; + } else { + if (!config.ExcludedFromBuild) + config.ExcludedFromBuild = true; + } + } + } + } + } + } + } + } + } + + public void ProjectItemsEvents_ItemAdded(ProjectItem projectItem) + { + var project = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte); + var qtPro = QtProject.Create(project); + if (!HelperFunctions.IsQtProject(project)) + return; + var vcFile = GetVCFileFromProject(projectItem.Name, qtPro.VCProject); + if (vcFile == null) + return; + + try { + if (HelperFunctions.IsSourceFile(vcFile.Name)) { + if (vcFile.Name.StartsWith("moc_", StringComparison.OrdinalIgnoreCase)) + return; + if (vcFile.Name.StartsWith("qrc_", StringComparison.OrdinalIgnoreCase)) { + // Do not use precompiled headers with these files + QtProject.SetPCHOption(vcFile, pchOption.pchNone); + return; + } + var pcHeaderThrough = qtPro.GetPrecompiledHeaderThrough(); + if (pcHeaderThrough != null) { + var pcHeaderCreator = pcHeaderThrough.Remove(pcHeaderThrough.LastIndexOf('.')) + ".cpp"; + if (vcFile.Name.EndsWith(pcHeaderCreator, StringComparison.OrdinalIgnoreCase) + && HelperFunctions.CxxFileContainsNotCommented(vcFile, "#include \"" + pcHeaderThrough + "\"", StringComparison.OrdinalIgnoreCase, false)) { + //File is used to create precompiled headers + QtProject.SetPCHOption(vcFile, pchOption.pchCreateUsingSpecific); + return; + } + } + if (HelperFunctions.HasQObjectDeclaration(vcFile)) { + if (!qtPro.IsQtMsBuildEnabled()) + HelperFunctions.EnsureCustomBuildToolAvailable(projectItem); + qtPro.AddMocStep(vcFile); + } + } else if (HelperFunctions.IsHeaderFile(vcFile.Name)) { + if (vcFile.Name.StartsWith("ui_", StringComparison.OrdinalIgnoreCase)) + return; + if (HelperFunctions.HasQObjectDeclaration(vcFile)) { + if (!qtPro.IsQtMsBuildEnabled()) + HelperFunctions.EnsureCustomBuildToolAvailable(projectItem); + qtPro.AddMocStep(vcFile); + } + } else if (HelperFunctions.IsUicFile(vcFile.Name)) { + if (!qtPro.IsQtMsBuildEnabled()) + HelperFunctions.EnsureCustomBuildToolAvailable(projectItem); + qtPro.AddUic4BuildStep(vcFile); + QtProjectIntellisense.Refresh(project); + } else if (HelperFunctions.IsQrcFile(vcFile.Name)) { + if (!qtPro.IsQtMsBuildEnabled()) + HelperFunctions.EnsureCustomBuildToolAvailable(projectItem); + qtPro.UpdateRccStep(vcFile, null); + } else if (HelperFunctions.IsTranslationFile(vcFile.Name)) { + } + } catch { } + } + + void ProjectItemsEvents_ItemRemoved(ProjectItem ProjectItem) + { + var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte); + if (pro == null) + return; + + var qtPro = QtProject.Create(pro); + qtPro.RemoveGeneratedFiles(ProjectItem.Name); + } + + void ProjectItemsEvents_ItemRenamed(ProjectItem ProjectItem, string OldName) + { + if (OldName == null) + return; + var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte); + if (pro == null) + return; + + var qtPro = QtProject.Create(pro); + qtPro.RemoveGeneratedFiles(OldName); + ProjectItemsEvents_ItemAdded(ProjectItem); + } + + void SolutionEvents_ProjectAdded(Project project) + { + if (HelperFunctions.IsQMakeProject(project)) { + InitializeVCProject(project); + QtProjectTracker.Add(project); + var vcpro = project.Object as VCProject; + VCFilter filter = null; + foreach (VCFilter f in vcpro.Filters as IVCCollection) { + if (f.Name == Filters.HeaderFiles().Name) { + filter = f; + break; + } + } + if (filter != null) { + foreach (VCFile file in filter.Files as IVCCollection) { + foreach (VCFileConfiguration config in file.FileConfigurations as IVCCollection) { + var tool = new QtCustomBuildTool(config); + var commandLine = tool.CommandLine; + if (!string.IsNullOrEmpty(commandLine) && commandLine.Contains("moc.exe")) { + var matches = Regex.Matches(commandLine, "[^ ^\n]+moc\\.(exe\"|exe)"); + string qtDir; + if (matches.Count != 1) { + var vm = QtVersionManager.The(); + qtDir = vm.GetInstallPath(vm.GetDefaultVersion()); + } else { + qtDir = matches[0].ToString().Trim('"'); + qtDir = qtDir.Remove(qtDir.LastIndexOf('\\')); + qtDir = qtDir.Remove(qtDir.LastIndexOf('\\')); + } + qtDir = qtDir.Replace("_(QTDIR)", "$(QTDIR)"); + HelperFunctions.SetDebuggingEnvironment(project, "PATH=" + + Path.Combine(qtDir, "bin") + ";$(PATH)", false, config.Name); + } + } + } + } + QtProjectIntellisense.Refresh(project); + } + } + + void SolutionEvents_ProjectRemoved(Project project) + { + } + + public void SolutionEvents_Opened() + { + QtProjectTracker.SolutionPath = QtVsToolsPackage.Instance.Dte.Solution.FullName; + foreach (var p in HelperFunctions.ProjectsInSolution(QtVsToolsPackage.Instance.Dte)) { + if (HelperFunctions.IsQtProject(p)) { + InitializeVCProject(p); + QtProjectTracker.Add(p); + } + } + } + + void SolutionEvents_AfterClosing() + { + QtProject.ClearInstances(); + QtProjectTracker.Reset(); + QtProjectTracker.SolutionPath = string.Empty; + } + + void InitializeVCProjects() + { + foreach (var project in HelperFunctions.ProjectsInSolution(dte)) { + if (project != null && HelperFunctions.IsQtProject(project)) + InitializeVCProject(project); + } + } + + void InitializeVCProject(Project p) + { + if (vcProjectEngineEvents != null) + return; + + var vcPrj = p.Object as VCProject; + if (vcPrj == null) + return; + + // Retrieves the VCProjectEngine from the given project and registers the handlers for VCProjectEngineEvents. + var prjEngine = vcPrj.VCProjectEngine as VCProjectEngine; + if (prjEngine != null) { + vcProjectEngineEvents = prjEngine.Events as VCProjectEngineEvents; + if (vcProjectEngineEvents != null) { + try { + vcProjectEngineEvents.ItemPropertyChange += OnVCProjectEngineItemPropertyChange; + } catch { + Messages.DisplayErrorMessage("VCProjectEngine events could not be registered."); + } + } + } + } + + private void OnVCProjectEngineItemPropertyChange(object item, object tool, int dispid) + { + //System.Diagnostics.Debug.WriteLine("OnVCProjectEngineItemPropertyChange " + dispid.ToString()); + var vcFileCfg = item as VCFileConfiguration; + if (vcFileCfg == null) { + // A global or project specific property has changed. + + var vcCfg = item as VCConfiguration; + if (vcCfg == null) + return; + var vcPrj = vcCfg.project as VCProject; + if (vcPrj == null) + return; + if (!HelperFunctions.IsQtProject(vcPrj)) + return; + // Ignore property events when using shared compiler properties + if (QtProject.GetFormatVersion(vcPrj) >= Resources.qtMinFormatVersion_ClProperties) + return; + + if (dispid == dispId_VCCLCompilerTool_UsePrecompiledHeader + || dispid == dispId_VCCLCompilerTool_PrecompiledHeaderThrough + || dispid == dispId_VCCLCompilerTool_AdditionalIncludeDirectories + || dispid == dispId_VCCLCompilerTool_PreprocessorDefinitions) { + var qtPrj = QtProject.Create(vcPrj); + if (qtPrj.IsQtMsBuildEnabled() + && dispid == dispId_VCCLCompilerTool_AdditionalIncludeDirectories) { + qtPrj.RefreshQtMocIncludePath(); + + } else if (qtPrj.IsQtMsBuildEnabled() + && dispid == dispId_VCCLCompilerTool_PreprocessorDefinitions) { + qtPrj.RefreshQtMocDefine(); + + } else { + qtPrj.RefreshMocSteps(); + } + } + } else { + // A file specific property has changed. + + var vcFile = vcFileCfg.File as VCFile; + if (vcFile == null) + return; + var vcPrj = vcFile.project as VCProject; + if (vcPrj == null) + return; + if (!HelperFunctions.IsQtProject(vcPrj)) + return; + // Ignore property events when using shared compiler properties + if (QtProject.GetFormatVersion(vcPrj) >= Resources.qtMinFormatVersion_ClProperties) + return; + + if (dispid == dispId_VCFileConfiguration_ExcludedFromBuild) { + var qtPrj = QtProject.Create(vcPrj); + qtPrj.OnExcludedFromBuildChanged(vcFile, vcFileCfg); + } else if (dispid == dispId_VCCLCompilerTool_UsePrecompiledHeader + || dispid == dispId_VCCLCompilerTool_PrecompiledHeaderThrough + || dispid == dispId_VCCLCompilerTool_AdditionalIncludeDirectories + || dispid == dispId_VCCLCompilerTool_PreprocessorDefinitions) { + var qtPrj = QtProject.Create(vcPrj); + qtPrj.RefreshMocStep(vcFile); + } + } + } + + private static VCFile GetVCFileFromProject(string absFileName, VCProject project) + { + foreach (VCFile f in (IVCCollection)project.Files) { + if (f.Name.ToLower() == absFileName.ToLower()) + return f; + } + return null; + } + + /// <summary> + /// Returns the COM DISPID of the given property. + /// </summary> + private static int GetPropertyDispId(Type type, string propertyName) + { + var pi = type.GetProperty(propertyName); + if (pi != null) { + foreach (Attribute attribute in pi.GetCustomAttributes(true)) { + var dispIdAttribute = attribute as DispIdAttribute; + if (dispIdAttribute != null) + return dispIdAttribute.Value; + } + } + return 0; + } + + } +} diff --git a/QtVsTools.Package/Package/ExtLoader.cs b/QtVsTools.Package/Package/ExtLoader.cs new file mode 100644 index 0000000..83885ac --- /dev/null +++ b/QtVsTools.Package/Package/ExtLoader.cs @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.Core; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Windows.Forms; + +namespace QtVsTools +{ + public static class ExtLoader + { + public static void ImportProFile() + { + var vm = QtVersionManager.The(); + var qtVersion = vm.GetDefaultVersion(); + var qtDir = vm.GetInstallPath(qtVersion); + if (qtDir == null) { + Messages.DisplayErrorMessage(SR.GetString("CannotFindQMake")); + return; + } + var vi = VersionInformation.Get(qtDir); + if (vi.qtMajor < 5) { + Messages.DisplayErrorMessage(SR.GetString("NoVSSupport")); + return; + } + if (QtVsToolsPackage.Instance.Dte != null) { + var proFileImporter = new ProjectImporter(QtVsToolsPackage.Instance.Dte); + proFileImporter.ImportProFile(qtVersion); + } + } + + public static void ImportPriFile(EnvDTE.Project project) + { + if (project == null) + return; + + VCProject vcproj; + if (!HelperFunctions.IsQtProject(project)) + return; + + vcproj = project.Object as VCProject; + if (vcproj == null) + return; + + // make the user able to choose .pri file + using (var fd = new OpenFileDialog()) { + fd.Multiselect = false; + fd.CheckFileExists = true; + fd.Title = SR.GetString("ExportProject_ImportPriFile"); + fd.Filter = "Project Include Files (*.pri)|*.pri"; + fd.FileName = vcproj.ProjectDirectory + vcproj.Name + ".pri"; + + if (fd.ShowDialog() != DialogResult.OK) + return; + + ImportPriFile(project, fd.FileName); + } + } + + public static void ImportPriFile(EnvDTE.Project project, string fileName) + { + if (project == null) + return; + + if (!HelperFunctions.IsQtProject(project)) + return; + + var vcproj = project.Object as VCProject; + if (vcproj == null) + return; + + var vm = QtVersionManager.The(); + var qtDir = vm.GetInstallPath(vm.GetDefaultVersion()); + if (qtDir == null) { + Messages.DisplayErrorMessage(SR.GetString("CannotFindQMake")); + return; + } + + var qmake = new QMakeWrapper { QtDir = qtDir }; + var priFileInfo = new FileInfo(fileName); + if (qmake.ReadFile(priFileInfo.FullName)) { + var priFiles = ResolveFilesFromQMake(qmake.SourceFiles, project, priFileInfo.DirectoryName); + var projFiles = HelperFunctions.GetProjectFiles(project, FilesToList.FL_CppFiles); + projFiles = ProjectExporter.ConvertFilesToFullPath(projFiles, vcproj.ProjectDirectory); + ProjectExporter.SyncIncludeFiles(vcproj, priFiles, projFiles, project.DTE, qmake.IsFlat, Filters.SourceFiles()); + + priFiles = ResolveFilesFromQMake(qmake.HeaderFiles, project, priFileInfo.DirectoryName); + projFiles = HelperFunctions.GetProjectFiles(project, FilesToList.FL_HFiles); + projFiles = ProjectExporter.ConvertFilesToFullPath(projFiles, vcproj.ProjectDirectory); + ProjectExporter.SyncIncludeFiles(vcproj, priFiles, projFiles, project.DTE, qmake.IsFlat, Filters.HeaderFiles()); + + priFiles = ResolveFilesFromQMake(qmake.FormFiles, project, priFileInfo.DirectoryName); + projFiles = HelperFunctions.GetProjectFiles(project, FilesToList.FL_UiFiles); + projFiles = ProjectExporter.ConvertFilesToFullPath(projFiles, vcproj.ProjectDirectory); + ProjectExporter.SyncIncludeFiles(vcproj, priFiles, projFiles, project.DTE, qmake.IsFlat, Filters.FormFiles()); + + priFiles = ResolveFilesFromQMake(qmake.ResourceFiles, project, priFileInfo.DirectoryName); + projFiles = HelperFunctions.GetProjectFiles(project, FilesToList.FL_Resources); + projFiles = ProjectExporter.ConvertFilesToFullPath(projFiles, vcproj.ProjectDirectory); + ProjectExporter.SyncIncludeFiles(vcproj, priFiles, projFiles, project.DTE, qmake.IsFlat, Filters.ResourceFiles()); + } else { + Messages.Print("--- (Importing .pri file) file: " + + priFileInfo + " could not be read."); + } + } + + private static List<string> ResolveFilesFromQMake(string[] files, EnvDTE.Project project, string path) + { + var lst = new List<string>(); + foreach (var file in files) { + var s = ResolveEnvironmentVariables(file, project); + if (s == null) { + Messages.Print(SR.GetString("ImportPriFileNotResolved", file)); + } else { + if (!HelperFunctions.IsAbsoluteFilePath(s)) + s = path + "\\" + s; + lst.Add(s); + } + } + return lst; + } + + private static string ResolveEnvironmentVariables(string str, EnvDTE.Project project) + { + string env = null; + string val = null; + var reg = new Regex(@"\$\(([^\s\(\)]+)\)"); + var col = reg.Matches(str); + for (var i = 0; i < col.Count; ++i) { + env = col[i].Groups[1].ToString(); + if (env == "QTDIR") { + var vm = QtVersionManager.The(); + val = vm.GetInstallPath(project); + if (val == null) + val = System.Environment.GetEnvironmentVariable(env); + } else { + val = System.Environment.GetEnvironmentVariable(env); + } + if (val == null) + return null; + str = str.Replace("$(" + env + ")", val); + } + return str; + } + + public static void ExportProFile() + { + if (QtVsToolsPackage.Instance.Dte != null) { + var proFileExporter = new ProjectExporter(QtVsToolsPackage.Instance.Dte); + proFileExporter.ExportToProFile(); + } + } + + public static void ExportPriFile() + { + var dte = QtVsToolsPackage.Instance.Dte; + if (dte != null) { + var proFileExporter = new ProjectExporter(dte); + proFileExporter.ExportToPriFile(HelperFunctions.GetSelectedQtProject + (dte)); + } + } + } +} diff --git a/QtVsTools.Package/Package/FormChangeQtVersion.Designer.cs b/QtVsTools.Package/Package/FormChangeQtVersion.Designer.cs new file mode 100644 index 0000000..4f440a1 --- /dev/null +++ b/QtVsTools.Package/Package/FormChangeQtVersion.Designer.cs @@ -0,0 +1,87 @@ +namespace QtVsTools +{ + partial class FormChangeQtVersion + { + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + btnOK = new System.Windows.Forms.Button(); + btnCancel = new System.Windows.Forms.Button(); + lbQtVersions = new System.Windows.Forms.ListBox(); + lQtVersions = new System.Windows.Forms.Label(); + SuspendLayout(); + // + // btnOK + // + btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + btnOK.Location = new System.Drawing.Point(124, 231); + btnOK.Name = "btnOK"; + btnOK.Size = new System.Drawing.Size(75, 23); + btnOK.TabIndex = 1; + btnOK.Text = "&OK"; + btnOK.UseVisualStyleBackColor = true; + // + // btnCancel + // + btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + btnCancel.Location = new System.Drawing.Point(205, 231); + btnCancel.Name = "btnCancel"; + btnCancel.Size = new System.Drawing.Size(75, 23); + btnCancel.TabIndex = 2; + btnCancel.Text = "&Cancel"; + btnCancel.UseVisualStyleBackColor = true; + // + // lbQtVersions + // + lbQtVersions.FormattingEnabled = true; + lbQtVersions.Location = new System.Drawing.Point(13, 39); + lbQtVersions.Name = "lbQtVersions"; + lbQtVersions.Size = new System.Drawing.Size(267, 173); + lbQtVersions.TabIndex = 0; + // + // lQtVersions + // + lQtVersions.AutoSize = true; + lQtVersions.Location = new System.Drawing.Point(13, 20); + lQtVersions.Name = "lQtVersions"; + lQtVersions.Size = new System.Drawing.Size(103, 13); + lQtVersions.TabIndex = 3; + lQtVersions.Text = "Installed Qt Versions"; + // + // FormChangeQtVersion + // + AcceptButton = btnOK; + AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + CancelButton = btnCancel; + ClientSize = new System.Drawing.Size(292, 266); + Controls.Add(lQtVersions); + Controls.Add(lbQtVersions); + Controls.Add(btnCancel); + Controls.Add(btnOK); + FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + KeyPreview = true; + MaximizeBox = false; + MinimizeBox = false; + Name = "FormChangeQtVersion"; + ShowInTaskbar = false; + Text = "FormChangeQtVersion"; + ResumeLayout(false); + PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.ListBox lbQtVersions; + private System.Windows.Forms.Label lQtVersions; + } +} diff --git a/QtVsTools.Package/Package/FormChangeQtVersion.cs b/QtVsTools.Package/Package/FormChangeQtVersion.cs new file mode 100644 index 0000000..043b315 --- /dev/null +++ b/QtVsTools.Package/Package/FormChangeQtVersion.cs @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using QtVsTools.Core; +using System; +using System.Windows.Forms; + +namespace QtVsTools +{ + public partial class FormChangeQtVersion : Form + { + + public FormChangeQtVersion() + { + InitializeComponent(); + btnOK.Text = SR.GetString("OK"); + btnCancel.Text = SR.GetString("Cancel"); + lQtVersions.Text = SR.GetString("InstalledQtVersions"); + lbQtVersions.DoubleClick += lbQtVersions_DoubleClick; + KeyPress += FormChangeQtVersion_KeyPress; + Shown += FormChangeQtVersion_Shown; + } + + private void FormChangeQtVersion_Shown(object sender, EventArgs e) + { + Text = SR.GetString("SolutionQtVersion"); + } + + void lbQtVersions_DoubleClick(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + Close(); + } + + void FormChangeQtVersion_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == 27) { + DialogResult = DialogResult.Cancel; + Close(); + } + } + + public void UpdateContent(ChangeFor change) + { + lbQtVersions.Items.Clear(); + var vm = QtVersionManager.The(); + foreach (var versionName in vm.GetVersions()) + lbQtVersions.Items.Add(versionName); + + lbQtVersions.Items.Add("$(DefaultQtVersion)"); + string qtVer = null; + if (change == ChangeFor.Solution) { + qtVer = vm.GetSolutionQtVersion(QtVsToolsPackage.Instance.Dte.Solution); + if (qtVer == null) + qtVer = vm.GetDefaultVersion(); + if (qtVer != null) + lbQtVersions.SelectedItem = qtVer; + Text = SR.GetString("SolutionQtVersion"); + } else { + var pro = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte); + qtVer = vm.GetProjectQtVersion(pro); + if (qtVer == null) + qtVer = vm.GetDefaultVersion(); + if (qtVer != null) + lbQtVersions.SelectedItem = qtVer; + Text = SR.GetString("ProjectQtVersion"); + } + } + + public string GetSelectedQtVersion() + { + var idx = lbQtVersions.SelectedIndex; + if (idx < 0) + return null; + return lbQtVersions.Items[idx].ToString(); + } + } +} diff --git a/QtVsTools.Package/Package/FormChangeQtVersion.resx b/QtVsTools.Package/Package/FormChangeQtVersion.resx new file mode 100644 index 0000000..ff31a6d --- /dev/null +++ b/QtVsTools.Package/Package/FormChangeQtVersion.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/QtVsTools.Package/Package/FormProjectQtSettings.Designer.cs b/QtVsTools.Package/Package/FormProjectQtSettings.Designer.cs new file mode 100644 index 0000000..9f9742f --- /dev/null +++ b/QtVsTools.Package/Package/FormProjectQtSettings.Designer.cs @@ -0,0 +1,157 @@ +using System.Windows.Forms; + +namespace QtVsTools +{ + partial class FormProjectQtSettings + { + + #region Windows Form Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.OptionsPropertyGrid = new System.Windows.Forms.PropertyGrid(); + this.panel1 = new System.Windows.Forms.Panel(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.panel1.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.SuspendLayout(); + // + // OptionsPropertyGrid + // + this.OptionsPropertyGrid.Dock = System.Windows.Forms.DockStyle.Fill; + this.OptionsPropertyGrid.HelpVisible = false; + this.OptionsPropertyGrid.Location = new System.Drawing.Point(6, 6); + this.OptionsPropertyGrid.Margin = new System.Windows.Forms.Padding(6); + this.OptionsPropertyGrid.Name = "OptionsPropertyGrid"; + this.OptionsPropertyGrid.PropertySort = System.Windows.Forms.PropertySort.Alphabetical; + this.OptionsPropertyGrid.Size = new System.Drawing.Size(898, 603); + this.OptionsPropertyGrid.TabIndex = 8; + this.OptionsPropertyGrid.ToolbarVisible = false; + // + // panel1 + // + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.Controls.Add(this.okButton); + this.panel1.Controls.Add(this.cancelButton); + this.panel1.Location = new System.Drawing.Point(608, 746); + this.panel1.Margin = new System.Windows.Forms.Padding(6); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(336, 73); + this.panel1.TabIndex = 9; + // + // okButton + // + this.okButton.Location = new System.Drawing.Point(16, 15); + this.okButton.Margin = new System.Windows.Forms.Padding(6); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(150, 44); + this.okButton.TabIndex = 0; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(176, 15); + this.cancelButton.Margin = new System.Windows.Forms.Padding(6); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(150, 44); + this.cancelButton.TabIndex = 1; + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Location = new System.Drawing.Point(24, 23); + this.tabControl1.Margin = new System.Windows.Forms.Padding(6); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(926, 662); + this.tabControl1.TabIndex = 10; + // + // tabPage1 + // + this.tabPage1.BackColor = System.Drawing.SystemColors.Control; + this.tabPage1.Controls.Add(this.OptionsPropertyGrid); + this.tabPage1.Location = new System.Drawing.Point(8, 39); + this.tabPage1.Margin = new System.Windows.Forms.Padding(6); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(6); + this.tabPage1.Size = new System.Drawing.Size(910, 615); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "General Settings"; + // + // tabPage2 + // + this.tabPage2.BackColor = System.Drawing.SystemColors.Control; + this.tabPage2.Controls.Add(this.flowLayoutPanel1); + this.tabPage2.Location = new System.Drawing.Point(8, 39); + this.tabPage2.Margin = new System.Windows.Forms.Padding(6); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(6); + this.tabPage2.Size = new System.Drawing.Size(910, 615); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Add/Remove Qt Modules"; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.AutoScroll = true; + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanel1.Location = new System.Drawing.Point(6, 6); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(898, 603); + this.flowLayoutPanel1.TabIndex = 66; + // + // FormProjectQtSettings + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(962, 842); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.panel1); + this.KeyPreview = true; + this.Margin = new System.Windows.Forms.Padding(6); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(988, 913); + this.Name = "FormProjectQtSettings"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + this.Text = "FormAddinSettings"; + this.panel1.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private PropertyGrid OptionsPropertyGrid; + private Panel panel1; + private Button okButton; + private Button cancelButton; + private TabControl tabControl1; + private TabPage tabPage1; + private TabPage tabPage2; + private FlowLayoutPanel flowLayoutPanel1; + } +} diff --git a/QtVsTools.Package/Package/FormProjectQtSettings.cs b/QtVsTools.Package/Package/FormProjectQtSettings.cs new file mode 100644 index 0000000..b5417fb --- /dev/null +++ b/QtVsTools.Package/Package/FormProjectQtSettings.cs @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using QtVsTools.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using System.IO; + +namespace QtVsTools +{ + public partial class FormProjectQtSettings : Form + { + private Project project; + private QtProject qtProject; + private ProjectQtSettings qtSettings; + + private struct ModuleMapItem + { + public CheckBox checkbox; + public int moduleId; + public bool initialValue; + + public ModuleMapItem(CheckBox cb, int mid) + { + checkbox = cb; + moduleId = mid; + initialValue = false; + } + } + + private List<ModuleMapItem> moduleMap = new List<ModuleMapItem>(); + + public FormProjectQtSettings() + { + InitializeComponent(); + okButton.Text = SR.GetString("OK"); + cancelButton.Text = SR.GetString("Cancel"); + tabControl1.TabPages[0].Text = SR.GetString("ActionDialog_Properties"); + tabControl1.TabPages[1].Text = SR.GetString("QtModules"); + + var modules = QtModules.Instance.GetAvailableModules() + .Where(x => x.Selectable) + .OrderBy(x => x.Name); + foreach (var module in modules) { + var checkBox = new CheckBox(); + checkBox.Location = new System.Drawing.Point(844, 152); + checkBox.Margin = new Padding(3, 2, 6, 2); + checkBox.Name = module.LibraryPrefix; + checkBox.Size = new System.Drawing.Size(256, 46); + checkBox.UseVisualStyleBackColor = true; + flowLayoutPanel1.Controls.Add(checkBox); + checkBox.Text = module.Name; + AddMapping(checkBox, module.Id); + } + + KeyPress += FormProjectQtSettings_KeyPress; + + Shown += FormProjectQtSettings_Shown; + } + + private void FormProjectQtSettings_Shown(object sender, EventArgs e) + { + Text = SR.GetString("ProjectQtSettingsButtonText"); + } + + private void AddMapping(CheckBox checkbox, int moduleId) + { + moduleMap.Add(new ModuleMapItem(checkbox, moduleId)); + } + + public void SetProject(Project pro) + { + project = pro; + qtProject = QtProject.Create(project); + InitModules(); + qtSettings = new ProjectQtSettings(project); + OptionsPropertyGrid.SelectedObject = qtSettings; + } + + private void FormProjectQtSettings_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == 27) { + DialogResult = DialogResult.Cancel; + Close(); + } + } + + private void okButton_Click(object sender, EventArgs e) + { + // Disable the buttons since some operations are quite expensive (e.g. changing + // the Qt version) and take some to finish. Keeping the buttons enables allows to hit + // the buttons several times resulting in successive executions of these operations. + okButton.Enabled = false; + cancelButton.Enabled = false; + + qtSettings.SaveSettings(); + saveModules(); + okButton.DialogResult = DialogResult.OK; + Close(); + } + + private void InitModules() + { + var versionManager = QtVersionManager.The(); + var qtVersion = qtProject.GetQtVersion(); + var install_path = versionManager.GetInstallPath(qtVersion) ?? string.Empty; + + for (var i = 0; i < moduleMap.Count; ++i) { + var item = moduleMap[i]; + item.initialValue = qtProject.HasModule(item.moduleId); + item.checkbox.Checked = item.initialValue; + moduleMap[i] = item; + + // Disable if module not installed + var info = QtModules.Instance.Module(item.moduleId); + var versionInfo = versionManager.GetVersionInfo(qtVersion); + if (info != null && versionInfo != null) { + var libraryPrefix = info.LibraryPrefix; + if (libraryPrefix.StartsWith("Qt", StringComparison.Ordinal)) + libraryPrefix = "Qt5" + libraryPrefix.Substring(2); + var full_path = Path.Combine(install_path, "lib", + string.Format("{0}{1}.lib", libraryPrefix, versionInfo.LibInfix())); + var fi = new System.IO.FileInfo(full_path); + item.checkbox.Enabled = fi.Exists; + if (fi.Exists == false) { + // Don't disable item if qtVersion not available + if (qtVersion != null) + item.checkbox.Checked = false; + } + } else { + item.checkbox.Checked = false; + } + } + } + + private void saveModules() + { + qtProject = QtProject.Create(project); + for (var i = 0; i < moduleMap.Count; ++i) { + var item = moduleMap[i]; + var isModuleChecked = item.checkbox.Checked; + if (isModuleChecked != item.initialValue) { + if (isModuleChecked) + qtProject.AddModule(item.moduleId); + else + qtProject.RemoveModule(item.moduleId); + } + } + } + + } +} diff --git a/QtVsTools.Package/Package/FormProjectQtSettings.resx b/QtVsTools.Package/Package/FormProjectQtSettings.resx new file mode 100644 index 0000000..c7e0d4b --- /dev/null +++ b/QtVsTools.Package/Package/FormProjectQtSettings.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/QtVsTools.Package/Package/ProjectQtSettings.cs b/QtVsTools.Package/Package/ProjectQtSettings.cs new file mode 100644 index 0000000..453ea3d --- /dev/null +++ b/QtVsTools.Package/Package/ProjectQtSettings.cs @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using QtVsTools.Core; +using System; +using System.ComponentModel; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace QtVsTools +{ + public class ProjectQtSettings + { + + public ProjectQtSettings(EnvDTE.Project proj) + { + versionManager = QtVersionManager.The(); + project = proj; + newMocDir = oldMocDir = QtVSIPSettings.GetMocDirectory(project); + newMocOptions = oldMocOptions = QtVSIPSettings.GetMocOptions(project); + newRccDir = oldRccDir = QtVSIPSettings.GetRccDirectory(project); + newUicDir = oldUicDir = QtVSIPSettings.GetUicDirectory(project); + newLUpdateOnBuild = oldLUpdateOnBuild = QtVSIPSettings.GetLUpdateOnBuild(project); + newLUpdateOptions = oldLUpdateOptions = QtVSIPSettings.GetLUpdateOptions(project); + newLReleaseOptions = oldLReleaseOptions = QtVSIPSettings.GetLReleaseOptions(project); + newQtVersion = oldQtVersion = versionManager.GetProjectQtVersion(project); + QmlDebug = oldQmlDebug = QtVSIPSettings.GetQmlDebug(project); + } + + private QtVersionManager versionManager; + private EnvDTE.Project project; + + private string oldMocDir; + private string oldMocOptions; + private string oldRccDir; + private string oldUicDir; + private string oldQtVersion; + private bool oldLUpdateOnBuild; + private string oldLUpdateOptions; + private string oldLReleaseOptions; + private bool oldQmlDebug; + + private string newMocDir; + private string newMocOptions; + private string newRccDir; + private string newUicDir; + private string newQtVersion; + private bool newLUpdateOnBuild; + private string newLUpdateOptions; + private string newLReleaseOptions; + + public void SaveSettings() + { + var updateMoc = false; + var qtPro = QtProject.Create(project); + + if (oldMocDir != newMocDir) { + QtVSIPSettings.SaveMocDirectory(project, newMocDir); + updateMoc = true; + } + if (oldMocOptions != newMocOptions) { + QtVSIPSettings.SaveMocOptions(project, newMocOptions); + updateMoc = true; + } + if (updateMoc) + qtPro.UpdateMocSteps(oldMocDir); + + if (oldUicDir != newUicDir) { + QtVSIPSettings.SaveUicDirectory(project, newUicDir); + qtPro.UpdateUicSteps(oldUicDir, true); + } + + if (oldRccDir != newRccDir) { + QtVSIPSettings.SaveRccDirectory(project, newRccDir); + qtPro.RefreshRccSteps(oldRccDir); + } + + if (oldLUpdateOnBuild != newLUpdateOnBuild) + QtVSIPSettings.SaveLUpdateOnBuild(project, newLUpdateOnBuild); + + if (oldLUpdateOptions != newLUpdateOptions) + QtVSIPSettings.SaveLUpdateOptions(project, newLUpdateOptions); + + if (oldLReleaseOptions != newLReleaseOptions) + QtVSIPSettings.SaveLReleaseOptions(project, newLReleaseOptions); + + if (oldQmlDebug != QmlDebug) + QtVSIPSettings.SaveQmlDebug(project, QmlDebug); + + if (oldQtVersion != newQtVersion) { + if (qtPro.PromptChangeQtVersion(oldQtVersion, newQtVersion)) { + var newProjectCreated = false; + var versionChanged = qtPro.ChangeQtVersion( + oldQtVersion, newQtVersion, ref newProjectCreated); + if (versionChanged && newProjectCreated) + project = qtPro.Project; + } + } + } + + public string MocDirectory + { + get + { + return newMocDir; + } + set + { + var tmp = HelperFunctions.NormalizeRelativeFilePath(value); + if (tmp.ToLower() == oldMocDir.ToLower()) + return; + + string badMacros = IncompatibleMacros(tmp); + if (!string.IsNullOrEmpty(badMacros)) + Messages.DisplayErrorMessage(SR.GetString("IncompatibleMacros", badMacros)); + else + newMocDir = tmp; + } + } + + public string MocOptions + { + get + { + return newMocOptions; + } + + set + { + newMocOptions = value; + } + } + + public string UicDirectory + { + get + { + return newUicDir; + } + set + { + var tmp = HelperFunctions.NormalizeRelativeFilePath(value); + if (tmp.ToLower() == oldUicDir.ToLower()) + return; + + string badMacros = IncompatibleMacros(tmp); + if (!string.IsNullOrEmpty(badMacros)) + Messages.DisplayErrorMessage(SR.GetString("IncompatibleMacros", badMacros)); + else + newUicDir = tmp; + } + } + + public string RccDirectory + { + get + { + return newRccDir; + } + set + { + var tmp = HelperFunctions.NormalizeRelativeFilePath(value); + if (tmp.ToLower() == oldRccDir.ToLower()) + return; + + string badMacros = IncompatibleMacros(tmp); + if (!string.IsNullOrEmpty(badMacros)) + Messages.DisplayErrorMessage(SR.GetString("IncompatibleMacros", badMacros)); + else + newRccDir = tmp; + } + } + + public bool lupdateOnBuild + { + get + { + return newLUpdateOnBuild; + } + + set + { + newLUpdateOnBuild = value; + } + } + + public string LUpdateOptions + { + get + { + return newLUpdateOptions; + } + + set + { + newLUpdateOptions = value; + } + } + + public string LReleaseOptions + { + get + { + return newLReleaseOptions; + } + + set + { + newLReleaseOptions = value; + } + } + + [DisplayName("QML Debug")] + [TypeConverter(typeof(QmlDebugConverter))] + public bool QmlDebug { get; set; } + + internal class QmlDebugConverter : BooleanConverter + { + public override object ConvertTo( + ITypeDescriptorContext context, + CultureInfo culture, + object value, + Type destinationType) + { + return (bool)value ? "Enabled" : "Disabled"; + } + + public override object ConvertFrom( + ITypeDescriptorContext context, + CultureInfo culture, + object value) + { + return (string)value == "Enabled"; + } + } + + [TypeConverter(typeof(VersionConverter))] + public string Version + { + get + { + return newQtVersion; + } + set + { + newQtVersion = value; + } + } + + internal class VersionConverter : StringConverter + { + private QtVersionManager versionManager; + + public VersionConverter() + { + versionManager = QtVersionManager.The(); + } + + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) + { + return true; + } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + { + var versions = versionManager.GetVersions(); + Array.Resize(ref versions, versions.Length + 1); + versions[versions.Length - 1] = "$(DefaultQtVersion)"; + return new StandardValuesCollection(versions); + } + + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) + { + return true; + } + } + + public static string IncompatibleMacros(string stringToExpand) + { + string incompatibleMacros = ""; + foreach (Match metaNameMatch in Regex.Matches(stringToExpand, @"\%\(([^\)]+)\)")) { + string metaName = metaNameMatch.Groups[1].Value; + if (!incompatibleMacros.Contains(string.Format("%({0})", metaName))) { + switch (metaName) { + case "RecursiveDir": + case "ModifiedTime": + case "CreatedTime": + case "AccessedTime": + if (!string.IsNullOrEmpty(incompatibleMacros)) + incompatibleMacros += ", "; + incompatibleMacros += string.Format("%({0})", metaName); + break; + } + } + } + return incompatibleMacros; + } + } +} diff --git a/QtVsTools.Package/Package/QMakeWrapper.cs b/QtVsTools.Package/Package/QMakeWrapper.cs new file mode 100644 index 0000000..11b0614 --- /dev/null +++ b/QtVsTools.Package/Package/QMakeWrapper.cs @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Diagnostics; +using System.Xml; + +namespace QtVsTools +{ + public class QMakeWrapper + { + public string QtDir { get; set; } + + public bool IsFlat { get; private set; } + public bool IsValid { get; private set; } + + public string[] SourceFiles { get; private set; } + public string[] HeaderFiles { get; private set; } + public string[] ResourceFiles { get; private set; } + public string[] FormFiles { get; private set; } + + public bool ReadFile(string filePath) + { + string output; + try { + var exeFilePath = QtVsToolsPackage.Instance.QMakeFileReaderPath; + if (!System.IO.File.Exists(exeFilePath)) + return false; + + using (var process = new Process()) { + process.StartInfo.CreateNoWindow = true; + process.StartInfo.FileName = exeFilePath; + process.StartInfo.Arguments = ShellQuote(QtDir) + ' ' + ShellQuote(filePath); + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + if (!process.Start()) + return false; + output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + } + + System.IO.StringReader stringReader = null; + try { + stringReader = new System.IO.StringReader(output); + using (var reader = new XmlTextReader(stringReader)) { + stringReader = null; + reader.ReadToFollowing("content"); + IsFlat = reader.GetAttribute("flat") == "true"; + IsValid = reader.GetAttribute("valid") == "true"; + SourceFiles = ReadFileElements(reader, "SOURCES"); + HeaderFiles = ReadFileElements(reader, "HEADERS"); + ResourceFiles = ReadFileElements(reader, "RESOURCES"); + FormFiles = ReadFileElements(reader, "FORMS"); + } + } finally { + if (stringReader != null) + stringReader.Dispose(); + } + } catch { + return false; + } + return true; + } + + private static string ShellQuote(string filePath) + { + if (filePath.Contains(" ")) + return '"' + filePath + '"'; + return filePath; + } + + private static string[] ReadFileElements(XmlReader reader, string tag) + { + var fileNames = new List<string>(); + if (reader.ReadToFollowing(tag)) { + if (reader.ReadToDescendant("file")) { + do { + var fname = reader.ReadString(); + fileNames.Add(fname); + } while (reader.ReadToNextSibling("file")); + } + } + return fileNames.ToArray(); + } + } +} diff --git a/QtVsTools.Package/Package/QtHelp.cs b/QtVsTools.Package/Package/QtHelp.cs new file mode 100644 index 0000000..e36cb83 --- /dev/null +++ b/QtVsTools.Package/Package/QtHelp.cs @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.Settings; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Shell.Settings; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Data.Common; +using System.Data.SQLite; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Task = System.Threading.Tasks.Task; + +namespace QtVsTools +{ + public class QtHelp + { + public enum SourcePreference { Online, Offline } + + public static QtHelp Instance + { + get; + private set; + } + + public static void Initialize(Package package) + { + Instance = new QtHelp(package); + } + + const int F1QtHelpId = 0x0502; + + readonly Package package; + public static readonly Guid MainMenuGuid = new Guid("58f83fff-d39d-4c66-810b-2702e1f04e73"); + + QtHelp(Package pkg) + { + if (pkg == null) + throw new ArgumentNullException("package"); + package = pkg; + + var commandService = VsServiceProvider + .GetService<IMenuCommandService, OleMenuCommandService>(); + if (commandService == null) + return; + + var menuCommandID = new CommandID(MainMenuGuid, F1QtHelpId); + commandService.AddCommand(new MenuCommand(F1QtHelpEventHandler, menuCommandID)); + } + + IServiceProvider ServiceProvider + { + get { return package; } + } + + static bool IsSuperfluousCharacter(string text) + { + switch (text) { + case " ": + case ";": + case ".": + case "<": + case ">": + case "{": + case "}": + case "(": + case ")": + case ":": + case ",": + case "/": + case "\\": + case "^": + case "%": + case "+": + case "-": + case "*": + case "\t": + case "&": + case "\"": + case "!": + case "[": + case "]": + case "|": + case "'": + case "~": + case "#": + case "=": + return true; // nothing we are interested in + } + return false; + } + + static string GetString(DbDataReader reader, int index) + { + if (!reader.IsDBNull(index)) + return reader.GetString(index); + return string.Empty; + } + + void F1QtHelpEventHandler(object sender, EventArgs args) + { + QueryEditorContextHelp(true); + } + + public static bool QueryEditorContextHelp(bool defaultTryOnline = false) + { + try { + var dte = VsServiceProvider.GetService<SDTE, DTE>(); + var objTextDocument = dte?.ActiveDocument?.Object() as TextDocument; + if (objTextDocument == null) + return false; + + var keyword = string.Empty; + var selection = objTextDocument.Selection; + if (selection.IsEmpty) { // no selection inside the document + var line = selection.ActivePoint.Line; // current line + var offset = selection.ActivePoint.LineCharOffset; // current char offset + + selection.CharLeft(true); // try the character before the cursor + if (!selection.IsEmpty) { + keyword = selection.Text; // something in front of the cursor + selection.CharRight(true); // reset to origin + if (!IsSuperfluousCharacter(keyword)) { + // move the selection to the start of the word + selection.WordLeft(true); + selection.MoveToPoint(selection.TopPoint); + } + } + selection.WordRight(true); // select the word + keyword = selection.Text; // get the selected text + selection.MoveToLineAndOffset(line, offset); // reset + } else { + keyword = selection.Text; + } + + keyword = keyword.Trim(); + if (keyword.Length <= 1 || IsSuperfluousCharacter(keyword)) + return false; // suppress single character, operators etc... + + var qtVersion = "$(DefaultQtVersion)"; + var project = HelperFunctions.GetSelectedQtProject(dte); + if (project == null) { + project = HelperFunctions.GetSelectedProject(dte); + if (project != null && HelperFunctions.IsQMakeProject(project)) { + var qmakeQtDir = HelperFunctions.GetQtDirFromQMakeProject(project); + qtVersion = QtVersionManager.The().GetQtVersionFromInstallDir(qmakeQtDir); + } + } else { + qtVersion = QtVersionManager.The().GetProjectQtVersion(project); + } + + var info = QtVersionManager.The().GetVersionInfo(qtVersion); + var docPath = info?.QtInstallDocs; + if (string.IsNullOrEmpty(docPath) || !Directory.Exists(docPath)) + return false; + + var qchFiles = Directory.GetFiles(docPath, "*?.qch"); + if (qchFiles.Length == 0) + return false; + + var offline = QtVsToolsPackage.Instance.Options.HelpPreference == SourcePreference.Offline; + + var linksForKeyword = string.Format("SELECT d.Title, f.Name, e.Name, " + + "d.Name, a.Anchor FROM IndexTable a, FileNameTable d, FolderTable e, " + + "NamespaceTable f WHERE a.FileId=d.FileId AND d.FolderId=e.Id AND " + + "a.NamespaceId=f.Id AND a.Name='{0}'", keyword); + + var links = new Dictionary<string, string>(); + var builder = new SQLiteConnectionStringBuilder + { + ReadOnly = true + }; + foreach (var file in qchFiles) { + builder.DataSource = file; + using (var connection = new SQLiteConnection(builder.ToString())) { + connection.Open(); + using (var command = new SQLiteCommand(linksForKeyword, connection)) { + using (var reader = + Task.Run(async () => await command.ExecuteReaderAsync()).Result) { + while (reader.Read()) { + var title = GetString(reader, 0); + if (string.IsNullOrWhiteSpace(title)) + title = keyword + ':' + GetString(reader, 3); + var path = string.Empty; + if (offline) { + path = "file:///" + Path.Combine(docPath, + GetString(reader, 2), GetString(reader, 3)); + } else { + path = "https://" + Path.Combine("doc.qt.io", + $"qt-{info.qtMajor}", GetString(reader, 3)); + } + if (!string.IsNullOrWhiteSpace(GetString(reader, 4))) + path += "#" + GetString(reader, 4); + links.Add(title, path); + } + } + } + } + } + + var uri = string.Empty; + switch (links.Values.Count) { + case 0: + if (!offline && defaultTryOnline) { + uri = new UriBuilder($"https://doc.qt.io/qt-{info.qtMajor}/search-results.html") + { + Query = "q=" + keyword + }.ToString(); + } else { + return false; + } + break; + case 1: + uri = links.First().Value; + break; + default: + var dialog = new QtHelpLinkChooser + { + Links = links, + Keyword = keyword, + ShowInTaskbar = false + }; + if (!dialog.ShowModal().GetValueOrDefault()) + return false; + uri = dialog.Link + .Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + break; + } + + if (string.IsNullOrEmpty(uri)) { // offline mode without a single search hit + VsShellUtilities.ShowMessageBox(Instance.ServiceProvider, + "Your search - " + keyword + " - did not match any documents.", + string.Empty, OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); + } else { + var helpUri = new Uri(uri.Replace('\\', '/')); + if (helpUri.IsFile && !File.Exists(helpUri.LocalPath)) { + VsShellUtilities.ShowMessageBox(Instance.ServiceProvider, + "Your search - " + keyword + " - did match a document, but it could " + + "not be found on disk. To use the online help, select: " + + "Help | Set Qt Help Preference | Use Online Documentation", + string.Empty, OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); + } else { + VsShellUtilities.OpenSystemBrowser(HelperFunctions.ChangePathFormat(uri)); + } + } + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + return true; + } + } +} diff --git a/QtVsTools.Package/Package/QtHelpLinkChooser.xaml b/QtVsTools.Package/Package/QtHelpLinkChooser.xaml new file mode 100644 index 0000000..d901292 --- /dev/null +++ b/QtVsTools.Package/Package/QtHelpLinkChooser.xaml @@ -0,0 +1,107 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<local:VsToolsDialogWindow x:Class="QtVsTools.QtHelpLinkChooser" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:local="clr-namespace:QtVsTools" + Width="400" + Height="250" + MinWidth="400" + MinHeight="250" + mc:Ignorable="d" + Title="Choose Topic" + ShowInTaskbar="False" + HasHelpButton="False" + HasMinimizeButton="False" + ResizeMode="CanResizeWithGrip" + WindowStartupLocation="CenterOwner"> + <local:VsToolsDialogWindow.Resources> + <BooleanToVisibilityConverter x:Key="b2v" /> + <Style x:Key="ListBoxDoubleClickStyle" + TargetType="ListBoxItem"> + <EventSetter Event="MouseDoubleClick" + Handler="OnListBoxItem_DoubleClick" /> + </Style> + </local:VsToolsDialogWindow.Resources> + <Grid Margin="10" + FocusManager.FocusedElement="{Binding ElementName=searchBox}"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock Grid.Row="0" + Margin="0,0,0,5"> + <Run Text="Choose a topic for " /> + <Run FontWeight="Bold" + Text="{Binding Path=Keyword}" /> + <Run Text=":" /> + </TextBlock> + <Grid Grid.Row="1" + MinHeight="22" + Background="White"> + <TextBlock Text=" Filter..." + Foreground="LightSteelBlue" + VerticalAlignment="Center" + Visibility="{Binding ElementName=searchBox, + Path=Text.IsEmpty, Converter={StaticResource b2v}}" /> + <TextBox Name="searchBox" + Background="Transparent" + TextChanged="OnSearchBox_TextChanged" + VerticalContentAlignment="Center" /> + </Grid> + <ListBox Grid.Row="2" + Margin="0,10,0,0" + Name="linkListBox" + DisplayMemberPath="Key" + SelectedValuePath="Value" + ItemsSource="{Binding Path=Links}" + SelectedValue="{Binding Path=Link}" + SelectionChanged="OnLinkListBox_SelectionChanged" + ItemContainerStyle="{StaticResource ListBoxDoubleClickStyle}" /> + <StackPanel Grid.Row="3" + Orientation="Horizontal" + HorizontalAlignment="Right"> + <Button MinWidth="75" + Content="Show" + IsDefault="True" + Click="OnShowButton_Click" + Margin="0,10,10,0" /> + <Button MinWidth="75" + IsCancel="True" + Content="Cancel" + Margin="0,10,0,0" /> + </StackPanel> + </Grid> +</local:VsToolsDialogWindow> diff --git a/QtVsTools.Package/Package/QtHelpLinkChooser.xaml.cs b/QtVsTools.Package/Package/QtHelpLinkChooser.xaml.cs new file mode 100644 index 0000000..290f966 --- /dev/null +++ b/QtVsTools.Package/Package/QtHelpLinkChooser.xaml.cs @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; + +namespace QtVsTools +{ + partial class QtHelpLinkChooser : VsToolsDialogWindow + { + public QtHelpLinkChooser() + { + InitializeComponent(); + + DataContext = this; + Loaded += OnLoaded; + } + + public string Link { get; set; } + public string Keyword { private get; set; } + public Dictionary<string, string> Links { private get; set; } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + var view = CollectionViewSource.GetDefaultView(linkListBox.ItemsSource); + view.Filter = obj => + { + if (string.IsNullOrEmpty(searchBox.Text)) + return true; + + var item = (KeyValuePair<string, string>)obj; + return item.Key.IndexOf(searchBox.Text, StringComparison.OrdinalIgnoreCase) >= 0; + }; + linkListBox.SelectedIndex = 0; + } + + private void OnSearchBox_TextChanged(object sender, TextChangedEventArgs e) + { + CollectionViewSource.GetDefaultView(linkListBox.ItemsSource).Refresh(); + if (linkListBox.Items.Count == 1 || linkListBox.SelectedItem == null) + linkListBox.SelectedIndex = 0; + } + + private void OnListBoxItem_DoubleClick(object sender, MouseButtonEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed) + OnShowButton_Click(sender, null); + } + + private void OnShowButton_Click(object sender, RoutedEventArgs e) + { + DialogResult = true; + Close(); + } + + private void OnLinkListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (e.RemovedItems != null && (e.AddedItems == null || e.AddedItems.Count == 0)) { + if (linkListBox.Items.Count != 0) + linkListBox.SelectedIndex = 0; + } + } + } +} diff --git a/QtVsTools.Package/Package/QtItemContextMenu.cs b/QtVsTools.Package/Package/QtItemContextMenu.cs new file mode 100644 index 0000000..19bc389 --- /dev/null +++ b/QtVsTools.Package/Package/QtItemContextMenu.cs @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System; +using System.ComponentModel.Design; + +namespace QtVsTools +{ + /// <summary> + /// Command handler + /// </summary> + internal sealed class QtItemContextMenu + { + /// <summary> + /// Command menu group (command set GUID). + /// </summary> + public static readonly Guid ItemContextMenuGuid = new Guid("9f67a0bd-ee0a-47e3-b656-5efb12e3c770"); + + /// <summary> + /// Gets the instance of the command. + /// </summary> + public static QtItemContextMenu Instance + { + get; + private set; + } + + /// <summary> + /// Initializes the singleton instance of the command. + /// </summary> + /// <param name="package">Owner package, not null.</param> + public static void Initialize(Package package) + { + Instance = new QtItemContextMenu(package); + } + + /// <summary> + /// VS Package that provides this command, not null. + /// </summary> + private readonly Package m_package; + + /// <summary> + /// Command ID. + /// </summary> + private const int lUpdateOnItemId = 0x0125; + private const int lReleaseOnItemId = 0x0126; + + /// <summary> + /// Initializes a new instance of the <see cref="QtMainMenu"/> class. + /// Adds our command handlers for menu (commands must exist in the command table file) + /// </summary> + /// <param name="package">Owner package, not null.</param> + private QtItemContextMenu(Package package) + { + if (package == null) + throw new ArgumentNullException("package"); + + m_package = package; + + var commandService = VsServiceProvider + .GetService<IMenuCommandService, OleMenuCommandService>(); + if (commandService == null) + return; + + var command = new OleMenuCommand(execHandler, + new CommandID(ItemContextMenuGuid, lUpdateOnItemId)); + command.BeforeQueryStatus += beforeQueryStatus; + commandService.AddCommand(command); + + command = new OleMenuCommand(execHandler, + new CommandID(ItemContextMenuGuid, lReleaseOnItemId)); + command.BeforeQueryStatus += beforeQueryStatus; + commandService.AddCommand(command); + } + + private void execHandler(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + + switch (command.CommandID.ID) { + case lUpdateOnItemId: + Translation.RunlUpdate(HelperFunctions.GetSelectedFiles(QtVsToolsPackage.Instance.Dte)); + break; + case lReleaseOnItemId: + Translation.RunlRelease(HelperFunctions.GetSelectedFiles(QtVsToolsPackage.Instance.Dte)); + break; + } + } + + private void beforeQueryStatus(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + + command.Enabled = false; + command.Visible = false; + + var prj = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte); + if (!HelperFunctions.IsQtProject(prj) || QtVsToolsPackage.Instance.Dte.SelectedItems.Count <= 0) + return; + + foreach (SelectedItem si in QtVsToolsPackage.Instance.Dte.SelectedItems) { + if (!HelperFunctions.IsTranslationFile(si.Name)) + return; // Don't display commands if one of the selected files is not a .ts file. + } + + command.Enabled = true; + command.Visible = true; + } + } +} diff --git a/QtVsTools.Package/Package/QtMainMenu.cs b/QtVsTools.Package/Package/QtMainMenu.cs new file mode 100644 index 0000000..2c8e90d --- /dev/null +++ b/QtVsTools.Package/Package/QtMainMenu.cs @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System; +using System.ComponentModel.Design; +using System.Windows.Forms; + +namespace QtVsTools +{ + /// <summary> + /// Command handler + /// </summary> + internal sealed class QtMainMenu + { + /// <summary> + /// Command menu group (command set GUID). + /// </summary> + public static readonly Guid MainMenuGuid = new Guid("58f83fff-d39d-4c66-810b-2702e1f04e73"); + + /// <summary> + /// Gets the instance of the command. + /// </summary> + public static QtMainMenu Instance + { + get; + private set; + } + + /// <summary> + /// Initializes the singleton instance of the command. + /// </summary> + /// <param name="package">Owner package, not null.</param> + public static void Initialize(Package package) + { + Instance = new QtMainMenu(package); + } + + /// <summary> + /// Command ID. + /// </summary> + private enum CommandId + { + QtVersionId = 0x0500, + ViewQtHelpId = 0x0501, + LaunchDesignerId = 0x0100, + LaunchLinguistId = 0x0101, + OpenProFileId = 0x0102, + ImportPriFileId = 0x0103, + ExportPriFileId = 0x0104, + ExportProFileId = 0x0105, + CreateNewTsFileId = 0x0107, + ConvertToQtMsBuild = 0x0130, + ConvertToQtId = 0x0124, + ConvertToQmakeId = 0x0108, + QtProjectSettingsId = 0x0109, + ChangeProjectQtVersionId = 0x0126, + QtOptionsId = 0x0110, + QtVersionsId = 0x0111, + } + + /// <summary> + /// VS Package that provides this command, not null. + /// </summary> + private readonly Package m_package; + + /// <summary> + /// Initializes a new instance of the <see cref="QtMainMenu"/> class. + /// Adds our command handlers for menu (commands must exist in the command table file) + /// </summary> + /// <param name="package">Owner package, not null.</param> + private QtMainMenu(Package package) + { + if (package == null) + throw new ArgumentNullException("package"); + + m_package = package; + + var commandService = VsServiceProvider + .GetService<IMenuCommandService, OleMenuCommandService>(); + if (commandService == null) + return; + + foreach (var id in Enum.GetValues(typeof(CommandId))) { + var command = new OleMenuCommand(execHandler, + new CommandID(MainMenuGuid, (int)id)); + command.BeforeQueryStatus += beforeQueryStatus; + commandService.AddCommand(command); + } + } + + private void execHandler(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + + switch ((CommandId)command.CommandID.ID) { + case CommandId.ViewQtHelpId: + VsShellUtilities.OpenSystemBrowser("https://www.qt.io/developers"); + break; + case CommandId.LaunchDesignerId: + QtVsToolsPackage.Instance.QtDesigner.Start(hideWindow: false); + break; + case CommandId.LaunchLinguistId: + QtVsToolsPackage.Instance.QtLinguist.Start(hideWindow: false); + break; + case CommandId.OpenProFileId: + ExtLoader.ImportProFile(); + break; + case CommandId.ImportPriFileId: + ExtLoader.ImportPriFile(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte)); + break; + case CommandId.ExportPriFileId: + ExtLoader.ExportPriFile(); + break; + case CommandId.ExportProFileId: + ExtLoader.ExportProFile(); + break; + case CommandId.CreateNewTsFileId: + Translation.CreateNewTranslationFile(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage + .Instance.Dte)); + break; + case CommandId.ConvertToQtId: + case CommandId.ConvertToQmakeId: { + var caption = SR.GetString("ConvertTitle"); + var text = SR.GetString("ConvertConfirmation"); + if (MessageBox.Show(text, caption, MessageBoxButtons.YesNo) == DialogResult.Yes) { + HelperFunctions.ToggleProjectKind(HelperFunctions.GetSelectedProject(QtVsToolsPackage + .Instance.Dte)); + } + } + break; + case CommandId.ConvertToQtMsBuild: { + QtMsBuildConverter.SolutionToQtMsBuild(); + } + break; + case CommandId.QtProjectSettingsId: { + var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte); + int projectVersion = QtProject.GetFormatVersion(pro); + if (projectVersion >= Resources.qtMinFormatVersion_Settings) { + QtVsToolsPackage.Instance.Dte.ExecuteCommand("Project.Properties"); + } else if (pro != null) { + using (var formProjectQtSettings = new FormProjectQtSettings()) { + formProjectQtSettings.SetProject(pro); + formProjectQtSettings.StartPosition = FormStartPosition.CenterParent; + var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte); + formProjectQtSettings.ShowDialog(ww); + } + } else { + MessageBox.Show(SR.GetString("NoProjectOpened")); + } + } + break; + case CommandId.ChangeProjectQtVersionId: { + var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte); + if (HelperFunctions.IsQMakeProject(pro)) { + using (var formChangeQtVersion = new FormChangeQtVersion()) { + formChangeQtVersion.UpdateContent(ChangeFor.Project); + var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte); + if (formChangeQtVersion.ShowDialog(ww) == DialogResult.OK) { + var qtVersion = formChangeQtVersion.GetSelectedQtVersion(); + HelperFunctions.SetDebuggingEnvironment(pro, "PATH=" + QtVersionManager + .The().GetInstallPath(qtVersion) + @"\bin;$(PATH)", true); + } + } + } + } + break; + case CommandId.QtOptionsId: + QtVsToolsPackage.Instance.ShowOptionPage(typeof(Options.QtOptionsPage)); + break; + case CommandId.QtVersionsId: + QtVsToolsPackage.Instance.ShowOptionPage(typeof(Options.QtVersionsPage)); + break; + } + } + + private void beforeQueryStatus(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + + switch ((CommandId)command.CommandID.ID) { + case CommandId.ViewQtHelpId: + command.Visible = command.Enabled = true; + break; + case CommandId.QtVersionId: + command.Text = "Qt Visual Studio Tools version " + Version.USER_VERSION; + command.Visible = true; + command.Enabled = false; + break; + case CommandId.LaunchDesignerId: + case CommandId.LaunchLinguistId: + case CommandId.OpenProFileId: + case CommandId.QtOptionsId: + case CommandId.QtVersionsId: + command.Visible = true; + command.Enabled = true; + break; + case CommandId.ImportPriFileId: + case CommandId.ExportPriFileId: + case CommandId.ExportProFileId: + case CommandId.CreateNewTsFileId: { + command.Visible = true; + command.Enabled = HelperFunctions.IsQtProject(HelperFunctions + .GetSelectedProject(QtVsToolsPackage.Instance.Dte)); + } + break; + // TODO: Fix these functionality and re-enable the menu items + case CommandId.ConvertToQtId: + case CommandId.ConvertToQmakeId: { + command.Visible = false; + } + break; + //case CommandId.ConvertToQmakeId: + case CommandId.QtProjectSettingsId: { + var status = vsCommandStatus.vsCommandStatusSupported; + var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte); + if (project != null) { + if (HelperFunctions.IsQtProject(project)) + status |= vsCommandStatus.vsCommandStatusEnabled; + else if (HelperFunctions.IsQMakeProject(project)) + status |= vsCommandStatus.vsCommandStatusInvisible; + } + command.Enabled = ((status & vsCommandStatus.vsCommandStatusEnabled) != 0); + command.Visible = ((status & vsCommandStatus.vsCommandStatusInvisible) == 0); + } + break; + //case CommandId.ConvertToQtId: + case CommandId.ChangeProjectQtVersionId: { + var status = vsCommandStatus.vsCommandStatusSupported; + var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte); + if ((project == null) || HelperFunctions.IsQtProject(project)) + status |= vsCommandStatus.vsCommandStatusInvisible; + else if (HelperFunctions.IsQMakeProject(project)) + status |= vsCommandStatus.vsCommandStatusEnabled; + else + status |= vsCommandStatus.vsCommandStatusInvisible; + command.Enabled = ((status & vsCommandStatus.vsCommandStatusEnabled) != 0); + command.Visible = ((status & vsCommandStatus.vsCommandStatusInvisible) == 0); + } + break; + case CommandId.ConvertToQtMsBuild: { + command.Visible = true; + command.Enabled = (QtVsToolsPackage.Instance.Dte.Solution != null + && QtVsToolsPackage.Instance.Dte.Solution.Projects != null + && QtVsToolsPackage.Instance.Dte.Solution.Projects.Count > 0); + } + break; + } + } + } +} diff --git a/QtVsTools.Package/Package/QtMsBuildConverter.cs b/QtVsTools.Package/Package/QtMsBuildConverter.cs new file mode 100644 index 0000000..eac783f --- /dev/null +++ b/QtVsTools.Package/Package/QtMsBuildConverter.cs @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.Core; +using QtVsTools.VisualStudio; + +namespace QtVsTools +{ + static class QtMsBuildConverter + { + public static bool SolutionToQtMsBuild() + { + var solution = QtVsToolsPackage.Instance.Dte.Solution; + if (solution == null) + return ErrorMessage(string.Format(SR.GetString("ErrorConvertingProject"), "")); + + List<EnvDTE.Project> projects = new List<EnvDTE.Project>(); + var allProjects = solution.Projects; + if (allProjects.Count == 0) + return WarningMessage(SR.GetString("NoProjectsToConvert")); + + foreach (EnvDTE.Project project in allProjects) { + if ((HelperFunctions.IsQtProject(project) + || HelperFunctions.IsQMakeProject(project)) + && !QtProject.IsQtMsBuildEnabled(project)) { + projects.Add(project); + } + } + if (projects.Count == 0) + return WarningMessage(SR.GetString("NoProjectsToConvert")); + + if (MessageBox.Show( + SR.GetString("ConvertAllConfirmation"), + SR.GetString("ConvertTitle"), + MessageBoxButtons.YesNo) != DialogResult.Yes) + return WarningMessage(SR.GetString("CancelConvertingProject")); + + if (projects.Where(project => project.IsDirty).Any()) { + if (MessageBox.Show( + SR.GetString("ConvertSaveConfirmation"), + SR.GetString("ConvertTitle"), + MessageBoxButtons.YesNo) != DialogResult.Yes) + return WarningMessage(SR.GetString("CancelConvertingProject")); + } + + var projectPaths = projects.Select(x => x.FullName).ToList(); + + string solutionPath = solution.FileName; + solution.Close(true); + + var waitDialog = WaitDialog.StartWithProgress( + SR.GetString("Resources_QtVsTools"), SR.GetString("ConvertWait"), + projectPaths.Count, isCancelable: true); + + int projCount = 0; + bool canceled = false; + foreach (var projectPath in projectPaths) { + if (waitDialog != null) { + waitDialog.Update(string.Format(SR.GetString("ConvertProgress"), + projCount + 1, projectPaths.Count, + Path.GetFileNameWithoutExtension(projectPath)), + projectPaths.Count, projCount); + if (waitDialog.Canceled) + break; + } + if (!ConvertProject(projectPath)) { + if (waitDialog != null) + waitDialog.Stop(); + QtVsToolsPackage.Instance.Dte.Solution.Open(solutionPath); + return ErrorMessage(string.Format(SR.GetString("ErrorConvertingProject"), + Path.GetFileName(projectPath))); + } + ++projCount; + } + + if (waitDialog != null) + waitDialog.Stop(); + + QtVsToolsPackage.Instance.Dte.Solution.Open(solutionPath); + if (canceled && projCount < projectPaths.Count) { + MessageBox.Show(string.Format(SR.GetString("ConvertCanceled"), + projectPaths.Count - projCount), SR.GetString("Resources_QtVsTools"), + MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + return true; + } + + static bool ConvertProject(string pathToProject) + { + var xmlProject = MsBuildProject.Load(pathToProject); + bool ok = (xmlProject != null); + if (ok) + ok = xmlProject.AddQtMsBuildReferences(); + if (ok) + ok = xmlProject.ConvertCustomBuildToQtMsBuild(); + if (ok) + ok = xmlProject.EnableMultiProcessorCompilation(); + if (ok) + ok = xmlProject.UpdateProjectFormatVersion(); + if (ok) + ok = xmlProject.Save(); + + // Initialize Qt variables + if (ok) + xmlProject.BuildTarget("QtVarsDesignTime"); + return ok; + } + + public static bool ProjectToQtMsBuild(EnvDTE.Project project, bool askConfirmation = true) + { + if (project == null) + return ErrorMessage(string.Format(SR.GetString("ErrorConvertingProject"), "")); + var pathToProject = project.FullName; + + if (askConfirmation + && MessageBox.Show( + SR.GetString("ConvertConfirmation"), + SR.GetString("ConvertTitle"), + MessageBoxButtons.YesNo) != DialogResult.Yes) + return WarningMessage(SR.GetString("CancelConvertingProject")); + if (project.IsDirty) { + if (askConfirmation + && MessageBox.Show(SR.GetString("ConvertSaveConfirmation"), project.Name, + MessageBoxButtons.YesNo) != DialogResult.Yes) + return WarningMessage(SR.GetString("CancelConvertingProject")); + try { + project.Save(); + } catch (Exception e) { + return ErrorMessage(string.Format(SR.GetString("ErrorConvertingProject"), + string.Format("{0}\r\n{1}", project.Name, e.Message))); + } + } + + var vcProject = project.Object as VCProject; + if (vcProject == null) + return ErrorMessage( + string.Format(SR.GetString("ErrorConvertingProject"), project.Name)); + var solution = VsServiceProvider.GetService<SVsSolution, IVsSolution4>(); + if (solution == null) + return ErrorMessage( + string.Format(SR.GetString("ErrorConvertingProject"), project.Name)); + var projectGuid = new Guid(vcProject.ProjectGUID); + var projectName = project.Name; + try { + if (solution.UnloadProject( + ref projectGuid, + (uint)_VSProjectUnloadStatus.UNLOADSTATUS_LoadPendingIfNeeded) + != VSConstants.S_OK) + return ErrorMessage( + string.Format(SR.GetString("ErrorConvertingProject"), projectName)); + } catch (Exception e) { + return ErrorMessage(string.Format(SR.GetString("ErrorConvertingProject"), + string.Format("{0}\r\n{1}", projectName, e.Message))); + } + + bool ok = ConvertProject(pathToProject); + try { + solution.ReloadProject(ref projectGuid); + } catch (Exception e) { + return ErrorMessage( + string.Format(SR.GetString("ErrorConvertingProject"), + string.Format("{0}\r\n{1}", projectName, e.Message))); + } + if (!ok) { + return ErrorMessage( + string.Format(SR.GetString("ErrorConvertingProject"), projectName)); + } + return true; + } + + static bool ErrorMessage(string msg) + { + Messages.DisplayErrorMessage(msg); + return false; + } + + static bool WarningMessage(string msg) + { + Messages.DisplayWarningMessage(msg); + return true; + } + } +} diff --git a/QtVsTools.Package/Package/QtProjectContextMenu.cs b/QtVsTools.Package/Package/QtProjectContextMenu.cs new file mode 100644 index 0000000..2706ef5 --- /dev/null +++ b/QtVsTools.Package/Package/QtProjectContextMenu.cs @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System; +using System.ComponentModel.Design; +using System.Windows.Forms; + +namespace QtVsTools +{ + using QtMsBuild; + + /// <summary> + /// Command handler + /// </summary> + internal sealed class QtProjectContextMenu + { + /// <summary> + /// Command menu group (command set GUID). + /// </summary> + public static readonly Guid ProjectContextMenuGuid = new Guid("5732faa9-6074-4e07-b035-2816e809f50e"); + + /// <summary> + /// Gets the instance of the command. + /// </summary> + public static QtProjectContextMenu Instance + { + get; + private set; + } + + /// <summary> + /// Initializes the singleton instance of the command. + /// </summary> + /// <param name="package">Owner package, not null.</param> + public static void Initialize(Package package) + { + Instance = new QtProjectContextMenu(package); + } + + /// <summary> + /// Command ID. + /// </summary> + private enum CommandId + { + ImportPriFileProjectId = 0x0114, + ExportPriFileProjectId = 0x0115, + ExportProFileProjectId = 0x0116, + CreateNewTsFileProjectId = 0x0117, + lUpdateOnProjectId = 0x0118, + lReleaseOnProjectId = 0x0119, + ProjectConvertToQtMsBuild = 0x0130, + ProjectRefreshIntelliSense = 0x0131, + ConvertToQtProjectId = 0x0120, + ConvertToQmakeProjectId = 0x0121, + QtProjectSettingsProjectId = 0x0122, + ChangeProjectQtVersionProjectId = 0x0123, + ProjectAddNewQtClassProjectId = 0x200 + } + + /// <summary> + /// VS Package that provides this command, not null. + /// </summary> + private readonly Package m_package; + + /// <summary> + /// Initializes a new instance of the <see cref="QtMainMenu"/> class. + /// Adds our command handlers for menu (commands must exist in the command table file) + /// </summary> + /// <param name="package">Owner package, not null.</param> + private QtProjectContextMenu(Package package) + { + if (package == null) + throw new ArgumentNullException("package"); + + m_package = package; + + var commandService = VsServiceProvider + .GetService<IMenuCommandService, OleMenuCommandService>(); + if (commandService == null) + return; + + foreach (var id in Enum.GetValues(typeof(CommandId))) { + var command = new OleMenuCommand(execHandler, + new CommandID(ProjectContextMenuGuid, (int)id)); + command.BeforeQueryStatus += beforeQueryStatus; + commandService.AddCommand(command); + } + } + + private void execHandler(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + + switch ((CommandId)command.CommandID.ID) { + case CommandId.ImportPriFileProjectId: + ExtLoader.ImportPriFile(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte)); + break; + case CommandId.ExportPriFileProjectId: + ExtLoader.ExportPriFile(); + break; + case CommandId.ExportProFileProjectId: + ExtLoader.ExportProFile(); + break; + case CommandId.CreateNewTsFileProjectId: + Translation.CreateNewTranslationFile(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage + .Instance.Dte)); + break; + case CommandId.lUpdateOnProjectId: + Translation.RunlUpdate(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte)); + break; + case CommandId.lReleaseOnProjectId: + Translation.RunlRelease(HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte)); + break; + case CommandId.ConvertToQtProjectId: + case CommandId.ConvertToQmakeProjectId: { + var caption = SR.GetString("ConvertTitle"); + var text = SR.GetString("ConvertConfirmation"); + if (MessageBox.Show(text, caption, MessageBoxButtons.YesNo) == DialogResult.Yes) { + HelperFunctions.ToggleProjectKind(HelperFunctions.GetSelectedProject(QtVsToolsPackage + .Instance.Dte)); + } + } + break; + case CommandId.QtProjectSettingsProjectId: { + var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte); + int projectVersion = QtProject.GetFormatVersion(pro); + if (projectVersion >= Resources.qtMinFormatVersion_Settings) { + QtVsToolsPackage.Instance.Dte.ExecuteCommand("Project.Properties"); + } else if (pro != null) { + using (var formProjectQtSettings = new FormProjectQtSettings()) { + formProjectQtSettings.SetProject(pro); + formProjectQtSettings.StartPosition = FormStartPosition.CenterParent; + var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte); + formProjectQtSettings.ShowDialog(ww); + } + } else { + MessageBox.Show(SR.GetString("NoProjectOpened")); + } + } + break; + case CommandId.ChangeProjectQtVersionProjectId: { + var pro = HelperFunctions.GetSelectedQtProject(QtVsToolsPackage.Instance.Dte); + if (HelperFunctions.IsQMakeProject(pro)) { + using (var formChangeQtVersion = new FormChangeQtVersion()) { + formChangeQtVersion.UpdateContent(ChangeFor.Project); + var ww = new MainWinWrapper(QtVsToolsPackage.Instance.Dte); + if (formChangeQtVersion.ShowDialog(ww) == DialogResult.OK) { + var qtVersion = formChangeQtVersion.GetSelectedQtVersion(); + HelperFunctions.SetDebuggingEnvironment(pro, "PATH=" + QtVersionManager + .The().GetInstallPath(qtVersion) + @"\bin;$(PATH)", true); + } + } + } + } + break; + case CommandId.ProjectConvertToQtMsBuild: { + QtMsBuildConverter.ProjectToQtMsBuild( + HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte)); + } + break; + case CommandId.ProjectRefreshIntelliSense: { + var selectedProject = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte); + var tracker = QtProjectTracker.Get(selectedProject); + QtProjectIntellisense.Refresh(tracker.Project); + } + break; + case CommandId.ProjectAddNewQtClassProjectId: { + try { + var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte); + if (!HelperFunctions.IsQtProject(project)) + return; + + var vcProject = project.Object as VCProject; + if (vcProject == null) + return; + + var loop = true; + do { + var classWizard = new Wizards.ClassWizard.AddClassWizard(); + loop = classWizard.Run(QtVsToolsPackage.Instance.Dte, vcProject.Name, + vcProject.ProjectDirectory) == Wizards.WizardResult.Exception; + } while (loop); + } catch { + // Deliberately ignore any kind of exception but close the dialog. + } + } + break; + } + } + + private void beforeQueryStatus(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + + var project = HelperFunctions.GetSelectedProject(QtVsToolsPackage.Instance.Dte); + var isQtProject = HelperFunctions.IsQtProject(project); + var isQMakeProject = HelperFunctions.IsQMakeProject(project); + var isQtMsBuildEnabled = QtProject.IsQtMsBuildEnabled(project); + + if (!isQtProject && !isQMakeProject) { + command.Enabled = command.Visible = false; + return; + } + + switch ((CommandId)command.CommandID.ID) { + // TODO: Fix these functionality and re-enable the menu items + case CommandId.ConvertToQtProjectId: + case CommandId.ConvertToQmakeProjectId: { + command.Visible = false; + } + break; + case CommandId.ImportPriFileProjectId: + case CommandId.ExportPriFileProjectId: + case CommandId.ExportProFileProjectId: + case CommandId.CreateNewTsFileProjectId: + case CommandId.lUpdateOnProjectId: + case CommandId.lReleaseOnProjectId: + command.Visible = true; + command.Enabled = HelperFunctions.IsQtProject(HelperFunctions + .GetSelectedProject(QtVsToolsPackage.Instance.Dte)); + break; + //case CommandId.ConvertToQmakeProjectId: + case CommandId.QtProjectSettingsProjectId: + case CommandId.ProjectAddNewQtClassProjectId: { + var status = vsCommandStatus.vsCommandStatusSupported; + if (project != null) { + if (isQtProject) + status |= vsCommandStatus.vsCommandStatusEnabled; + else if (isQMakeProject) + status |= vsCommandStatus.vsCommandStatusInvisible; + } + command.Enabled = ((status & vsCommandStatus.vsCommandStatusEnabled) != 0); + command.Visible = ((status & vsCommandStatus.vsCommandStatusInvisible) == 0); + } + break; + //case CommandId.ConvertToQtProjectId: + case CommandId.ChangeProjectQtVersionProjectId: { + var status = vsCommandStatus.vsCommandStatusSupported; + if ((project == null) || isQtProject) + status |= vsCommandStatus.vsCommandStatusInvisible; + else if (isQMakeProject) + status |= vsCommandStatus.vsCommandStatusEnabled; + else + status |= vsCommandStatus.vsCommandStatusInvisible; + command.Enabled = ((status & vsCommandStatus.vsCommandStatusEnabled) != 0); + command.Visible = ((status & vsCommandStatus.vsCommandStatusInvisible) == 0); + } + break; + case CommandId.ProjectConvertToQtMsBuild: { + if (project == null || (!isQtProject && !isQMakeProject)) { + command.Visible = false; + command.Enabled = false; + } else if (isQtMsBuildEnabled) { + command.Visible = true; + command.Enabled = false; + } else { + command.Visible = true; + command.Enabled = true; + } + } + break; + case CommandId.ProjectRefreshIntelliSense: { + command.Visible = command.Enabled = isQtMsBuildEnabled; + } + break; + } + + if (project != null && isQtProject) { + int projectVersion = QtProject.GetFormatVersion(project); + switch ((CommandId)command.CommandID.ID) { + case CommandId.ChangeProjectQtVersionProjectId: + if (projectVersion >= Resources.qtMinFormatVersion_Settings) + command.Visible = command.Enabled = false; + break; + case CommandId.ProjectConvertToQtMsBuild: + if (projectVersion >= Resources.qtProjectFormatVersion) { + command.Visible = command.Enabled = false; + } else { + command.Visible = command.Enabled = true; + if (isQtMsBuildEnabled) + command.Text = "Upgrade to latest Qt project format version"; + } + break; + } + } + } + } +} diff --git a/QtVsTools.Package/Package/QtSolutionContextMenu.cs b/QtVsTools.Package/Package/QtSolutionContextMenu.cs new file mode 100644 index 0000000..d680fe7 --- /dev/null +++ b/QtVsTools.Package/Package/QtSolutionContextMenu.cs @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE80; +using Microsoft.VisualStudio.Shell; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System; +using System.ComponentModel.Design; +using System.Windows.Forms; + +namespace QtVsTools +{ + using QtMsBuild; + + /// <summary> + /// Command handler + /// </summary> + internal sealed class QtSolutionContextMenu + { + /// <summary> + /// Command menu group (command set GUID). + /// </summary> + public static readonly Guid SolutionContextMenuGuid = new Guid("6dcda34f-4d22-4d6a-a176-5507069c5a3e"); + + /// <summary> + /// Gets the instance of the command. + /// </summary> + public static QtSolutionContextMenu Instance + { + get; + private set; + } + + /// <summary> + /// Initializes the singleton instance of the command. + /// </summary> + /// <param name="package">Owner package, not null.</param> + public static void Initialize(Package package) + { + Instance = new QtSolutionContextMenu(package); + } + + /// <summary> + /// VS Package that provides this command, not null. + /// </summary> + private readonly Package m_package; + + /// <summary> + /// Command ID. + /// </summary> + private enum CommandId + { + lUpdateOnSolutionId = 0x0111, + lReleaseOnSolutionId = 0x0112, + SolutionConvertToQtMsBuild = 0x0130, + SolutionEnableProjectTracking = 0x1130, + ChangeSolutionQtVersionId = 0x0113 + } + + /// <summary> + /// Initializes a new instance of the <see cref="QtMainMenu"/> class. + /// Adds our command handlers for menu (commands must exist in the command table file) + /// </summary> + /// <param name="package">Owner package, not null.</param> + private QtSolutionContextMenu(Package package) + { + if (package == null) + throw new ArgumentNullException("package"); + + m_package = package; + + var commandService = VsServiceProvider + .GetService<IMenuCommandService, OleMenuCommandService>(); + if (commandService == null) + return; + + foreach (var id in Enum.GetValues(typeof(CommandId))) { + var command = new OleMenuCommand(execHandler, + new CommandID(SolutionContextMenuGuid, (int)id)); + command.BeforeQueryStatus += beforeQueryStatus; + commandService.AddCommand(command); + } + } + + private void beforeQueryStatus(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + command.Enabled = command.Visible = true; + } + + private void execHandler(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + if (command == null) + return; + + var dte = QtVsToolsPackage.Instance.Dte; + switch (command.CommandID.ID) { + case (int)CommandId.lUpdateOnSolutionId: + Translation.RunlUpdate(QtVsToolsPackage.Instance.Dte.Solution); + break; + case (int)CommandId.lReleaseOnSolutionId: + Translation.RunlRelease(QtVsToolsPackage.Instance.Dte.Solution); + break; + case (int)CommandId.ChangeSolutionQtVersionId: + string newQtVersion = null; + using (var formChangeQtVersion = new FormChangeQtVersion()) { + formChangeQtVersion.UpdateContent(ChangeFor.Solution); + if (formChangeQtVersion.ShowDialog() != DialogResult.OK) + return; + newQtVersion = formChangeQtVersion.GetSelectedQtVersion(); + } + if (newQtVersion == null) + return; + + string currentPlatform = null; + try { + var config2 = QtVsToolsPackage.Instance.Dte.Solution.SolutionBuild + .ActiveConfiguration as SolutionConfiguration2; + currentPlatform = config2.PlatformName; + } catch { } + if (string.IsNullOrEmpty(currentPlatform)) + return; + + foreach (var project in HelperFunctions.ProjectsInSolution(dte)) { + if (HelperFunctions.IsQtProject(project)) { + var OldQtVersion = QtVersionManager.The().GetProjectQtVersion(project, + currentPlatform); + if (OldQtVersion == null) + OldQtVersion = QtVersionManager.The().GetDefaultVersion(); + + var created = false; + var qtProject = QtProject.Create(project); + if (qtProject.PromptChangeQtVersion(OldQtVersion, newQtVersion)) + qtProject.ChangeQtVersion(OldQtVersion, newQtVersion, ref created); + } + } + QtVersionManager.The().SaveSolutionQtVersion(dte.Solution, newQtVersion); + break; + case (int)CommandId.SolutionConvertToQtMsBuild: { + QtMsBuildConverter.SolutionToQtMsBuild(); + } + break; + case (int)CommandId.SolutionEnableProjectTracking: { + foreach (var project in HelperFunctions.ProjectsInSolution(dte)) { + if (HelperFunctions.IsQtProject(project)) + QtProjectTracker.Get(project); + } + } + break; + } + } + } +} diff --git a/QtVsTools.Package/Package/SR.cs b/QtVsTools.Package/Package/SR.cs new file mode 100644 index 0000000..8dd6c57 --- /dev/null +++ b/QtVsTools.Package/Package/SR.cs @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Globalization; +using System.Resources; + +namespace QtVsTools +{ + internal sealed class SR + { + static SR loader; + readonly ResourceManager resources; + static readonly Object obj = new Object(); + + internal const string OK = "OK"; + internal const string Cancel = "Cancel"; + + internal static CultureInfo appCultureInfo; + internal static CultureInfo defaultCultureInfo; + + internal SR(int localeId) + { + defaultCultureInfo = CultureInfo.GetCultureInfo("en"); + appCultureInfo = CultureInfo.GetCultureInfo(localeId); + if (appCultureInfo.Name.StartsWith("en", StringComparison.Ordinal)) + appCultureInfo = null; + resources = new ResourceManager("QtVsTools.Package.Resources", GetType().Assembly); + } + + private static SR GetLoader(int localeId) + { + if (loader == null) { + lock (obj) { + if (loader == null) + loader = new SR(localeId); + } + } + return loader; + } + + private static CultureInfo Culture + { + get { return appCultureInfo; } + } + + public static string GetString(string name, params object[] args) + { + var res = GetString(name); + if (!string.IsNullOrEmpty(res) && args != null && args.Length > 0) + return string.Format(res, args); + return res; + } + + public static string GetString(string name) + { + return GetString(name, QtVsToolsPackage.Instance); + } + + public static string GetString(string name, QtVsToolsPackage vsixInstance) + { + var sys = GetLoader(vsixInstance.Dte.LocaleID); + if (sys == null) + return null; + + string result; + try { + result = sys.resources.GetString(name, Culture); + } catch (Exception) { + result = sys.resources.GetString(name, defaultCultureInfo); + } + + return result; + } + } +} diff --git a/QtVsTools.Package/Package/Translation.cs b/QtVsTools.Package/Package/Translation.cs new file mode 100644 index 0000000..3c7e9f2 --- /dev/null +++ b/QtVsTools.Package/Package/Translation.cs @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace QtVsTools +{ + using static Core.HelperFunctions; + using QtMsBuild; + + /// <summary> + /// Run Qt translation tools by invoking the corresponding Qt/MSBuild targets + /// </summary> + public static class Translation + { + public static void RunlRelease(VCFile vcFile) + { + var vcProj = vcFile.project as VCProject; + var project = vcProj?.Object as EnvDTE.Project; + RunTranslationTarget(BuildAction.Release, + project, new[] { vcFile.RelativePath }); + } + + public static void RunlRelease(VCFile[] vcFiles) + { + var vcProj = vcFiles.FirstOrDefault()?.project as VCProject; + var project = vcProj?.Object as EnvDTE.Project; + RunTranslationTarget(BuildAction.Release, + project, vcFiles.Select(vcFile => vcFile?.RelativePath)); + } + + public static void RunlRelease(EnvDTE.Project project) + { + RunTranslationTarget(BuildAction.Release, project); + } + + public static void RunlRelease(EnvDTE.Solution solution) + { + if (solution == null) + return; + + foreach (var project in HelperFunctions.ProjectsInSolution(solution.DTE)) + RunlRelease(project); + } + + public static void RunlUpdate(VCFile vcFile) + { + var vcProj = vcFile.project as VCProject; + var project = vcProj?.Object as EnvDTE.Project; + RunTranslationTarget(BuildAction.Update, + project, new[] { vcFile.RelativePath }); + } + + public static void RunlUpdate(VCFile[] vcFiles) + { + var vcProj = vcFiles.FirstOrDefault()?.project as VCProject; + var project = vcProj?.Object as EnvDTE.Project; + RunTranslationTarget(BuildAction.Update, + project, vcFiles.Select(vcFile => vcFile?.RelativePath)); + } + + public static void RunlUpdate(EnvDTE.Project project) + { + RunTranslationTarget(BuildAction.Update, project); + } + + enum BuildAction { Update, Release } + + static void RunTranslationTarget( + BuildAction buildAction, + EnvDTE.Project project, + IEnumerable<string> selectedFiles = null) + { + using (WaitDialog.Start( + "Qt Visual Studio Tools", "Running translation tool...")) { + + var qtPro = QtProject.Create(project); + if (project == null || qtPro == null) { + Messages.Print( + "translation: Error accessing project interface"); + return; + } + + if (qtPro.FormatVersion < Resources.qtMinFormatVersion_Settings) { + Messages.Print("translation: Legacy project format"); + try { + Legacy_RunTranslation(buildAction, qtPro, selectedFiles); + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + return; + } + + var activeConfig = project.ConfigurationManager?.ActiveConfiguration; + if (activeConfig == null) { + Messages.Print( + "translation: Error accessing build interface"); + return; + } + var activeConfigId = string.Format("{0}|{1}", + activeConfig.ConfigurationName, activeConfig.PlatformName); + + var target = "QtTranslation"; + var properties = new Dictionary<string, string>(); + switch (buildAction) { + case BuildAction.Update: + properties["QtTranslationForceUpdate"] = "true"; + break; + case BuildAction.Release: + properties["QtTranslationForceRelease"] = "true"; + break; + } + if (selectedFiles != null) + properties["SelectedFiles"] = string.Join(";", selectedFiles); + + QtProjectBuild.StartBuild(project, activeConfigId, properties, new[] { target }); + } + } + + public static void RunlUpdate(EnvDTE.Solution solution) + { + if (solution == null) + return; + + foreach (var project in HelperFunctions.ProjectsInSolution(solution.DTE)) + RunlUpdate(project); + } + + static void Legacy_RunTranslation( + BuildAction buildAction, + QtProject qtProject, + IEnumerable<string> tsFiles) + { + if (tsFiles == null) { + tsFiles = (qtProject.VCProject + .GetFilesEndingWith(".ts") as IVCCollection) + .Cast<VCFile>() + .Select(vcFile => vcFile.RelativePath); + if (tsFiles == null) { + Messages.Print("translation: no translation files found"); + return; + } + } + string tempFile = null; + foreach (var file in tsFiles.Where(file => file != null)) + Legacy_RunTranslation(buildAction, qtProject, file, ref tempFile); + } + + static void Legacy_RunTranslation( + BuildAction buildAction, + QtProject qtProject, + string tsFile, + ref string tempFile) + { + var qtVersion = qtProject.GetQtVersion(); + var qtInstallPath = QtVersionManager.The().GetInstallPath(qtVersion); + if (string.IsNullOrEmpty(qtInstallPath)) { + Messages.Print("translation: Error accessing Qt installation"); + return; + } + + var procInfo = new ProcessStartInfo + { + WorkingDirectory = qtProject.ProjectDir, + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + Arguments = "" + }; + switch (buildAction) { + case BuildAction.Update: + Messages.Print("\r\n--- (lupdate) file: " + tsFile); + procInfo.FileName = Path.Combine(qtInstallPath, "bin", "lupdate.exe"); + var options = QtVSIPSettings.GetLUpdateOptions(); + if (!string.IsNullOrEmpty(options)) + procInfo.Arguments += options + " "; + if (tempFile == null) { + var inputFiles = GetProjectFiles(qtProject.Project, FilesToList.FL_HFiles) + .Union(GetProjectFiles(qtProject.Project, FilesToList.FL_CppFiles)) + .Union(GetProjectFiles(qtProject.Project, FilesToList.FL_UiFiles)) + .Union(GetProjectFiles(qtProject.Project, FilesToList.FL_QmlFiles)); + tempFile = Path.GetTempFileName(); + File.WriteAllLines(tempFile, inputFiles); + } + procInfo.Arguments += string.Format("\"@{0}\" -ts \"{1}\"", tempFile, tsFile); + break; + case BuildAction.Release: + Messages.Print("\r\n--- (lrelease) file: " + tsFile); + procInfo.FileName = Path.Combine(qtInstallPath, "bin", "lrelease.exe"); + options = QtVSIPSettings.GetLReleaseOptions(); + if (!string.IsNullOrEmpty(options)) + procInfo.Arguments += options + " "; + procInfo.Arguments += string.Format("\"{0}\"", tsFile); + break; + } + using (var proc = Process.Start(procInfo)) { + proc.OutputDataReceived += (object sender, DataReceivedEventArgs e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Messages.Print(e.Data); + }; + proc.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Messages.Print(e.Data); + }; + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + proc.WaitForExit(); + switch (proc.ExitCode) { + case 0: + Messages.Print("translation: ok"); + break; + default: + Messages.Print(string.Format("translation: ERROR {0}", proc.ExitCode)); + break; + } + } + } + + public static void CreateNewTranslationFile(EnvDTE.Project project) + { + if (project == null) + return; + + using (var transDlg = new AddTranslationDialog(project)) { + if (transDlg.ShowDialog() == DialogResult.OK) { + try { + var qtPro = QtProject.Create(project); + var file = qtPro.AddFileInFilter(Filters.TranslationFiles(), + transDlg.TranslationFile, true); + RunlUpdate(file); + } catch (QtVSException e) { + Messages.DisplayErrorMessage(e.Message); + } catch (System.Exception ex) { + Messages.DisplayErrorMessage(ex.Message); + } + } + } + } + } +} diff --git a/QtVsTools.Package/Package/TranslationItem.cs b/QtVsTools.Package/Package/TranslationItem.cs new file mode 100644 index 0000000..ad4c09a --- /dev/null +++ b/QtVsTools.Package/Package/TranslationItem.cs @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Globalization; + +namespace QtVsTools +{ + public class TranslationItem : CultureInfo + { + public TranslationItem(int culture) + : base(culture) + { + } + + public override string ToString() + { + if (NativeName != DisplayName) + return DisplayName; + + var culture = GetCultureInfo(QtVsToolsPackage.Instance.Dte.LocaleID); + if (culture.TwoLetterISOLanguageName == TwoLetterISOLanguageName) + return DisplayName; + + return EnglishName; + } + + public static TranslationItem SystemLanguage() + { + return new TranslationItem(CurrentCulture.LCID); + } + + public static TranslationItem[] GetTranslationItems() + { + var cultures = GetCultures(CultureTypes.SpecificCultures + & ~CultureTypes.UserCustomCulture & ~CultureTypes.ReplacementCultures); + var transItems = new List<TranslationItem>(); + for (var i = 0; i < cultures.Length; i++) { + // Locales without a LCID are given LCID 0x1000 (http://msdn.microsoft.com/en-us/library/dn363603.aspx) + // Trying to create a TranslationItem for these will cause an exception to be thrown. + var lcid = cultures[i].LCID; + if (lcid != 0x1000) + transItems.Add(new TranslationItem(lcid)); + } + return transItems.ToArray(); + } + } +} diff --git a/QtVsTools.Package/Package/Version.cs b/QtVsTools.Package/Package/Version.cs new file mode 100644 index 0000000..6b9ce5f --- /dev/null +++ b/QtVsTools.Package/Package/Version.cs @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +**************************************************************************** +*/ + +namespace QtVsTools +{ + internal static class Version + { + public const string PRODUCT_VERSION = "<#=QT_VS_TOOLS_VERSION_MANIFEST#>"; + public const string USER_VERSION = "<#=QT_VS_TOOLS_VERSION_USER#>"; + } +} diff --git a/QtVsTools.Package/Properties/AssemblyInfo.cs b/QtVsTools.Package/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..83dacf6 --- /dev/null +++ b/QtVsTools.Package/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +***************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Qt VS Tools")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2C1F6CA5-00EC-4DDE-B746-F309E35F356E")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs b/QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs new file mode 100644 index 0000000..d8698b4 --- /dev/null +++ b/QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs @@ -0,0 +1,651 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// This file contains the definition of the abstract class QmlAsyncClassifier which is the base +/// class for asynchronous implementations of text classifiers, e.g. for syntax highlighting and +/// syntax error annotations + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace QtVsTools.Qml.Classification +{ + using HelperTypes; + + /// <summary> + /// A SharedTagList is a list of tracking tags (instances of TrackingTag), sorted by starting + /// location. It works as write-once-read-many data storage. Write access must be requested and + /// will only be granted to the first object/thread, which will be responsible for filling in + /// the data. Once writing is complete, concurrent read-only access will then be allowed. + /// </summary> + class SharedTagList : Concurrent + { + SortedList<int, TrackingTag> data = new SortedList<int, TrackingTag>(); + object owner; + + public bool Ready { get; private set; } + + public enum AccessType { ReadOnly, ReadWrite } + public AccessType RequestWriteAccess(object client) + { + EnterCriticalSection(); + if (owner == null) { + owner = client; + return AccessType.ReadWrite; + } else { + LeaveCriticalSection(); + return AccessType.ReadOnly; + } + } + + public void WriteComplete(object client) + { + if (owner != client) + return; + Ready = true; + try { + LeaveCriticalSection(); + } catch { } + } + + public void AddRange(object client, IEnumerable<TrackingTag> tags) + { + if (owner != client || Ready) + return; + foreach (var tag in tags) + Add(client, tag); + } + + public void Add(object client, TrackingTag tag) + { + if (owner != client || Ready) + return; + data[tag.Start] = tag; + } + + class TrackingTagComparer : Comparer<TrackingTag> + { + ITextSnapshot snapshot; + public TrackingTagComparer(ITextSnapshot snapshot) + { + this.snapshot = snapshot; + } + public override int Compare(TrackingTag t1, TrackingTag t2) + { + int t1Version = t1.Snapshot.Version.VersionNumber; + int t2Version = t2.Snapshot.Version.VersionNumber; + if (t1Version == t2Version && t2Version == snapshot.Version.VersionNumber) + return Comparer<int>.Default.Compare(t1.Start, t2.Start); + + var t1Mapped = t1.MapToSnapshot(snapshot); + var t2Mapped = t2.MapToSnapshot(snapshot); + return Comparer<int>.Default.Compare(t1Mapped.Span.Start, t2Mapped.Span.Start); + } + } + + /// <summary> + /// Perform a binary search to find the tag whose start precedes a given location relative + /// to a text snapshot. If the tags in the list are relative to another version of the + /// text, their location will be mapped to the given snapshot. + /// </summary> + /// <param name="snapshot">Text snapshot</param> + /// <param name="location">Location in the given snapshot</param> + /// <returns> + /// Index of the tag in the list; -1 indicates error + /// </returns> + public int FindTagAtLocation(ITextSnapshot snapshot, int location) + { + if (!Ready) + return -1; + + var firstTag = data.Values.FirstOrDefault(); + if (firstTag == null) + return -1; + + bool sameVersion = + (firstTag.Snapshot.Version.VersionNumber == snapshot.Version.VersionNumber); + + int? idx = null; + if (sameVersion) { + idx = data.Keys.BinarySearch(location); + } else { + if (location >= snapshot.Length) + return -1; + var locationTag = new TrackingTag(snapshot, location, 1); + var comparer = new TrackingTagComparer(snapshot); + idx = data.Values.BinarySearch(locationTag, comparer); + } + if (idx == null) + return -1; + + if (idx < 0) { + // location was not found; idx has the bitwise complement of the smallest element + // that is after location, or the bitwise complement of the list count in case all + // elements are before location. + + if (~idx == 0) // first tag starts after location + return -1; + + // Because we are looking for the nearest tag that starts before location, we will + // return the element that precedes the one found + idx = ~idx - 1; + } + + return idx.Value; + } + + public IList<TrackingTag> Values + { + get + { + if (!Ready) + return new List<TrackingTag>(); + return data.Values; + } + } + } + + /// <summary> + /// Base class for QML classifier classes implementing the ITagger interface. This interface + /// is used in the Visual Studio text editor extensibility for e.g. syntax highlighting. The + /// processing of the QML source code is done asynchronously in a background thread in order + /// to prevent the UI thread from blocking. + /// + /// The result of the processing is a list of tracking tags that is stored in a SharedTagList + /// and is made available to any instances of QmlAsyncClassifier working on the same source + /// code. This prevents the processing being invoked more than once for any given version of + /// that source code. + /// + /// Derived classes are required to implement the processing of the source code as well as the + /// conversion from TrackingTag to the type expected by the Visual Studio text editor. + /// </summary> + /// <typeparam name="T"> + /// Type of classification tag expected by the Visual Studio text editor extensibility + /// </typeparam> + abstract class QmlAsyncClassifier<T> : ITagger<T> where T : ITag + { + protected enum ClassificationRefresh + { + FullText, + TagsOnly + } + + /// <summary> + /// Process QML source code. Implementations will override this method with the specific + /// processing required to convert the parser results into a list of tracking tags + /// </summary> + /// <param name="snapshot">The current version of the source code</param> + /// <param name="parseResult">The result of parsing the source code</param> + /// <param name="tagList">Shared list of tracking tags</param> + /// <param name="writeAccess"> + /// If true, the instance is required to populate the list of tags; + /// otherwise, the instance has read-only access and cannot modify the list. + /// </param> + /// <returns> + /// Hint on how to notify Visual Studio concerning the tags in the list + /// FullText: refresh the entire contents of the text editor + /// TagsOnly: refresh only the spans pointed to by the tags + /// </returns> + protected abstract ClassificationRefresh ProcessText( + ITextSnapshot snapshot, + Parser parseResult, + SharedTagList tagList, + bool writeAccess); + + /// <summary> + /// Conversion from TrackingTag to the type T of classification tag expected by the + /// Visual Studio text editor extensibility. + /// </summary> + /// <param name="tag">TrackingTag to convert</param> + /// <returns>Instance of T corresponding to the given TrackingTag</returns> + protected abstract T GetClassification(TrackingTag tag); + + protected ITextView TextView { get; private set; } + protected ITextBuffer Buffer { get; private set; } + + readonly object criticalSection = new object(); + + string classificationType; + ParserKey currentParserKey; + TagListKey currentTagListKey; + SharedTagList currentTagList; + Dispatcher dispatcher; + DispatcherTimer timer; + bool flag = false; + + protected QmlAsyncClassifier( + string classificationType, + ITextView textView, + ITextBuffer buffer) + { + TextView = textView; + textView.Closed += TextView_Closed; + Buffer = buffer; + buffer.Changed += Buffer_Changed; + + dispatcher = Dispatcher.CurrentDispatcher; + timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle, dispatcher) + { + Interval = TimeSpan.FromMilliseconds(250) + }; + timer.Tick += Timer_Tick; + + currentParserKey = null; + currentTagListKey = null; + currentTagList = null; + this.classificationType = classificationType; + + AsyncParse(buffer.CurrentSnapshot); + } + + private void TextView_Closed(object sender, EventArgs e) + { + if (currentParserKey != null) { + ParserStore.Instance.Release(this, currentParserKey); + currentParserKey = null; + } + if (currentTagListKey != null) { + TagListStore.Instance.Release(this, currentTagListKey); + currentTagListKey = null; + } + currentTagList = null; + } + + private void Buffer_Changed(object sender, TextContentChangedEventArgs e) + { + timer.Stop(); + AsyncParse(e.After); + } + + private void Timer_Tick(object sender, EventArgs e) + { + timer.Stop(); + AsyncParse(Buffer.CurrentSnapshot); + } + + private async void AsyncParse(ITextSnapshot snapshot) + { + lock (criticalSection) { + if (flag) + return; + flag = true; + } + + var newParserKey = new ParserKey(snapshot); + var newTagListKey = new TagListKey(classificationType, snapshot); + if (newParserKey == currentParserKey || newTagListKey == currentTagListKey) + return; + + ParserKey oldParserKey = null; + TagListKey oldTagListKey = null; + + await Task.Run(() => + { + var parser = ParserStore.Instance.Get(this, newParserKey); + + var tagList = TagListStore.Instance.Get(this, newTagListKey); + var refresh = ClassificationRefresh.FullText; + try { + var accessType = tagList.RequestWriteAccess(this); + refresh = ProcessText(snapshot, parser, tagList, + accessType == SharedTagList.AccessType.ReadWrite); + } finally { + tagList.WriteComplete(this); + } + + oldParserKey = currentParserKey; + currentParserKey = newParserKey; + oldTagListKey = currentTagListKey; + currentTagListKey = newTagListKey; + currentTagList = tagList; + + RefreshClassification(snapshot, refresh, tagList); + + var currentVersion = Buffer.CurrentSnapshot.Version; + if (snapshot.Version.VersionNumber == currentVersion.VersionNumber) + timer.Stop(); + else + timer.Start(); + }); + + lock (criticalSection) { + flag = false; + } + + await Task.Run(() => + { + if (oldParserKey != null) + ParserStore.Instance.Release(this, oldParserKey); + if (oldTagListKey != null) + TagListStore.Instance.Release(this, oldTagListKey); + }); + } + + private void RefreshClassification( + ITextSnapshot snapshot, + ClassificationRefresh refresh, + SharedTagList tagList) + { + var tagsChangedHandler = TagsChanged; + if (refresh == ClassificationRefresh.FullText) { + var span = new SnapshotSpan(Buffer.CurrentSnapshot, + 0, Buffer.CurrentSnapshot.Length); + if (tagsChangedHandler != null) + tagsChangedHandler.Invoke(this, new SnapshotSpanEventArgs(span)); + } else { + foreach (var tag in tagList.Values) { + var tagMapped = tag.MapToSnapshot(snapshot); + if (tagsChangedHandler != null) + tagsChangedHandler.Invoke(this, new SnapshotSpanEventArgs(tagMapped.Span)); + } + } + } + + public IEnumerable<ITagSpan<T>> GetTags(NormalizedSnapshotSpanCollection spans) + { + if (currentTagList == null || !currentTagList.Ready) + yield break; + + var firstTag = currentTagList.Values.FirstOrDefault(); + if (firstTag == null) + yield break; + + var snapshot = spans[0].Snapshot; + + bool sameVersion = + (firstTag.Snapshot.Version.VersionNumber == snapshot.Version.VersionNumber); + + foreach (var span in spans) { + + int idx = currentTagList.FindTagAtLocation(snapshot, span.Start); + if (idx == -1) + continue; + + for (; idx < currentTagList.Values.Count; idx++) { + + var tag = currentTagList.Values[idx]; + + if (sameVersion && tag.Start > span.End) + break; + + var tagMapped = tag.MapToSnapshot(snapshot); + if (tagMapped.Span.Length == 0) + continue; + + if (!sameVersion && tagMapped.Span.Start > span.End) + break; + + if (!span.IntersectsWith(tagMapped.Span)) + continue; + + var classification = GetClassification(tag); + if (classification == null) + continue; + + var tracking = tagMapped.Tag.Span; + yield return + new TagSpan<T>(tracking.GetSpan(snapshot), classification); + } + } + } + + public event EventHandler<SnapshotSpanEventArgs> TagsChanged; + } + + namespace HelperTypes + { + public static class BinarySearchExtensions + { + /// <summary> + /// Generic BinarySearch method that will work on any IList(T), + /// based on Microsoft’s ownArray.BinarySearch(T) implementation + /// Adapted from http://philosopherdeveloper.com/posts/whats-annoying-about-sorted-list-index-of-key.html + /// </summary> + /// <returns> + /// The index of the specified value in the specified list, if value is found; + /// otherwise, a negative number. If value is not found and value is less than one or + /// more elements in list, the negative number returned is the bitwise complement of + /// the index of the first element that is larger than value. If value is not found and + /// value is greater than all elements in list, the negative number returned is the + /// bitwise complement of (the index of the last element plus 1). If this method is + /// called with a non-sorted list, the return value can be incorrect and a negative + /// number could be returned, even if value is present in list. + /// (cf. https://docs.microsoft.com/en-us/dotnet/api/system.array.binarysearch) + /// + /// In case of error, returns null. + /// </returns> + public static int? BinarySearch<T>( + this IList<T> list, + int index, + int length, + T value, + IComparer<T> comparer) + { + if (list == null) + return null; + if (index < 0 || length < 0) + return null; + if (list.Count - index < length) + return null; + + int lower = index; + int upper = (index + length) - 1; + + while (lower <= upper) { + int adjustedIndex = lower + ((upper - lower) >> 1); + int comparison = comparer.Compare(list[adjustedIndex], value); + if (comparison == 0) + return adjustedIndex; + else if (comparison < 0) + lower = adjustedIndex + 1; + else + upper = adjustedIndex - 1; + } + + return ~lower; + } + + public static int? BinarySearch<T>(this IList<T> list, T value, IComparer<T> comparer) + { + return list.BinarySearch(0, list.Count, value, comparer); + } + + public static int? BinarySearch<T>(this IList<T> list, T value) + where T : IComparable<T> + { + return list.BinarySearch(value, Comparer<T>.Default); + } + } + + /// <summary> + /// Base class for thread-safe, indexed data storage. References to stored values are + /// explicitly tracked to allow for timely disposal as soon as a value becomes + /// unreferenced. Shared data stores are intended to be used as singletons. For this + /// purpose, classes that inherit from SharedDataStore will include a static instance + /// member. + /// </summary> + /// <typeparam name="TKey">Value key type</typeparam> + /// <typeparam name="TValue">Value type</typeparam> + /// <typeparam name="TInstance"> + /// Type of singleton instance, i.e. the same class that is derived from SharedDataStore + /// </typeparam> + abstract class SharedDataStore<TKey, TValue, TInstance> + where TInstance : SharedDataStore<TKey, TValue, TInstance>, new() + { + protected abstract TValue GetDefaultValue(TKey key); + + class ValueRef + { + public TValue Value { get; set; } + public HashSet<object> ClientObjects { get; set; } + } + Dictionary<TKey, ValueRef> data = new Dictionary<TKey, ValueRef>(); + + static readonly object staticCriticalSection = new object(); + readonly object criticalSection = new object(); + + protected SharedDataStore() + { + data = new Dictionary<TKey, ValueRef>(); + } + + public TValue Get(object client, TKey key) + { + lock (criticalSection) { + ValueRef valueRef; + if (!data.TryGetValue(key, out valueRef)) { + valueRef = new ValueRef + { + Value = GetDefaultValue(key), + ClientObjects = new HashSet<object> { client } + }; + data.Add(key, valueRef); + } else { + valueRef.ClientObjects.Add(client); + } + return valueRef.Value; + } + } + + public void Release(object client, TKey key) + { + IDisposable disposable = null; + lock (criticalSection) { + ValueRef valueRef; + if (data.TryGetValue(key, out valueRef)) { + valueRef.ClientObjects.Remove(client); + if (valueRef.ClientObjects.Count == 0) { + data.Remove(key); + disposable = valueRef.Value as IDisposable; + } + } + } + if (disposable != null) + disposable.Dispose(); + } + + private static TInstance instance = null; + public static TInstance Instance + { + get + { + lock (staticCriticalSection) { + if (instance == null) { + instance = new TInstance(); + } + return instance; + } + } + } + } + + class TagListKey + { + public string Classification { get; private set; } + public ITextSnapshot Snapshot { get; private set; } + public TagListKey(string classification, ITextSnapshot snapshot) + { + Classification = classification; + Snapshot = snapshot; + } + + public override bool Equals(object obj) + { + var that = obj as TagListKey; + if (that == null) + return false; + if (Classification != that.Classification) + return false; + if (Snapshot.TextBuffer != that.Snapshot.TextBuffer) + return false; + if (Snapshot.Version.VersionNumber != that.Snapshot.Version.VersionNumber) + return false; + return true; + } + + public override int GetHashCode() + { + var hashBase = new Tuple<string, ITextBuffer, int>( + Classification, Snapshot.TextBuffer, + Snapshot.Version.VersionNumber); + return hashBase.GetHashCode(); + } + } + + class TagListStore : SharedDataStore<TagListKey, SharedTagList, TagListStore> + { + protected override SharedTagList GetDefaultValue(TagListKey key) + { + return new SharedTagList(); + } + } + + class ParserKey + { + public ITextSnapshot Snapshot { get; private set; } + public ParserKey(ITextSnapshot snapshot) + { + Snapshot = snapshot; + } + + public override bool Equals(object obj) + { + var that = obj as ParserKey; + if (that == null) + return false; + if (Snapshot.TextBuffer != that.Snapshot.TextBuffer) + return false; + if (Snapshot.Version.VersionNumber != that.Snapshot.Version.VersionNumber) + return false; + return true; + } + + public override int GetHashCode() + { + var hashBase = new Tuple<ITextBuffer, int>( + Snapshot.TextBuffer, Snapshot.Version.VersionNumber); + return hashBase.GetHashCode(); + } + } + + class ParserStore : SharedDataStore<ParserKey, Parser, ParserStore> + { + protected override Parser GetDefaultValue(ParserKey key) + { + return Parser.Parse(key.Snapshot.GetText()); + } + } + } +} diff --git a/QtVsTools.Package/QML/Classification/QmlClassificationFormat.cs b/QtVsTools.Package/QML/Classification/QmlClassificationFormat.cs new file mode 100644 index 0000000..4af9273 --- /dev/null +++ b/QtVsTools.Package/QML/Classification/QmlClassificationFormat.cs @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// This file contains the definition of syntax highlighting formats. +/// These definitions can be modified at run-time by the user. + +using System.ComponentModel.Composition; +using System.Windows.Media; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace QtVsTools.Qml.Classification +{ + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = QmlSyntaxTag.Keyword)] + [Name(QmlSyntaxTag.Keyword)] + [UserVisible(true)] + [Order(Before = Priority.Default)] + internal sealed class QmlKeywordFormat : ClassificationFormatDefinition + { + public QmlKeywordFormat() + { + DisplayName = "QML Keyword"; + ForegroundColor = Color.FromRgb(86, 156, 214); + } + } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = QmlSyntaxTag.Numeric)] + [Name(QmlSyntaxTag.Numeric)] + [UserVisible(true)] + [Order(Before = Priority.Default)] + internal sealed class QmlNumberFormat : ClassificationFormatDefinition + { + public QmlNumberFormat() + { + DisplayName = "QML Number"; + ForegroundColor = Color.FromRgb(181, 206, 168); + } + } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = QmlSyntaxTag.String)] + [Name(QmlSyntaxTag.String)] + [UserVisible(true)] + [Order(Before = Priority.Default)] + internal sealed class QmlStringFormat : ClassificationFormatDefinition + { + public QmlStringFormat() + { + DisplayName = "QML String"; + ForegroundColor = Color.FromRgb(214, 157, 133); + } + } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = QmlSyntaxTag.TypeName)] + [Name(QmlSyntaxTag.TypeName)] + [UserVisible(true)] + [Order(Before = Priority.Default, After = QmlSyntaxTag.Keyword)] + internal sealed class QmlTypeNameFormat : ClassificationFormatDefinition + { + public QmlTypeNameFormat() + { + DisplayName = "QML Type Name"; + ForegroundColor = Color.FromRgb(78, 201, 176); + } + } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = QmlSyntaxTag.Binding)] + [Name(QmlSyntaxTag.Binding)] + [UserVisible(true)] + [Order(Before = Priority.Default, After = QmlSyntaxTag.Keyword)] + internal sealed class QmlBindingFormat : ClassificationFormatDefinition + { + public QmlBindingFormat() + { + DisplayName = "QML Binding"; + ForegroundColor = Color.FromRgb(183, 153, 185); + } + } +} diff --git a/QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs b/QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs new file mode 100644 index 0000000..85c216d --- /dev/null +++ b/QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// Highlighting of syntax errors + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Threading.Tasks; +using System.Windows.Threading; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace QtVsTools.Qml.Classification +{ + [Export(typeof(IViewTaggerProvider))] + [ContentType("qml")] + [TagType(typeof(ErrorTag))] + internal sealed class QmlErrorClassifierProvider : IViewTaggerProvider + { + [Import] + internal IClassificationTypeRegistryService classificationTypeRegistry = null; + + public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag + { + QmlClassificationType.InitClassificationTypes(classificationTypeRegistry); + return new QmlErrorClassifier(textView, buffer) as ITagger<T>; + } + } + + internal sealed class QmlErrorClassifier : QmlAsyncClassifier<ErrorTag> + { + internal QmlErrorClassifier( + ITextView textView, + ITextBuffer buffer) + : base("Error", textView, buffer) + { + } + + protected override ClassificationRefresh ProcessText( + ITextSnapshot snapshot, + Parser parseResult, + SharedTagList tagList, + bool writeAccess) + { + if (writeAccess) { + foreach (var diag in parseResult.DiagnosticMessages) { + tagList.Add(this, new QmlDiagnosticsTag(snapshot, diag)); + } + } + return ClassificationRefresh.FullText; + } + + protected override ErrorTag GetClassification(TrackingTag tag) + { + return new ErrorTag("ERROR"); + } + } +} diff --git a/QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs b/QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs new file mode 100644 index 0000000..dd0528e --- /dev/null +++ b/QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.ComponentModel.Composition; +using System.Linq; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Utilities; +using QtVsTools.Qml.Syntax; +using QtVsTools.VisualStudio; + +namespace QtVsTools.Qml.Classification +{ + [Export(typeof(IViewTaggerProvider))] + [ContentType("qml")] + [TagType(typeof(ClassificationTag))] + internal sealed class QmlExpressionEvalProvider : IViewTaggerProvider + { + public const string ClassificationType = QmlExpressionEval.ClassificationType; + + [Import] + internal IClassificationTypeRegistryService classificationTypeRegistry = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name(ClassificationType)] + internal static ClassificationTypeDefinition qmlDebug = null; + + public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag + { + QmlClassificationType.InitClassificationTypes(classificationTypeRegistry); + if (!QmlClassificationType.ClassificationTypes.ContainsKey(ClassificationType)) { + QmlClassificationType.ClassificationTypes.Add(ClassificationType, + classificationTypeRegistry.GetClassificationType(ClassificationType)); + } + + return QmlExpressionEval.Create(textView, buffer) as ITagger<T>; + } + } + + internal sealed class QmlExpressionEval : + QmlAsyncClassifier<ClassificationTag>, + IOleCommandTarget, + IVsTextViewFilter + { + public const string ClassificationType = "expr_eval.qml"; + + ITextView textView; + ITextBuffer buffer; + IVsDebugger debugger; + IVsTextView vsTextView; + IVsTextLines textLines; + IOleCommandTarget nextTarget; + + public static QmlExpressionEval Create(ITextView textView, ITextBuffer buffer) + { + var _this = new QmlExpressionEval(textView, buffer); + return _this.Initialize(textView, buffer) ? _this : null; + } + + private QmlExpressionEval(ITextView textView, ITextBuffer buffer) + : base(ClassificationType, textView, buffer) + { } + + private bool Initialize(ITextView textView, ITextBuffer buffer) + { + this.textView = textView; + this.buffer = buffer; + + debugger = VsServiceProvider.GetService<IVsDebugger>(); + if (debugger == null) + return false; + + var componentModel = VsServiceProvider + .GetService<SComponentModel, IComponentModel>(); + if (componentModel == null) + return false; + + var editorFactory = componentModel.GetService<IVsEditorAdaptersFactoryService>(); + if (editorFactory == null) + return false; + + vsTextView = editorFactory.GetViewAdapter(textView); + if (vsTextView == null) + return false; + + if (vsTextView.GetBuffer(out textLines) != VSConstants.S_OK) + return false; + + if (vsTextView.AddCommandFilter(this, out nextTarget) != VSConstants.S_OK) + return false; + + textView.Closed += TextView_Closed; + + return true; + } + + private void TextView_Closed(object sender, EventArgs e) + { + vsTextView.RemoveCommandFilter(this); + } + + protected override ClassificationRefresh ProcessText( + ITextSnapshot snapshot, + Parser parseResult, + SharedTagList tagList, + bool writeAccess) + { + if (writeAccess) { + var expressions = parseResult.AstNodes + .Where(x => x.Kind == AstNodeKind.FieldMemberExpression + || x.Kind == AstNodeKind.IdentifierExpression) + .GroupBy(x => x.FirstSourceLocation.Offset) + .Select(x => new + { + Offset = x.Key, + Length = x.Max(y => + y.LastSourceLocation.Offset + y.LastSourceLocation.Length) - x.Key, + List = x.OrderBy(y => + y.LastSourceLocation.Offset + y.LastSourceLocation.Length) + }); + tagList.AddRange(this, expressions + .Select(x => new ExprTrackingTag(snapshot, x.Offset, x.Length, x.List))); + } + return ClassificationRefresh.FullText; + } + + protected override ClassificationTag GetClassification(TrackingTag tag) + { + var debugTag = tag as ExprTrackingTag; + if (debugTag == null) + return null; + return new ExprTag(debugTag.Exprs, QmlClassificationType.Get(ClassificationType)); + } + + int IVsTextViewFilter.GetDataTipText(TextSpan[] pSpan, out string pbstrText) + { + pbstrText = ""; + var dbgMode = new DBGMODE[1]; + if (debugger.GetMode(dbgMode) != VSConstants.S_OK + || dbgMode[0] != DBGMODE.DBGMODE_Break) { + return VSConstants.S_FALSE; + } + + var startLine = buffer.CurrentSnapshot.GetLineFromLineNumber(pSpan[0].iStartLine); + var offset = startLine.Start.Position + pSpan[0].iStartIndex; + + var spans = new NormalizedSnapshotSpanCollection( + new SnapshotSpan(buffer.CurrentSnapshot, offset, 1)); + + var tags = GetTags(spans).Select(x => x.Tag).Cast<ExprTag>(); + + var expr = tags.SelectMany(x => x.Exprs) + .Where(x => offset < x.LastSourceLocation.Offset + x.LastSourceLocation.Length) + .FirstOrDefault(); + + if (expr == null) + return VSConstants.S_FALSE; + + var exprSpan = new Span(expr.FirstSourceLocation.Offset, + expr.LastSourceLocation.Offset + expr.LastSourceLocation.Length + - expr.FirstSourceLocation.Offset); + var exprText = buffer.CurrentSnapshot.GetText(exprSpan); + + return debugger.GetDataTipValue(textLines, pSpan, exprText, out pbstrText); + } + + int IVsTextViewFilter.GetWordExtent(int iLine, int iIndex, uint dwFlags, TextSpan[] pSpan) + { + return VSConstants.E_NOTIMPL; + } + + int IVsTextViewFilter.GetPairExtents(int iLine, int iIndex, TextSpan[] pSpan) + { + return VSConstants.E_NOTIMPL; + } + + int IOleCommandTarget.QueryStatus( + ref Guid pguidCmdGroup, + uint cCmds, + OLECMD[] prgCmds, + IntPtr pCmdText) + { + return nextTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + int IOleCommandTarget.Exec( + ref Guid pguidCmdGroup, + uint nCmdID, + uint nCmdexecopt, + IntPtr pvaIn, + IntPtr pvaOut) + { + return nextTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + class ExprTrackingTag : TrackingTag + { + public IOrderedEnumerable<AstNode> Exprs { get; private set; } + + public ExprTrackingTag( + ITextSnapshot snapshot, + int offset, + int length, + IOrderedEnumerable<AstNode> exprs) + : base(snapshot, offset, length) + { + Exprs = exprs; + } + } + + class ExprTag : ClassificationTag + { + public IOrderedEnumerable<AstNode> Exprs { get; private set; } + + public ExprTag(IOrderedEnumerable<AstNode> exprs, IClassificationType type) + : base(type) + { + Exprs = exprs; + } + } + } +} diff --git a/QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs b/QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs new file mode 100644 index 0000000..4675637 --- /dev/null +++ b/QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// This file implements the actual highlighting of the text according to the +/// classification of the syntax elements recognized by the QML parser. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Threading.Tasks; +using System.Windows.Threading; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace QtVsTools.Qml.Classification +{ + [Export(typeof(IViewTaggerProvider))] + [ContentType("qml")] + [TagType(typeof(ClassificationTag))] + internal sealed class QmlSyntaxClassifierProvider : IViewTaggerProvider + { + [Export] + [Name("qml")] + [BaseDefinition("code")] + internal static ContentTypeDefinition qmlContentType = null; + + [Export] + [FileExtension(".qml")] + [ContentType("qml")] + internal static FileExtensionToContentTypeDefinition qmlFileType = null; + + [Export] + [FileExtension(".qmlproject")] + [ContentType("qml")] + internal static FileExtensionToContentTypeDefinition qmlprojectFileType = null; + + [Import] + internal IClassificationTypeRegistryService classificationTypeRegistry = null; + + public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag + { + QmlClassificationType.InitClassificationTypes(classificationTypeRegistry); + return new QmlSyntaxClassifier(textView, buffer) as ITagger<T>; + } + } + + internal sealed class QmlSyntaxClassifier : QmlAsyncClassifier<ClassificationTag> + { + internal QmlSyntaxClassifier( + ITextView textView, + ITextBuffer buffer) + : base("Syntax", textView, buffer) + { + } + + protected override ClassificationRefresh ProcessText( + ITextSnapshot snapshot, + Parser parseResult, + SharedTagList tagList, + bool writeAccess) + { + bool parsedCorrectly = parseResult.ParsedCorrectly; + + if (writeAccess) { + foreach (var token in parseResult.Tokens) { + tagList.AddRange(this, QmlSyntaxTag.GetClassification(snapshot, token)); + } + foreach (var node in parseResult.AstNodes) { + tagList.AddRange(this, QmlSyntaxTag.GetClassification(snapshot, node)); + } + } + + if (parsedCorrectly) + return ClassificationRefresh.FullText; + else + return ClassificationRefresh.TagsOnly; + } + + protected override ClassificationTag GetClassification(TrackingTag tag) + { + var syntaxTag = tag as QmlSyntaxTag; + if (syntaxTag == null || syntaxTag.ClassificationType == null) + return null; + + return new ClassificationTag(syntaxTag.ClassificationType); + } + } +} diff --git a/QtVsTools.Package/QML/Classification/QmlTag.cs b/QtVsTools.Package/QML/Classification/QmlTag.cs new file mode 100644 index 0000000..5d92dcd --- /dev/null +++ b/QtVsTools.Package/QML/Classification/QmlTag.cs @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// This file contains the classification of the syntax elements recognized by the QML parser. + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Language.StandardClassification; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace QtVsTools.Qml.Classification +{ + using Syntax; + using VisualStudio.Text.Extensions; + + /// <summary> + /// Represents a classification tag that can be mapped onto future versions of the source code + /// </summary> + public class TrackingTag : ITag + { + public ITextSnapshot Snapshot { get; private set; } + public int Start { get; private set; } + public int Length { get; private set; } + public ITrackingSpan Span { get; private set; } + public TrackingTag(ITextSnapshot snapshot, int start, int length) + { + Snapshot = snapshot; + Start = start; + Length = length; + Span = snapshot.CreateTrackingSpan(start, length, SpanTrackingMode.EdgeExclusive); + } + public ITagSpan<TrackingTag> MapToSnapshot(ITextSnapshot snapshot) + { + return new TagSpan<TrackingTag>(Span.GetSpan(snapshot), this); + } + } + + /// <summary> + /// Represents the classification of a QML syntax element + /// </summary> + public class QmlSyntaxTag : TrackingTag + { + public const string Keyword = "keyword.qml"; + public const string Numeric = "numeric.qml"; + public const string String = "string.qml"; + public const string Comment = "comment.qml"; + public const string TypeName = "typename.qml"; + public const string Binding = "binding.qml"; + + public SyntaxElement SyntaxElement { get; private set; } + public SourceLocation SourceLocation { get; private set; } + public IClassificationType ClassificationType { get; private set; } + + private QmlSyntaxTag(ITextSnapshot snapshot, SourceLocation location) + : base(snapshot, location.Offset, location.Length) + { + SourceLocation = location; + } + + public QmlSyntaxTag( + ITextSnapshot snapshot, + SyntaxElement element, + string classificationType, + SourceLocation location) + : this(snapshot, location) + { + SyntaxElement = element; + ClassificationType = QmlClassificationType.Get(classificationType); + } + + static QmlSyntaxTag GetClassificationTag( + ITextSnapshot snapshot, + AstNode parentNode, + string classificationType, + UiQualifiedId qualifiedId) + { + var firstName = qualifiedId.IdentifierToken; + var lastName = qualifiedId.IdentifierToken; + while (qualifiedId.Next != null) { + qualifiedId = qualifiedId.Next; + lastName = qualifiedId.IdentifierToken; + } + var fullNameLocation = new SourceLocation + { + Offset = firstName.Offset, + Length = lastName.Offset + lastName.Length - firstName.Offset + }; + + return new QmlSyntaxTag(snapshot, parentNode, classificationType, fullNameLocation); + } + + public static readonly HashSet<string> QmlBasicTypes = new HashSet<string> { + "bool", "double", "enumeration", "int", + "list", "real", "string", "url", "var", + "date", "point", "rect", "size", "alias" + }; + + public static IEnumerable<QmlSyntaxTag> GetClassification( + ITextSnapshot snapshot, + SyntaxElement element) + { + var tags = new List<QmlSyntaxTag>(); + + if (element is KeywordToken) { + var token = element as KeywordToken; + tags.Add(new QmlSyntaxTag(snapshot, token, Keyword, token.Location)); + + } else if (element is NumberToken) { + var token = element as NumberToken; + tags.Add(new QmlSyntaxTag(snapshot, token, Numeric, token.Location)); + + } else if (element is StringToken) { + var token = element as StringToken; + tags.Add(new QmlSyntaxTag(snapshot, token, String, token.Location)); + + } else if (element is CommentToken) { + var token = element as CommentToken; + // QML parser does not report the initial/final tokens of comments + var commentStart = snapshot.GetText(token.Location.Offset - 2, 2); + var commentLocation = token.Location; + if (commentStart == "//") { + commentLocation.Offset -= 2; + commentLocation.Length += 2; + } else { + commentLocation.Offset -= 2; + commentLocation.Length += 4; + } + tags.Add(new QmlSyntaxTag(snapshot, token, Comment, commentLocation)); + + } else if (element is UiImport) { + var node = element as UiImport; + if (node.ImportIdToken.Length > 0) + tags.Add(new QmlSyntaxTag(snapshot, node, TypeName, node.ImportIdToken)); + + } else if (element is UiObjectDefinition) { + var node = element as UiObjectDefinition; + if (node.QualifiedTypeNameId != null) { + var name = snapshot.GetText(node.QualifiedTypeNameId.IdentifierToken); + // an UiObjectDefinition may be used to group property bindings + // think anchors { ... } + bool isGroupedBinding = !string.IsNullOrEmpty(name) && char.IsLower(name[0]); + if (!isGroupedBinding) { + tags.Add(GetClassificationTag( + snapshot, node, TypeName, node.QualifiedTypeNameId)); + } else { + tags.Add(GetClassificationTag( + snapshot, node, Binding, node.QualifiedTypeNameId)); + } + } + + } else if (element is UiObjectBinding) { + var node = element as UiObjectBinding; + if (node.QualifiedId != null) { + tags.Add(GetClassificationTag( + snapshot, node, Binding, node.QualifiedId)); + } + if (node.QualifiedTypeNameId != null) { + tags.Add(GetClassificationTag( + snapshot, node, TypeName, node.QualifiedTypeNameId)); + } + + } else if (element is UiScriptBinding) { + var node = element as UiScriptBinding; + var qualifiedId = node.QualifiedId; + while (qualifiedId != null) { + tags.Add(GetClassificationTag(snapshot, node, Binding, qualifiedId)); + qualifiedId = qualifiedId.Next; + } + + } else if (element is UiArrayBinding) { + var node = element as UiArrayBinding; + var qualifiedId = node.QualifiedId; + while (qualifiedId != null) { + tags.Add(GetClassificationTag(snapshot, node, Binding, qualifiedId)); + qualifiedId = qualifiedId.Next; + } + + } else if (element is UiPublicMember) { + var node = element as UiPublicMember; + if (node.Type == UiPublicMemberType.Property && node.TypeToken.Length > 0) { + var typeName = snapshot.GetText(node.TypeToken); + if (QmlBasicTypes.Contains(typeName)) + tags.Add(new QmlSyntaxTag(snapshot, node, Keyword, node.TypeToken)); + else + tags.Add(new QmlSyntaxTag(snapshot, node, TypeName, node.TypeToken)); + } + if (node.IdentifierToken.Length > 0) + tags.Add(new QmlSyntaxTag(snapshot, node, Binding, node.IdentifierToken)); + + } + return tags; + } + } + + public class QmlDiagnosticsTag : TrackingTag + { + public DiagnosticMessage DiagnosticMessage { get; private set; } + public QmlDiagnosticsTag(ITextSnapshot snapshot, DiagnosticMessage diagnosticMessage) + : base(snapshot, diagnosticMessage.Location.Offset, diagnosticMessage.Location.Length) + { + DiagnosticMessage = diagnosticMessage; + } + } + + internal static class QmlClassificationType + { + [Export(typeof(ClassificationTypeDefinition))] + [Name(QmlSyntaxTag.Keyword)] + internal static ClassificationTypeDefinition qmlKeyword = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name(QmlSyntaxTag.Numeric)] + internal static ClassificationTypeDefinition qmlNumber = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name(QmlSyntaxTag.String)] + internal static ClassificationTypeDefinition qmlString = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name(QmlSyntaxTag.Comment)] + internal static ClassificationTypeDefinition qmlComment = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name(QmlSyntaxTag.TypeName)] + internal static ClassificationTypeDefinition qmlTypeName = null; + + [Export(typeof(ClassificationTypeDefinition))] + [Name(QmlSyntaxTag.Binding)] + internal static ClassificationTypeDefinition qmlBinding = null; + + public static IDictionary<string, IClassificationType> ClassificationTypes + { + get; private set; + } + + public static void InitClassificationTypes(IClassificationTypeRegistryService typeService) + { + if (ClassificationTypes != null) + return; + ClassificationTypes = new Dictionary<string, IClassificationType> + { + { QmlSyntaxTag.Keyword, + typeService.GetClassificationType(QmlSyntaxTag.Keyword) }, + { QmlSyntaxTag.Numeric, + typeService.GetClassificationType(QmlSyntaxTag.Numeric) }, + { QmlSyntaxTag.String, + typeService.GetClassificationType(QmlSyntaxTag.String) }, + { QmlSyntaxTag.TypeName, + typeService.GetClassificationType(QmlSyntaxTag.TypeName) }, + { QmlSyntaxTag.Binding, + typeService.GetClassificationType(QmlSyntaxTag.Binding) }, + + // QML comments are mapped to the Visual Studio pre-defined comment classification + { QmlSyntaxTag.Comment, + typeService.GetClassificationType(PredefinedClassificationTypeNames.Comment) } + }; + } + public static IClassificationType Get(string classificationType) + { + if (ClassificationTypes == null) + return null; + return ClassificationTypes[classificationType]; + } + } + + namespace VisualStudio.Text.Extensions + { + public static class TextSnapshotExtensions + { + public static string GetText(this ITextSnapshot _this, SourceLocation sourceLocation) + { + if (sourceLocation.Length == 0) + return string.Empty; + if (_this.Length < sourceLocation.Offset + sourceLocation.Length) + return string.Empty; + try { + return _this.GetText(sourceLocation.Offset, sourceLocation.Length); + } catch (Exception) { + return string.Empty; + } + } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7BoilerPlate.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7BoilerPlate.cs new file mode 100644 index 0000000..1868a51 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7BoilerPlate.cs @@ -0,0 +1,547 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + sealed partial class QmlEngine + { + #region //////////////////// IDebugEngine2 //////////////////////////////////////////////// + + int IDebugEngine2.SetLocale(ushort wLangID) + { + return VSConstants.S_OK; + } + + int IDebugEngine2.SetRegistryRoot(string pszRegistryRoot) + { + return VSConstants.S_OK; + } + + int IDebugEngine2.SetMetric(string pszMetric, object varValue) + { + return VSConstants.S_OK; + } + + int IDebugEngine2.EnumPrograms(out IEnumDebugPrograms2 ppEnum) + { throw new NotImplementedException(); } + + int IDebugEngine2.SetException(EXCEPTION_INFO[] pException) + { throw new NotImplementedException(); } + + int IDebugEngine2.RemoveSetException(EXCEPTION_INFO[] pException) + { throw new NotImplementedException(); } + + int IDebugEngine2.RemoveAllSetExceptions(ref Guid guidType) + { throw new NotImplementedException(); } + + int IDebugEngine2.DestroyProgram(IDebugProgram2 pProgram) + { throw new NotImplementedException(); } + + int IDebugEngine2.CauseBreak() + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugEngine2 ///////////////////////////////////////////// + } + + sealed partial class ProgramProvider + { + #region //////////////////// IDebugProgramProvider2 /////////////////////////////////////// + + int IDebugProgramProvider2.GetProviderProcessData( + enum_PROVIDER_FLAGS Flags, + IDebugDefaultPort2 pPort, + AD_PROCESS_ID ProcessId, + CONST_GUID_ARRAY EngineFilter, + PROVIDER_PROCESS_DATA[] pProcess) + { + return VSConstants.E_NOTIMPL; + } + + int IDebugProgramProvider2.GetProviderProgramNode( + enum_PROVIDER_FLAGS Flags, + IDebugDefaultPort2 pPort, + AD_PROCESS_ID ProcessId, + ref Guid guidEngine, + ulong programId, + out IDebugProgramNode2 ppProgramNode) + { + ppProgramNode = null; + return VSConstants.E_NOTIMPL; + } + + int IDebugProgramProvider2.WatchForProviderEvents( + enum_PROVIDER_FLAGS Flags, + IDebugDefaultPort2 pPort, + AD_PROCESS_ID ProcessId, + CONST_GUID_ARRAY EngineFilter, + ref Guid guidLaunchingEngine, + IDebugPortNotify2 pEventCallback) + { + return VSConstants.S_OK; + } + + int IDebugProgramProvider2.SetLocale(ushort wLangID) + { + return VSConstants.S_OK; + } + + #endregion //////////////////// IDebugProgramProvider2 //////////////////////////////////// + } + + sealed partial class Program + { + #region //////////////////// IDebugProgramNode2 /////////////////////////////////////////// + + int IDebugProgramNode2.GetHostName( + enum_GETHOSTNAME_TYPE dwHostNameType, + out string pbstrHostName) + { + pbstrHostName = string.Empty; + return VSConstants.E_NOTIMPL; + } + + int IDebugProgramNode2.GetHostMachineName_V7(out string pbstrHostMachineName) + { throw new NotImplementedException(); } + + int IDebugProgramNode2.Attach_V7( + IDebugProgram2 pMDMProgram, + IDebugEventCallback2 pCallback, + uint dwReason) + { throw new NotImplementedException(); } + + int IDebugProgramNode2.DetachDebugger_V7() + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugProgramNode2 //////////////////////////////////////// + + + #region //////////////////// IDebugProgram3 /////////////////////////////////////////////// + + public int /*IDebugProgram3*/ Terminate() + { + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ CauseBreak() + { + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ CanDetach() + { + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ Detach() + { + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ GetProcess(out IDebugProcess2 ppProcess) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ Attach(IDebugEventCallback2 pCallback) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ GetDebugProperty(out IDebugProperty2 ppProperty) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ Execute() + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ GetEngineInfo(out string pbstrEngine, out Guid pguidEngine) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ EnumCodeContexts( + IDebugDocumentPosition2 pDocPos, + out IEnumDebugCodeContexts2 ppEnum) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ GetMemoryBytes(out IDebugMemoryBytes2 ppMemoryBytes) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ GetDisassemblyStream( + enum_DISASSEMBLY_STREAM_SCOPE dwScope, + IDebugCodeContext2 pCodeContext, + out IDebugDisassemblyStream2 ppDisassemblyStream) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ GetENCUpdate(out object ppUpdate) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ EnumCodePaths(string pszHint, + IDebugCodeContext2 pStart, + IDebugStackFrame2 pFrame, + int fSource, + out IEnumCodePaths2 ppEnum, + out IDebugCodeContext2 ppSafety) + { throw new NotImplementedException(); } + + public int /*IDebugProgram3*/ WriteDump(enum_DUMPTYPE DUMPTYPE, string pszDumpUrl) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugProgram3 //////////////////////////////////////////// + + + #region //////////////////// IDebugModule3 //////////////////////////////////////////////// + + public int /*IDebugModule3*/ IsUserCode(out int pfUser) + { + pfUser = 1; + return VSConstants.S_OK; + } + + public int /*IDebugModule3*/ ReloadSymbols_Deprecated( + string pszUrlToSymbols, + out string pbstrDebugMessage) + { throw new NotImplementedException(); } + + public int /*IDebugModule3*/ GetSymbolInfo( + enum_SYMBOL_SEARCH_INFO_FIELDS dwFields, + MODULE_SYMBOL_SEARCH_INFO[] pinfo) + { throw new NotImplementedException(); } + + public int /*IDebugModule3*/ LoadSymbols() + { throw new NotImplementedException(); } + + public int /*IDebugModule3*/ SetJustMyCodeState(int fIsUserCode) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugModule3 ///////////////////////////////////////////// + + + #region //////////////////// IDebugProcess2 /////////////////////////////////////////////// + + int IDebugProcess2.GetInfo(enum_PROCESS_INFO_FIELDS Fields, PROCESS_INFO[] pProcessInfo) + { throw new NotImplementedException(); } + + int IDebugProcess2.GetName(enum_GETNAME_TYPE gnType, out string pbstrName) + { throw new NotImplementedException(); } + + int IDebugProcess2.GetServer(out IDebugCoreServer2 ppServer) + { throw new NotImplementedException(); } + + int IDebugProcess2.Terminate() + { throw new NotImplementedException(); } + + int IDebugProcess2.EnumPrograms(out IEnumDebugPrograms2 ppEnum) + { throw new NotImplementedException(); } + + int IDebugProcess2.Attach( + IDebugEventCallback2 pCallback, + Guid[] rgguidSpecificEngines, + uint celtSpecificEngines, + int[] rghrEngineAttach) + { throw new NotImplementedException(); } + + int IDebugProcess2.GetAttachedSessionName(out string pbstrSessionName) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugProcess2 //////////////////////////////////////////// + + + #region //////////////////// IDebugThread2 //////////////////////////////////////////////// + + int IDebugThread2.SetThreadName(string pszName) + { throw new NotImplementedException(); } + + int IDebugThread2.GetProgram(out IDebugProgram2 ppProgram) + { throw new NotImplementedException(); } + + int IDebugThread2.CanSetNextStatement( + IDebugStackFrame2 pStackFrame, + IDebugCodeContext2 pCodeContext) + { throw new NotImplementedException(); } + + int IDebugThread2.SetNextStatement(IDebugStackFrame2 pStackFrame, + IDebugCodeContext2 pCodeContext) + { throw new NotImplementedException(); } + + int IDebugThread2.Suspend(out uint pdwSuspendCount) + { throw new NotImplementedException(); } + + int IDebugThread2.Resume(out uint pdwSuspendCount) + { throw new NotImplementedException(); } + + int IDebugThread2.GetLogicalThread(IDebugStackFrame2 pStackFrame, + out IDebugLogicalThread2 ppLogicalThread) + { throw new NotImplementedException(); } + + int IDebugThread100.GetFlags(out uint pFlags) + { throw new NotImplementedException(); } + + int IDebugThread100.SetFlags(uint flags) + { throw new NotImplementedException(); } + + int IDebugThread100.CanDoFuncEval() + { throw new NotImplementedException(); } + + int IDebugThread100.GetThreadDisplayName(out string bstrDisplayName) + { throw new NotImplementedException(); } + + int IDebugThread100.SetThreadDisplayName(string bstrDisplayName) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugThread2 ///////////////////////////////////////////// + } + + sealed partial class CodeContext + { + #region //////////////////// IDebugDocumentContext2 /////////////////////////////////////// + int IDebugDocumentContext2.GetDocument(out IDebugDocument2 ppDocument) + { + ppDocument = null; + return VSConstants.E_FAIL; + } + int IDebugDocumentContext2.GetSourceRange( + TEXT_POSITION[] pBegPosition, + TEXT_POSITION[] pEndPosition) + { throw new NotImplementedException(); } + + int IDebugDocumentContext2.Compare( + enum_DOCCONTEXT_COMPARE Compare, + IDebugDocumentContext2[] rgpDocContextSet, + uint dwDocContextSetLen, + out uint pdwDocContext) + { + dwDocContextSetLen = 0; + pdwDocContext = 0; + return VSConstants.E_NOTIMPL; + } + + int IDebugDocumentContext2.Seek(int nCount, out IDebugDocumentContext2 ppDocContext) + { + ppDocContext = null; + return VSConstants.E_NOTIMPL; + } + + #endregion //////////////////// IDebugDocumentContext2 //////////////////////////////////// + + + #region //////////////////// IDebugCodeContext2 /////////////////////////////////////////// + + public int /*IDebugCodeContext2*/ Add(ulong dwCount, out IDebugMemoryContext2 ppMemCxt) + { throw new NotImplementedException(); } + + public int /*IDebugCodeContext2*/ Subtract( + ulong dwCount, + out IDebugMemoryContext2 ppMemCxt) + { throw new NotImplementedException(); } + + public int /*IDebugCodeContext2*/ GetName(out string pbstrName) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugCodeContext2 //////////////////////////////////////// + } + + sealed partial class PendingBreakpoint + { + #region //////////////////// IDebugPendingBreakpoint2 ///////////////////////////////////// + + int IDebugPendingBreakpoint2.CanBind(out IEnumDebugErrorBreakpoints2 ppErrorEnum) + { + ppErrorEnum = null; + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.Virtualize(int fVirtualize) + { + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.EnumErrorBreakpoints( + enum_BP_ERROR_TYPE bpErrorType, + out IEnumDebugErrorBreakpoints2 ppEnum) + { + ppEnum = null; + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.SetCondition(BP_CONDITION bpCondition) + { throw new NotImplementedException(); } + + int IDebugPendingBreakpoint2.SetPassCount(BP_PASSCOUNT bpPassCount) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugPendingBreakpoint2 ////////////////////////////////// + } + + sealed partial class Breakpoint + { + #region //////////////////// IDebugBoundBreakpoint2 /////////////////////////////////////// + + int IDebugBoundBreakpoint2.GetHitCount(out uint pdwHitCount) + { + pdwHitCount = 0; + return VSConstants.E_NOTIMPL; + } + + int IDebugBoundBreakpoint2.SetHitCount(uint dwHitCount) + { + return VSConstants.E_NOTIMPL; + } + + int IDebugBoundBreakpoint2.SetCondition(BP_CONDITION bpCondition) + { throw new NotImplementedException(); } + + int IDebugBoundBreakpoint2.SetPassCount(BP_PASSCOUNT bpPassCount) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugBoundBreakpoint2 //////////////////////////////////// + } + + sealed partial class StackFrame + { + #region //////////////////// IDebugExpressionContext2 ///////////////////////////////////// + + int IDebugExpressionContext2.GetName(out string pbstrName) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugExpressionContext2 ////////////////////////////////// + + + #region //////////////////// IDebugProperty2 ////////////////////////////////////////////// + + int IDebugProperty2.GetDerivedMostProperty(out IDebugProperty2 ppDerivedMost) + { + ppDerivedMost = null; + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.GetExtendedInfo(ref Guid guidExtendedInfo, out object pExtendedInfo) + { + pExtendedInfo = null; + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.GetMemoryBytes(out IDebugMemoryBytes2 ppMemoryBytes) + { + ppMemoryBytes = null; + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.GetMemoryContext(out IDebugMemoryContext2 ppMemory) + { + ppMemory = null; + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.GetParent(out IDebugProperty2 ppParent) + { + ppParent = null; + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.GetPropertyInfo( + enum_DEBUGPROP_INFO_FLAGS dwFields, + uint dwRadix, + uint dwTimeout, + IDebugReference2[] rgpArgs, + uint dwArgCount, + DEBUG_PROPERTY_INFO[] pPropertyInfo) + { + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.GetReference(out IDebugReference2 ppReference) + { + ppReference = null; + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.GetSize(out uint pdwSize) + { + pdwSize = 0; + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.SetValueAsReference( + IDebugReference2[] rgpArgs, + uint dwArgCount, + IDebugReference2 pValue, + uint dwTimeout) + { + return VSConstants.E_NOTIMPL; + } + + int IDebugProperty2.SetValueAsString(string pszValue, uint dwRadix, uint dwTimeout) + { + return VSConstants.E_NOTIMPL; + } + + #endregion //////////////////// IDebugProperty2 /////////////////////////////////////////// + } + + sealed partial class Property + { + #region //////////////////// IDebugProperty2 ////////////////////////////////////////////// + + int IDebugProperty2.SetValueAsReference( + IDebugReference2[] rgpArgs, + uint dwArgCount, + IDebugReference2 pValue, + uint dwTimeout) + { throw new NotImplementedException(); } + + int IDebugProperty2.GetDerivedMostProperty(out IDebugProperty2 ppDerivedMost) + { throw new NotImplementedException(); } + + int IDebugProperty2.GetMemoryBytes(out IDebugMemoryBytes2 ppMemoryBytes) + { throw new NotImplementedException(); } + + int IDebugProperty2.GetMemoryContext(out IDebugMemoryContext2 ppMemory) + { throw new NotImplementedException(); } + + int IDebugProperty2.GetSize(out uint pdwSize) + { throw new NotImplementedException(); } + + int IDebugProperty2.GetReference(out IDebugReference2 ppReference) + { throw new NotImplementedException(); } + + int IDebugProperty2.GetExtendedInfo(ref Guid guidExtendedInfo, out object pExtendedInfo) + { throw new NotImplementedException(); } + + #endregion //////////////////// IDebugProperty2 /////////////////////////////////////////// + } + + sealed partial class Expression + { + #region //////////////////// IDebugExpression2 //////////////////////////////////////////// + + int IDebugExpression2.Abort() + { + return VSConstants.E_NOTIMPL; + } + + #endregion //////////////////// IDebugExpression2 ///////////////////////////////////////// + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Breakpoint.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Breakpoint.cs new file mode 100644 index 0000000..fad795d --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Breakpoint.cs @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + sealed partial class PendingBreakpoint : Disposable, + + IDebugPendingBreakpoint2 // "This interface represents a breakpoint that is ready to bind + // to a code location." + { + static readonly string[] ValidExtensions = new string[] { ".qml", ".js" }; + + public QmlEngine Engine { get; private set; } + public IDebugBreakpointRequest2 Request { get; private set; } + public enum_BP_LOCATION_TYPE LocationType { get; private set; } + public BP_REQUEST_INFO RequestInfo { get; private set; } + public string FileName { get; private set; } + public TEXT_POSITION BeginPosition { get; private set; } + public TEXT_POSITION EndPosition { get; private set; } + public bool Enabled { get; private set; } + + HashSet<Breakpoint> breakpoints; + + public static PendingBreakpoint Create(QmlEngine engine, IDebugBreakpointRequest2 request) + { + var _this = new PendingBreakpoint(); + return _this.Initialize(engine, request) ? _this : null; + } + + private PendingBreakpoint() + { } + + private bool Initialize(QmlEngine engine, IDebugBreakpointRequest2 request) + { + var locationType = new enum_BP_LOCATION_TYPE[1]; + if (request.GetLocationType(locationType) != VSConstants.S_OK) + return false; + + var requestInfo = new BP_REQUEST_INFO[1]; + if (request.GetRequestInfo(enum_BPREQI_FIELDS.BPREQI_ALLFIELDS, requestInfo) + != VSConstants.S_OK) { + return false; + } + + if (requestInfo[0].bpLocation.bpLocationType + != (uint)enum_BP_LOCATION_TYPE.BPLT_CODE_FILE_LINE) { + return false; + } + + var docPosition = Marshal.GetObjectForIUnknown(requestInfo[0].bpLocation.unionmember2) + as IDebugDocumentPosition2; + if (docPosition == null) + return false; + + string fileName; + if (docPosition.GetFileName(out fileName) != VSConstants.S_OK) + return false; + + if (!ValidExtensions.Where(x => string.Equals(x, Path.GetExtension(fileName))).Any()) + return false; + + TEXT_POSITION[] beginPosition = new TEXT_POSITION[1]; + TEXT_POSITION[] endPosition = new TEXT_POSITION[1]; + if (docPosition.GetRange(beginPosition, endPosition) != VSConstants.S_OK) + return false; + + Engine = engine; + Request = request; + LocationType = locationType[0]; + RequestInfo = requestInfo[0]; + FileName = fileName; + BeginPosition = beginPosition[0]; + EndPosition = endPosition[0]; + + breakpoints = new HashSet<Breakpoint>(); + + return true; + } + + protected override void DisposeManaged() + { + foreach (var breakpoint in ThreadSafe(() => breakpoints.ToList())) + breakpoint.Dispose(); + + ThreadSafe(() => breakpoints.Clear()); + } + + public void DisposeBreakpoint(Breakpoint breakpoint) + { + ThreadSafe(() => breakpoints.Remove(breakpoint)); + breakpoint.Dispose(); + } + + int IDebugPendingBreakpoint2.Bind() + { + foreach (var program in Engine.Programs) { + var breakpoint = Breakpoint.Create(this, program); + ThreadSafe(() => breakpoints.Add(breakpoint)); + program.SetBreakpoint(breakpoint); + } + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.Enable(int fEnable) + { + bool enable = (fEnable != 0); + if (Atomic(() => Enabled != enable, () => Enabled = enable)) { + foreach (var breakpoint in ThreadSafe(() => breakpoints.ToList())) + (breakpoint as IDebugBoundBreakpoint2).Enable(fEnable); + } + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.Delete() + { + Engine.DisposePendingBreakpoint(this); + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.EnumBoundBreakpoints(out IEnumDebugBoundBreakpoints2 ppEnum) + { + ppEnum = BoundBreakpointsEnum.Create(ThreadSafe(() => breakpoints.ToList())); + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.GetState(PENDING_BP_STATE_INFO[] pState) + { + if (Disposed) { + pState[0].state = (enum_PENDING_BP_STATE)enum_BP_STATE.BPS_DELETED; + + } else if (Enabled) { + pState[0].state = (enum_PENDING_BP_STATE)enum_BP_STATE.BPS_ENABLED; + + } else { + pState[0].state = (enum_PENDING_BP_STATE)enum_BP_STATE.BPS_DISABLED; + } + + return VSConstants.S_OK; + } + + int IDebugPendingBreakpoint2.GetBreakpointRequest(out IDebugBreakpointRequest2 ppBPRequest) + { + ppBPRequest = Request; + return VSConstants.S_OK; + } + } + + sealed partial class Breakpoint : Disposable, IBreakpoint, + + IDebugBoundBreakpoint2, // "This interface represents a breakpoint that is bound to a + // code location." + + IDebugBreakpointResolution2 // "This interface represents the information that describes a + // bound breakpoint." + { + public QmlDebugger Debugger { get; private set; } + public QmlEngine Engine { get; private set; } + public Program Program { get; private set; } + public PendingBreakpoint Parent { get; private set; } + public CodeContext CodeContext { get; private set; } + public bool Enabled { get; set; } + + bool supressNotify; + + string IBreakpoint.QrcPath + { + get + { + var qrcPath = Engine.FileSystem[Parent.FileName].QrcPath; + if (qrcPath == null) + return string.Empty; + if (qrcPath.StartsWith("qrc:///", StringComparison.InvariantCultureIgnoreCase)) + qrcPath = qrcPath.Substring("qrc:///".Length); + return qrcPath; + } + } + + uint IBreakpoint.Line + { + get { return Parent.BeginPosition.dwLine; } + } + + public static Breakpoint Create(PendingBreakpoint parent, Program program) + { + return new Breakpoint + { + Engine = parent.Engine, + Parent = parent, + Program = program, + Debugger = program.Debugger, + Enabled = parent.Enabled, + CodeContext = CodeContext.Create( + parent.Engine, + program, + parent.FileName, + parent.BeginPosition.dwLine), + }; + } + + private Breakpoint() + { } + + protected override void DisposeManaged() + { + Program.ClearBreakpoint(this); + } + + int IDebugBoundBreakpoint2.Enable(int fEnable) + { + bool enable = (fEnable != 0); + if (Atomic(() => Enabled != enable, + () => { Enabled = enable; supressNotify = true; })) { + + if (enable) + Debugger.SetBreakpoint(this); + else + Debugger.ClearBreakpoint(this); + } + + return VSConstants.S_OK; + } + + int IDebugBoundBreakpoint2.Delete() + { + Parent.DisposeBreakpoint(this); + return VSConstants.S_OK; + } + + void IBreakpoint.NotifySet() + { + if (!Atomic(() => supressNotify, () => supressNotify = false)) + Program.NotifyBreakpointSet(this); + } + + void IBreakpoint.NotifyClear() + { + if (!Atomic(() => supressNotify, () => supressNotify = false)) + Program.NotifyBreakpointCleared(this); + } + + void IBreakpoint.NotifyBreak() + { + Program.NotifyBreakpointHit(this); + } + + void IBreakpoint.NotifyError(string errorMessage) + { + Program.OutputWriteLine(errorMessage); + } + + int IDebugBoundBreakpoint2.GetPendingBreakpoint( + out IDebugPendingBreakpoint2 ppPendingBreakpoint) + { + ppPendingBreakpoint = Parent; + return VSConstants.S_OK; + } + + int IDebugBoundBreakpoint2.GetState(enum_BP_STATE[] pState) + { + pState[0] = 0; + if (Disposed) { + pState[0] = enum_BP_STATE.BPS_DELETED; + + } else if (Enabled) { + pState[0] = enum_BP_STATE.BPS_ENABLED; + + } else { + pState[0] = enum_BP_STATE.BPS_DISABLED; + } + + return VSConstants.S_OK; + } + + + #region //////////////////// IDebugBreakpointResolution2 ////////////////////////////////// + + int IDebugBoundBreakpoint2.GetBreakpointResolution( + out IDebugBreakpointResolution2 ppBPResolution) + { + ppBPResolution = this; + return VSConstants.S_OK; + } + + int IDebugBreakpointResolution2.GetBreakpointType(enum_BP_TYPE[] pBPType) + { + pBPType[0] = enum_BP_TYPE.BPT_CODE; + return VSConstants.S_OK; + } + + int IDebugBreakpointResolution2.GetResolutionInfo( + enum_BPRESI_FIELDS dwFields, + BP_RESOLUTION_INFO[] pBPResolutionInfo) + { + if ((dwFields & enum_BPRESI_FIELDS.BPRESI_BPRESLOCATION) != 0) { + BP_RESOLUTION_LOCATION location = new BP_RESOLUTION_LOCATION(); + location.bpType = (uint)enum_BP_TYPE.BPT_CODE; + location.unionmember1 + = Marshal.GetComInterfaceForObject(CodeContext, typeof(IDebugCodeContext2)); + pBPResolutionInfo[0].bpResLocation = location; + pBPResolutionInfo[0].dwFields |= enum_BPRESI_FIELDS.BPRESI_BPRESLOCATION; + } + if ((dwFields & enum_BPRESI_FIELDS.BPRESI_PROGRAM) != 0) { + pBPResolutionInfo[0].pProgram = Program as IDebugProgram2; + pBPResolutionInfo[0].dwFields |= enum_BPRESI_FIELDS.BPRESI_PROGRAM; + } + + return VSConstants.S_OK; + } + + #endregion //////////////////// IDebugBreakpointResolution2 /////////////////////////////// + + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7CodeContext.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7CodeContext.cs new file mode 100644 index 0000000..b6d7362 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7CodeContext.cs @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.IO; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + sealed partial class CodeContext : + + IDebugDocumentContext2, // "This interface represents a position in a source file document." + + IDebugCodeContext2, // "This interface represents the starting position of a code + IDebugCodeContext100 // instruction. For most run-time architectures today, a code + // context can be thought of as an address in a program's execution + // stream." + { + public QmlEngine Engine { get; private set; } + public Program Program { get; private set; } + + public string FilePath { get; private set; } + public uint FileLine { get; private set; } + + public enum Language { QML, JavaScript, Other } + public Language FileType + { + get + { + var ext = Path.GetExtension(FilePath); + if (string.Equals(ext, ".qml", StringComparison.InvariantCultureIgnoreCase)) + return Language.QML; + if (string.Equals(ext, ".js", StringComparison.InvariantCultureIgnoreCase)) + return Language.JavaScript; + return Language.Other; + } + } + + public static CodeContext Create( + QmlEngine engine, Program program, + string filePath, uint fileLine) + { + return new CodeContext() + { + Engine = engine, + Program = program, + FilePath = filePath, + FileLine = fileLine + }; + } + + private CodeContext() + { } + + class CodeContextInfo : InfoHelper<CodeContextInfo> + { + public string Address { get; set; } + } + + CodeContextInfo Info + { + get + { + return new CodeContextInfo + { + Address = FileLine.ToString() + }; + } + } + + static readonly CodeContextInfo.Mapping MappingToCONTEXT_INFO = + + #region //////////////////// CONTEXT_INFO <-- CodeContextInfo ///////////////////////////// + // r: Ref<CONTEXT_INFO> + // f: enum_CONTEXT_INFO_FIELDS + // i: CodeContextInfo + // v: value of i.<<property>> + + new CodeContextInfo.Mapping<CONTEXT_INFO, enum_CONTEXT_INFO_FIELDS> + ((r, f) => r.s.dwFields |= f) + { + { enum_CONTEXT_INFO_FIELDS.CIF_ADDRESS, + (r, v) => r.s.bstrAddress = v, i => i.Address } + }; + + #endregion //////////////////// CONTEXT_INFO <-- CodeContextInfo ////////////////////////// + + + public int /*IDebugCodeContext2*/ GetInfo( + enum_CONTEXT_INFO_FIELDS dwFields, + CONTEXT_INFO[] pinfo) + { + Info.Map(MappingToCONTEXT_INFO, dwFields, out pinfo[0]); + return VSConstants.S_OK; + } + + public int /*IDebugCodeContext2*/ Compare( + enum_CONTEXT_COMPARE Compare, + IDebugMemoryContext2[] rgpMemoryContextSet, + uint dwMemoryContextSetLen, + out uint pdwMemoryContext) + { + pdwMemoryContext = uint.MaxValue; + if (Compare != enum_CONTEXT_COMPARE.CONTEXT_EQUAL) + return VSConstants.E_NOTIMPL; + + for (uint i = 0; i < dwMemoryContextSetLen; ++i) { + var that = rgpMemoryContextSet[i] as CodeContext; + if (that == null) + continue; + if (this.Engine != that.Engine) + continue; + if (this.Program != that.Program) + continue; + if (!string.Equals( + Path.GetFullPath(this.FilePath), Path.GetFullPath(that.FilePath), + StringComparison.InvariantCultureIgnoreCase)) { + continue; + } + if (this.FileLine != that.FileLine) + continue; + + pdwMemoryContext = i; + return VSConstants.S_OK; + } + + return VSConstants.S_FALSE; + } + + int IDebugDocumentContext2.GetName(enum_GETNAME_TYPE gnType, out string pbstrFileName) + { + pbstrFileName = FilePath; + return VSConstants.S_OK; + } + + int IDebugDocumentContext2.GetStatementRange( + TEXT_POSITION[] pBegPosition, + TEXT_POSITION[] pEndPosition) + { + pBegPosition[0].dwLine = FileLine; + pBegPosition[0].dwColumn = 0; + pEndPosition[0].dwLine = FileLine; + pEndPosition[0].dwColumn = 0; + return VSConstants.S_OK; + } + + int IDebugDocumentContext2.EnumCodeContexts(out IEnumDebugCodeContexts2 ppEnumCodeCxts) + { + ppEnumCodeCxts = CodeContextEnum.Create(this); + return VSConstants.S_OK; + } + + int IDebugDocumentContext2.GetLanguageInfo( + ref string pbstrLanguage, + ref Guid pguidLanguage) + { + pbstrLanguage = "C++"; + pguidLanguage = NativeEngine.IdLanguageCpp; + return VSConstants.S_OK; + } + + int IDebugCodeContext2.GetLanguageInfo(ref string pbstrLanguage, ref Guid pguidLanguage) + { + return (this as IDebugDocumentContext2) + .GetLanguageInfo(ref pbstrLanguage, ref pguidLanguage); + } + + int IDebugCodeContext2.GetDocumentContext(out IDebugDocumentContext2 ppSrcCxt) + { + ppSrcCxt = this; + return VSConstants.S_OK; + } + + int IDebugCodeContext100.GetProgram(out IDebugProgram2 ppProgram) + { + ppProgram = Program; + return VSConstants.S_OK; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Engine.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Engine.cs new file mode 100644 index 0000000..8da6eb9 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Engine.cs @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + [ComVisible(true)] + [Guid(CLSID_ENGINE)] + sealed partial class QmlEngine : + + IDebugEngine2, // "This interface represents a debug engine (DE). It is used to manage + // various aspects of a debugging session, from creating breakpoints + // to setting and clearing exceptions." + + IDebugEngineLaunch2, // "Used by a debug engine (DE) to launch and terminate programs." + + IDebugEventCallback2 // "This interface is used by the debug engine (DE) to send debug + // events to the session debug manager (SDM)." + { + const string CLSID_ENGINE = "fa2993e3-8b2a-40a6-8853-ac2db2daed5a"; + public static readonly Guid ClassId = new Guid(CLSID_ENGINE); + + const string ID_ENGINE = "86102a1b-4378-4964-a7ed-21852a8afb7f"; + public static readonly Guid Id = new Guid(ID_ENGINE); + + public IDebugEventCallback2 Callback { get; private set; } + + public FileSystem FileSystem { get; private set; } + + int IDebugEventCallback2.Event( + IDebugEngine2 pEngine, + IDebugProcess2 pProcess, + IDebugProgram2 pProgram, + IDebugThread2 pThread, + IDebugEvent2 pEvent, + ref Guid riidEvent, + uint dwAttrib) + { + if (Callback == null) + return VSConstants.S_OK; + return Callback.Event(pEngine, pProcess, + pProgram, pThread, pEvent, ref riidEvent, dwAttrib); + } + + public QmlEngine() + { + FileSystem = FileSystem.Create(); + } + + Dictionary<Guid, Program> programs = new Dictionary<Guid, Program>(); + public IEnumerable<Program> Programs + { + get { return ThreadSafe(() => programs.Values.ToList()); } + } + + HashSet<PendingBreakpoint> pendingBreakpoints = new HashSet<PendingBreakpoint>(); + public IEnumerable<PendingBreakpoint> PendingBreakpoints + { + get { return ThreadSafe(() => pendingBreakpoints.ToList()); } + } + + int IDebugEngine2.GetEngineId(out Guid pguidEngine) + { + pguidEngine = Id; + return VSConstants.S_OK; + } + + int IDebugEngineLaunch2.LaunchSuspended(string pszServer, IDebugPort2 pPort, + string pszExe, string pszArgs, string pszDir, string bstrEnv, string pszOptions, + enum_LAUNCH_FLAGS dwLaunchFlags, uint hStdInput, uint hStdOutput, uint hStdError, + IDebugEventCallback2 pCallback, out IDebugProcess2 ppProcess) + { + ppProcess = null; + + if (string.IsNullOrEmpty(pszOptions)) + return VSConstants.E_FAIL; + + uint procId; + if (!uint.TryParse(pszOptions, out procId)) + return VSConstants.E_FAIL; + + var env = bstrEnv.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => x.Split(new[] { '=' })) + .Where(x => x.Length >= 2) + .ToDictionary(x => x[0], x => x[1].Split(new[] { ';' })); + + if (env.ContainsKey("QTRCC")) { + foreach (var rccFile in env["QTRCC"]) + FileSystem.RegisterRccFile(rccFile); + } + + IDebugProcess2 nativeProc; + var nativeProcId = new AD_PROCESS_ID + { + ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_SYSTEM, + dwProcessId = procId + }; + if (pPort.GetProcess(nativeProcId, out nativeProc) != VSConstants.S_OK) + return VSConstants.E_FAIL; + + var program = Program.Create(this, nativeProc, pszExe, pszArgs); + if (program == null) + return VSConstants.E_FAIL; + + programs.Add(program.ProcessId, program); + ppProcess = program; + return VSConstants.S_OK; + } + + int IDebugEngineLaunch2.ResumeProcess(IDebugProcess2 process) + { + var program = process as Program; + if (program == null) + return VSConstants.E_FAIL; + + IDebugPort2 port; + if (process.GetPort(out port) != VSConstants.S_OK) + return VSConstants.E_FAIL; + + string portName; + port.GetPortName(out portName); + + Guid guidPort; + port.GetPortId(out guidPort); + + IDebugDefaultPort2 defaultPort = (IDebugDefaultPort2)port; + + IDebugPortNotify2 portNotify; + if (defaultPort.GetPortNotify(out portNotify) != VSConstants.S_OK) + return VSConstants.E_FAIL; + + if (portNotify.AddProgramNode(program) != VSConstants.S_OK) + return VSConstants.E_FAIL; + + return VSConstants.S_OK; + } + + int IDebugEngine2.Attach( + IDebugProgram2[] rgpPrograms, + IDebugProgramNode2[] rgpProgramNodes, + uint celtPrograms, + IDebugEventCallback2 pCallback, + enum_ATTACH_REASON dwReason) + { + var program = rgpProgramNodes[0] as Program; + if (program == null) + return VSConstants.E_FAIL; + + Callback = pCallback; + + DebugEvent.Send(new EngineCreateEvent(this)); + + Guid pguidProgramId; + if (rgpPrograms[0].GetProgramId(out pguidProgramId) != VSConstants.S_OK) + return VSConstants.E_FAIL; + + program.ProgramId = pguidProgramId; + + DebugEvent.Send(new ProgramCreateEvent(program)); + DebugEvent.Send(new ThreadCreateEvent(program)); + DebugEvent.Send(new LoadCompleteEvent(program)); + DebugEvent.Send(new EntryPointEvent(program)); + + program.OutputWriteLine("Connecting to the QML runtime..."); + + return VSConstants.S_OK; + } + + int IDebugEngineLaunch2.CanTerminateProcess(IDebugProcess2 pProcess) + { + Guid procId; + if (pProcess.GetProcessId(out procId) != VSConstants.S_OK) + return VSConstants.E_FAIL; + + Program program; + if (!programs.TryGetValue(procId, out program)) + return VSConstants.S_FALSE; + + return VSConstants.S_OK; + } + + public bool ProgramIsRunning(Program program) + { + return programs.ContainsKey(program.ProcessId); + } + + int IDebugEngineLaunch2.TerminateProcess(IDebugProcess2 pProcess) + { + Guid procId; + if (pProcess.GetProcessId(out procId) != VSConstants.S_OK) + return VSConstants.E_FAIL; + + Program program; + if (!programs.TryGetValue(procId, out program)) + return VSConstants.S_FALSE; + + programs.Remove(procId); + + DebugEvent.Send(new ThreadDestroyEvent(program, 0)); + DebugEvent.Send(new ProgramDestroyEvent(program, 0)); + + return VSConstants.S_OK; + } + + int IDebugEngine2.ContinueFromSynchronousEvent(IDebugEvent2 pEvent) + { + var evtProgramDestroy = pEvent as ProgramDestroyEvent; + if (evtProgramDestroy != null) + evtProgramDestroy.Program.Dispose(); + + return VSConstants.S_OK; + } + + int IDebugEngine2.CreatePendingBreakpoint( + IDebugBreakpointRequest2 pBPRequest, + out IDebugPendingBreakpoint2 ppPendingBP) + { + ppPendingBP = null; + + var pendingBreakpoint = PendingBreakpoint.Create(this, pBPRequest); + if (pendingBreakpoint == null) + return VSConstants.E_FAIL; + + ppPendingBP = pendingBreakpoint; + pendingBreakpoints.Add(pendingBreakpoint); + + return VSConstants.S_OK; + } + + public void DisposePendingBreakpoint(PendingBreakpoint pendingBreakpoint) + { + pendingBreakpoints.Remove(pendingBreakpoint); + pendingBreakpoint.Dispose(); + } + + public void OutputWriteLine(string msg) + { + DebugEvent.Send(new OutputStringEvent(this, msg + "\r\n")); + } + + #region //////////////////// Concurrent /////////////////////////////////////////////////// + + LocalConcurrent concurrent = new LocalConcurrent(); + class LocalConcurrent : Concurrent + { + public void LocalThreadSafe(Action action) + { ThreadSafe(action); } + + public T LocalThreadSafe<T>(Func<T> func) + { return ThreadSafe(func); } + + public new bool Atomic(Func<bool> test, Action action, Action actionElse = null) + { return base.Atomic(test, action, actionElse); } + } + + void ThreadSafe(Action action) + { + concurrent.LocalThreadSafe(action); + } + + T ThreadSafe<T>(Func<T> func) + { + return concurrent.LocalThreadSafe(func); + } + + bool Atomic(Func<bool> test, Action action, Action actionElse = null) + { + return concurrent.Atomic(test, action, actionElse); + } + + #endregion //////////////////// Concurrent //////////////////////////////////////////////// + } + + [ComVisible(true)] + [Guid(CLSID_PROGRAMPROVIDER)] + sealed partial class ProgramProvider : + + IDebugProgramProvider2 // "This registered interface allows the session debug manager (SDM) + // to obtain information about programs that have been "published" + // through the IDebugProgramPublisher2 interface." + { + public const string CLSID_PROGRAMPROVIDER = "f2ff34e2-7fa5-461b-9e59-b5997ee0a637"; + public static readonly Guid ClassId = new Guid(CLSID_PROGRAMPROVIDER); + + public ProgramProvider() + { } + } + + public static class NativeEngine + { + const string ID_NATIVEENGINE = "3b476d35-a401-11d2-aad4-00c04f990171"; + public static readonly Guid Id = new Guid(ID_NATIVEENGINE); + + const string ID_LANGUAGE_CPP = "3a12d0b7-c26c-11d0-b442-00a0244a1dd2"; + public static Guid IdLanguageCpp = new Guid(ID_LANGUAGE_CPP); + } + + public static class GdbEngine + { + const string ID_GDBENGINE = "ea6637c6-17df-45b5-a183-0951c54243bc"; + public static readonly Guid Id = new Guid(ID_GDBENGINE); + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Enums.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Enums.cs new file mode 100644 index 0000000..040f8c2 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Enums.cs @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + /// <summary> + /// Abstraction of AD7 enum interfaces, e.g. IEnumDebugPrograms2 + /// (cf. https://docs.microsoft.com/en-us/visualstudio/extensibility/debugger/reference/ienumdebugprograms2) + /// </summary> + /// + class Enum<T, TEnum, IEnum> + where TEnum : Enum<T, TEnum, IEnum>, new() + where IEnum : class + { + int index; + IList<T> list; + + public static TEnum Create(IEnumerable<T> data) + { + return new TEnum + { + index = 0, + list = new List<T>(data) + }; + } + + public static TEnum Create(T singleElement) + { + return new TEnum + { + index = 0, + list = new List<T>() { singleElement } + }; + } + + public static TEnum Create() + { + return new TEnum + { + index = 0, + list = new List<T>() + }; + } + + protected Enum() + { } + + /// <summary> + /// Returns the next set of elements from the enumeration. + /// </summary> + /// <param name="numElems">The number of elements to retrieve.</param> + /// <returns> + /// Collection of retrieved elements. + /// </returns> + public IEnumerable<T> Next(uint numElems) + { + int oldIndex = index; + int maxIndex = Math.Min(list.Count, oldIndex + (int)numElems); + for (; index < maxIndex; ++index) + yield return list[index]; + } + + /// <summary> + /// Returns the next set of elements from the enumeration. + /// </summary> + /// <param name="numElems"> + /// The number of elements to retrieve. + /// </param> + /// <param name="elems"> + /// Array of elements to be filled in. + /// </param> + /// <param name="numElemsFetched"> + /// Returns the number of elements actually returned in elems. + /// </param> + /// <returns> + /// If successful, returns S_OK. Returns S_FALSE if fewer than the requested number of + /// elements could be returned. + /// </returns> + public int Next(uint numElems, T[] elems, ref uint numElemsFetched) + { + var next = Next(numElems).ToArray(); + Array.Copy(next, elems, next.Length); + numElemsFetched = (uint)next.Length; + if (numElemsFetched < numElems) + return VSConstants.S_FALSE; + return VSConstants.S_OK; + } + + /// <summary> + /// Skips over the specified number of elements. + /// </summary> + /// <param name="numElems">Number of elements to skip.</param> + /// <returns> + /// If successful, returns S_OK. Returns S_FALSE if numElems is greater than the number of + /// remaining elements; otherwise, returns an error code. + /// </returns> + /// <remarks> + /// If numElems specifies a value greater than the number of remaining elements, the + /// enumeration is set to the end and S_FALSE is returned. + /// </remarks> + public int Skip(uint numElems) + { + if ((ulong)index + numElems > Int32.MaxValue) + return VSConstants.E_INVALIDARG; + if (index + numElems > list.Count) { + index = list.Count; + return VSConstants.S_FALSE; + } + + index += (int)numElems; + return VSConstants.S_OK; + } + + /// <summary> + /// Resets the enumeration to the first element. + /// </summary> + /// <returns> + /// If successful, returns S_OK; otherwise, returns an error code. + /// </returns> + public int Reset() + { + index = 0; + return VSConstants.S_OK; + } + + /// <summary> + /// Returns the number of elements in the enumeration. + /// </summary> + /// <param name="numElems">Returns the number of elements in the enumeration.</param> + /// <returns> + /// If successful, returns S_OK; otherwise, returns an error code. + /// </returns> + public int GetCount(out uint numElems) + { + numElems = (uint)list.Count; + return VSConstants.S_OK; + } + + /// <summary> + /// Returns a copy of the current enumeration as a separate object. + /// </summary> + /// <param name="clonedEnum">Returns the clone of this enumeration.</param> + /// <returns> + /// If successful, returns S_OK; otherwise, returns an error code. + /// </returns> + /// <remarks> + /// The copy of the enumeration has the same state as the original at the time this method + /// is called. However, the copy's and the original's states are separate and can be + /// changed individually. + /// </remarks> + public int Clone(out IEnum clonedEnum) + { + var clone = new TEnum(); + clone.index = index; + clone.list = new List<T>(list); + clonedEnum = clone as IEnum; + return VSConstants.S_OK; + } + } + + class ProgramEnum : + Enum<IDebugProgram2, ProgramEnum, IEnumDebugPrograms2>, + IEnumDebugPrograms2 + { } + + class FrameInfoEnum : + Enum<FRAMEINFO, FrameInfoEnum, IEnumDebugFrameInfo2>, + IEnumDebugFrameInfo2 + { } + + class ThreadEnum : + Enum<IDebugThread2, ThreadEnum, IEnumDebugThreads2>, + IEnumDebugThreads2 + { } + + class ModuleEnum : + Enum<IDebugModule2, ModuleEnum, IEnumDebugModules2>, + IEnumDebugModules2 + { } + + class CodeContextEnum : + Enum<IDebugCodeContext2, CodeContextEnum, IEnumDebugCodeContexts2>, + IEnumDebugCodeContexts2 + { } + + class BoundBreakpointsEnum : + Enum<IDebugBoundBreakpoint2, BoundBreakpointsEnum, IEnumDebugBoundBreakpoints2>, + IEnumDebugBoundBreakpoints2 + { } + + class ErrorBreakpointsEnum : + Enum<IDebugErrorBreakpoint2, ErrorBreakpointsEnum, IEnumDebugErrorBreakpoints2>, + IEnumDebugErrorBreakpoints2 + { } + + class PropertyEnum : + Enum<DEBUG_PROPERTY_INFO, PropertyEnum, IEnumDebugPropertyInfo2>, + IEnumDebugPropertyInfo2 + { + public int Next(uint celt, DEBUG_PROPERTY_INFO[] rgelt, out uint pceltFetched) + { + pceltFetched = 0; + return Next(celt, rgelt, ref pceltFetched); + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Events.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Events.cs new file mode 100644 index 0000000..9c75bc5 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Events.cs @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; +using System; + +namespace QtVsTools.Qml.Debug.AD7 +{ + abstract class DebugEvent : + + IDebugEvent2 // "This interface is used to communicate both critical debug information, + // such as stopping at a breakpoint, and non-critical information, such as a + // debugging message." + { + public Guid InterfaceId { get; protected set; } + public uint Attributes { get; protected set; } + + protected const uint ASYNCHRONOUS = (uint)enum_EVENTATTRIBUTES.EVENT_ASYNCHRONOUS; + protected const uint STOPPING = (uint)enum_EVENTATTRIBUTES.EVENT_ASYNC_STOP; + protected const uint SYNCHRONOUS = (uint)enum_EVENTATTRIBUTES.EVENT_SYNCHRONOUS; + protected const uint SYNCHRONOUS_STOPPING = + (uint)enum_EVENTATTRIBUTES.EVENT_STOPPING + | (uint)enum_EVENTATTRIBUTES.EVENT_SYNCHRONOUS; + + protected QmlEngine Engine { get; set; } + protected IDebugProgram2 Program { get; set; } + protected IDebugThread2 Thread { get; set; } + protected IDebugEventCallback2 Callback { get; set; } + + protected DebugEvent( + QmlEngine engine, + Guid interfaceId, + uint attributes, + IDebugProgram2 program = null, + IDebugThread2 thread = null, + IDebugEventCallback2 callback = null) + { + InterfaceId = interfaceId; + Attributes = attributes; + Engine = engine; + Program = program; + Thread = thread; + Callback = (callback != null) ? callback : engine; + } + + protected void Send() + { + if (Callback != null) { + var interfaceId = InterfaceId; + Callback.Event( + Engine, null, Program, Thread, + this, ref interfaceId, Attributes); + } + } + + public int /*IDebugEvent2*/ GetAttributes(out uint eventAttributes) + { + eventAttributes = Attributes; + return VSConstants.S_OK; + } + + public static void Send(DebugEvent debugEvent) + { + debugEvent.Send(); + } + } + + class EngineCreateEvent : DebugEvent, IDebugEngineCreateEvent2 + { + public EngineCreateEvent(QmlEngine engine) + : base(engine, typeof(IDebugEngineCreateEvent2).GUID, ASYNCHRONOUS) + { } + + int IDebugEngineCreateEvent2.GetEngine(out IDebugEngine2 pEngine) + { + pEngine = Engine as IDebugEngine2; + return VSConstants.S_OK; + } + } + + class ProgramCreateEvent : DebugEvent, IDebugProgramCreateEvent2 + { + public ProgramCreateEvent(Program program) + : base(program.Engine, typeof(IDebugProgramCreateEvent2).GUID, + ASYNCHRONOUS, program) + { } + } + + class ProgramDestroyEvent : DebugEvent, IDebugProgramDestroyEvent2 + { + uint exitCode; + public new Program Program { get; private set; } + + public ProgramDestroyEvent(Program program, uint exitCode) + : base(program.Engine, typeof(IDebugProgramDestroyEvent2).GUID, + SYNCHRONOUS, program) + { + Program = program; + this.exitCode = exitCode; + } + + int IDebugProgramDestroyEvent2.GetExitCode(out uint exitCode) + { + exitCode = this.exitCode; + return VSConstants.S_OK; + } + } + + class ThreadCreateEvent : DebugEvent, IDebugThreadCreateEvent2 + { + public ThreadCreateEvent(Program program) + : base(program.Engine, typeof(IDebugThreadCreateEvent2).GUID, + ASYNCHRONOUS, program, program) + { } + } + + class ThreadDestroyEvent : DebugEvent, IDebugThreadDestroyEvent2 + { + uint exitCode; + + public ThreadDestroyEvent(Program program, uint exitCode) + : base(program.Engine, typeof(IDebugThreadDestroyEvent2).GUID, + SYNCHRONOUS, program, program) + { + this.exitCode = exitCode; + } + + int IDebugThreadDestroyEvent2.GetExitCode(out uint exitCode) + { + exitCode = this.exitCode; + return VSConstants.S_OK; + } + } + + class LoadCompleteEvent : DebugEvent, IDebugLoadCompleteEvent2 + { + public LoadCompleteEvent(Program program) + : base(program.Engine, typeof(IDebugLoadCompleteEvent2).GUID, + STOPPING, program, program) + { } + } + + class EntryPointEvent : DebugEvent, IDebugEntryPointEvent2 + { + public EntryPointEvent(Program program) + : base(program.Engine, typeof(IDebugEntryPointEvent2).GUID, + STOPPING, program, program) + { } + } + + class BreakpointBoundEvent : DebugEvent, IDebugBreakpointBoundEvent2 + { + public Breakpoint Breakpoint { get; private set; } + public BreakpointBoundEvent(Breakpoint breakpoint) + : base(breakpoint.Program.Engine, typeof(IDebugBreakpointBoundEvent2).GUID, + ASYNCHRONOUS, breakpoint.Program, breakpoint.Program) + { + Breakpoint = breakpoint; + } + + int IDebugBreakpointBoundEvent2.GetPendingBreakpoint( + out IDebugPendingBreakpoint2 ppPendingBP) + { + ppPendingBP = Breakpoint.Parent; + return VSConstants.S_OK; + } + + int IDebugBreakpointBoundEvent2.EnumBoundBreakpoints( + out IEnumDebugBoundBreakpoints2 ppEnum) + { + ppEnum = BoundBreakpointsEnum.Create(Breakpoint); + return VSConstants.S_OK; + } + } + + class BreakpointEvent : DebugEvent, IDebugBreakpointEvent2 + { + IEnumDebugBoundBreakpoints2 boundBreakpoints; + + public BreakpointEvent(Program program, + IEnumDebugBoundBreakpoints2 boundBreakpoints) + : base(program.Engine, typeof(IDebugBreakpointEvent2).GUID, + STOPPING, program, program) + { + this.boundBreakpoints = boundBreakpoints; + } + + int IDebugBreakpointEvent2.EnumBreakpoints(out IEnumDebugBoundBreakpoints2 ppEnum) + { + ppEnum = boundBreakpoints; + return VSConstants.S_OK; + } + } + + class StepCompleteEvent : DebugEvent, IDebugStepCompleteEvent2 + { + public StepCompleteEvent(Program program) + : base(program.Engine, typeof(IDebugStepCompleteEvent2).GUID, + STOPPING, program, program) + { } + } + + class ExpressionEvaluationCompleteEvent : DebugEvent, IDebugExpressionEvaluationCompleteEvent2 + { + public Expression Expression { get; private set; } + public Property Property { get; private set; } + + public ExpressionEvaluationCompleteEvent( + IDebugEventCallback2 callback, + Expression expression, + Property property) + : base(expression.Engine, typeof(IDebugExpressionEvaluationCompleteEvent2).GUID, + SYNCHRONOUS, expression.Program, expression.Program, callback) + { + Expression = expression; + Property = property; + } + + int IDebugExpressionEvaluationCompleteEvent2.GetExpression(out IDebugExpression2 ppExpr) + { + ppExpr = Expression; + return VSConstants.S_OK; + } + + int IDebugExpressionEvaluationCompleteEvent2.GetResult(out IDebugProperty2 ppResult) + { + ppResult = Property; + return VSConstants.S_OK; + } + } + + class OutputStringEvent : DebugEvent, IDebugOutputStringEvent2 + { + string outputString; + + public OutputStringEvent(QmlEngine engine, string outputString) + : base(engine, typeof(IDebugOutputStringEvent2).GUID, ASYNCHRONOUS) + { + this.outputString = outputString; + } + + int IDebugOutputStringEvent2.GetString(out string pbstrString) + { + pbstrString = outputString; + return VSConstants.S_OK; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Expression.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Expression.cs new file mode 100644 index 0000000..57f2e8b --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Expression.cs @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + using V4; + + sealed partial class Expression : + + IDebugExpression2 // "This interface represents a parsed expression ready for binding + // and evaluating." + { + public string ExpressionString { get; private set; } + + public StackFrame StackFrame { get; private set; } + public QmlEngine Engine { get; private set; } + public Program Program { get; private set; } + public QmlDebugger Debugger { get; private set; } + public CodeContext CodeContext { get; private set; } + + public static Expression Create(StackFrame frame, string expr) + { + return new Expression + { + ExpressionString = expr, + StackFrame = frame, + Engine = frame.Engine, + Program = frame.Program, + Debugger = frame.Debugger, + CodeContext = frame.Context, + }; + } + + private Expression() + { } + + int IDebugExpression2.EvaluateSync( + enum_EVALFLAGS dwFlags, + uint dwTimeout, + IDebugEventCallback2 pExprCallback, + out IDebugProperty2 ppResult) + { + ppResult = null; + var value = Debugger.Evaluate(StackFrame.FrameNumber, ExpressionString); + if (value == null || value is JsError) + return VSConstants.S_FALSE; + + Program.Refresh(); + + value.Name = ExpressionString; + ppResult = Property.Create(StackFrame, 0, value); + return VSConstants.S_OK; + } + + int IDebugExpression2.EvaluateAsync( + enum_EVALFLAGS dwFlags, + IDebugEventCallback2 pExprCallback) + { + Task.Run(() => + { + var value = Debugger.Evaluate(StackFrame.FrameNumber, ExpressionString); + if (value != null) + value.Name = ExpressionString; + + Program.Refresh(); + + DebugEvent.Send(new ExpressionEvaluationCompleteEvent( + pExprCallback, this, Property.Create(StackFrame, 0, value))); + }); + return VSConstants.S_OK; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7InfoHelpers.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7InfoHelpers.cs new file mode 100644 index 0000000..312aa39 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7InfoHelpers.cs @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.Debugger.Interop; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace QtVsTools.Qml.Debug.AD7 +{ + class InfoHelper<TDerived> + where TDerived : InfoHelper<TDerived> + { + public class Ref<TStruct> + { + public TStruct s = default(TStruct); + } + + public abstract class MapField<TStruct, TFieldMask> + { + public TFieldMask FieldMaskBitCheck { get; set; } + public TFieldMask FieldMaskBitUpdate { get; set; } + public abstract void Map(TDerived infoObj, Ref<TStruct> infoStruct); + } + + protected class MapField<TStruct, TFieldMask, T1, T2> : MapField<TStruct, TFieldMask> + { + public Func<TDerived, T1> FieldValue { get; set; } + public Func<T1, T2> Convert { get; set; } + public Func<T1, bool> IsNull { get; set; } + public Action<Ref<TStruct>, T2> MapToStruct { get; set; } + + public override void Map(TDerived infoObj, Ref<TStruct> infoStruct) + { + if (FieldValue == null || MapToStruct == null) + return; + + T1 fieldValue = FieldValue(infoObj); + if (IsNull(fieldValue)) + return; + + MapToStruct(infoStruct, Convert(fieldValue)); + } + } + + public abstract class Mapping + { } + + public class Mapping<TStruct, TFieldMask> : Mapping, + IEnumerable<MapField<TStruct, TFieldMask>> + { + List<MapField<TStruct, TFieldMask>> fieldMaps; + + protected static Action<Ref<TStruct>, TFieldMask> UpdateMask { get; set; } + + public Mapping(Action<Ref<TStruct>, TFieldMask> updateMask) + { + fieldMaps = new List<MapField<TStruct, TFieldMask>>(); + UpdateMask = updateMask; + } + + public void Add<T>( + TFieldMask fieldMaskBit, + Action<Ref<TStruct>, T> mapToStruct, + Func<TDerived, T> fieldValue) + where T : class + { + Add(fieldMaskBit, fieldMaskBit, mapToStruct, fieldValue); + } + + public void Add<T>( + TFieldMask fieldMaskBitCheck, + TFieldMask fieldMaskBitUpdate, + Action<Ref<TStruct>, T> mapToStruct, + Func<TDerived, T> fieldValue) + where T : class + { + fieldMaps.Add(new MapField<TStruct, TFieldMask, T, T> + { + FieldMaskBitCheck = fieldMaskBitCheck, + FieldMaskBitUpdate = fieldMaskBitUpdate, + FieldValue = fieldValue, + MapToStruct = mapToStruct, + IsNull = (x => x == null), + Convert = (x => x) + }); + } + + public void Add<T>( + TFieldMask fieldMaskBit, + Action<Ref<TStruct>, T> mapToStruct, + Func<TDerived, T?> fieldValue) + where T : struct + { + Add(fieldMaskBit, fieldMaskBit, mapToStruct, fieldValue); + } + + public void Add<T>( + TFieldMask fieldMaskBitCheck, + TFieldMask fieldMaskBitUpdate, + Action<Ref<TStruct>, T> mapToStruct, + Func<TDerived, T?> fieldValue) + where T : struct + { + fieldMaps.Add(new MapField<TStruct, TFieldMask, T?, T> + { + FieldMaskBitCheck = fieldMaskBitCheck, + FieldMaskBitUpdate = fieldMaskBitUpdate, + FieldValue = fieldValue, + MapToStruct = mapToStruct, + IsNull = (x => x == null), + Convert = (x => (x.HasValue ? x.Value : default(T))) + }); + } + + public IEnumerator<MapField<TStruct, TFieldMask>> GetEnumerator() + { + return fieldMaps.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Map(TDerived infoObj, TFieldMask fieldMask, out TStruct infoStruct) + { + infoStruct = default(TStruct); + var r = new Ref<TStruct>(); + + foreach (var mapping in this) { + if (!MaskHasValue(fieldMask, mapping.FieldMaskBitCheck)) + continue; + mapping.Map(infoObj, r); + UpdateMask(r, mapping.FieldMaskBitUpdate); + } + infoStruct = r.s; + } + + protected virtual bool MaskHasValue(TFieldMask fieldMask, TFieldMask fieldMaskBit) + { + if (typeof(TFieldMask).IsEnum) { + var enumFieldMask = fieldMask as Enum; + var enumFieldMaskBit = fieldMaskBit as Enum; + return enumFieldMask.HasFlag(enumFieldMaskBit); + } + + try { + var intFieldMask = Convert.ToUInt64(fieldMask); + var intFieldMaskBit = Convert.ToUInt64(fieldMaskBit); + return (intFieldMask & intFieldMaskBit) != 0; + + } catch { + return false; + } + } + } + + public void Map<TStruct, TFieldMask>( + Mapping mapping, + TFieldMask fieldMask, + out TStruct infoStruct) + { + var mappingToStruct = mapping as Mapping<TStruct, TFieldMask>; + if (mappingToStruct != null) + mappingToStruct.Map(this as TDerived, fieldMask, out infoStruct); + else + infoStruct = default(TStruct); + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Program.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Program.cs new file mode 100644 index 0000000..6a7cee4 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Program.cs @@ -0,0 +1,602 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Windows.Threading; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using QtVsTools.VisualStudio; + +namespace QtVsTools.Qml.Debug.AD7 +{ + sealed partial class Program : Disposable, IDebuggerEventSink, + + IDebugProgramNode2, // "This interface represents a program that can be debugged." + + IDebugProgram3, // "This interface represents a program that is running in a process." + + IDebugProcess2, // "This interface represents a process running on a port. If the + // port is the local port, then IDebugProcess2 usually represents + // a physical process on the local machine." + + IDebugThread2, // "This interface represents a thread running in a program." + IDebugThread100, + + IDebugModule3, // "This interface represents a module -- that is, an executable unit + // of a program -- such as a DLL." + + IDebugEventCallback2 // "This interface is used by the debug engine (DE) to send debug + // events to the session debug manager (SDM)." + { + public QmlDebugger Debugger { get; private set; } + + public QmlEngine Engine { get; private set; } + + public List<StackFrame> CurrentFrames { get; private set; } + + public const string Name = "QML Debugger"; + public Guid ProcessId { get; private set; } + public Guid ProgramId { get; set; } + public IDebugProcess2 NativeProc { get; private set; } + public uint NativeProcId { get; private set; } + public string ExecPath { get; private set; } + public string ExecArgs { get; private set; } + public IVsDebugger VsDebugger { get; private set; } + Dispatcher vsDebuggerThreadDispatcher; + + private readonly static object criticalSectionGlobal = new object(); + static bool originalBreakAllProcesses = BreakAllProcesses; + static int runningPrograms = 0; + + public static Program Create( + QmlEngine engine, + IDebugProcess2 nativeProc, + string execPath, + string execArgs) + { + var _this = new Program(); + return _this.Initialize(engine, nativeProc, execPath, execArgs) ? _this : null; + } + + private Program() + { } + + private bool Initialize( + QmlEngine engine, + IDebugProcess2 nativeProc, + string execPath, + string execArgs) + { + Engine = engine; + NativeProc = nativeProc; + + var nativeProcId = new AD_PROCESS_ID[1]; + nativeProc.GetPhysicalProcessId(nativeProcId); + NativeProcId = nativeProcId[0].dwProcessId; + + ExecPath = execPath; + ExecArgs = execArgs; + + Debugger = QmlDebugger.Create(this, execPath, execArgs); + if (Debugger == null) + return false; + + VsDebugger = VsServiceProvider.GetService<IVsDebugger>(); + if (VsDebugger != null) + VsDebugger.AdviseDebugEventCallback(this as IDebugEventCallback2); + vsDebuggerThreadDispatcher = Dispatcher.CurrentDispatcher; + + ProcessId = Guid.NewGuid(); + CurrentFrames = new List<StackFrame>(); + + lock (criticalSectionGlobal) { + if (runningPrograms == 0) + originalBreakAllProcesses = BreakAllProcesses; + runningPrograms++; + } + + return true; + } + + public override bool CanDispose + { + get + { + return !Engine.ProgramIsRunning(this); + } + } + + protected override void DisposeManaged() + { + Debugger.Dispose(); + if (VsDebugger != null) + VsDebugger.UnadviseDebugEventCallback(this as IDebugEventCallback2); + + lock (criticalSectionGlobal) { + runningPrograms--; + if (runningPrograms == 0) + BreakAllProcesses = originalBreakAllProcesses; + } + } + + public void OutputWriteLine(string msg) + { + var execFileName = Path.GetFileName(ExecPath); + Engine.OutputWriteLine(string.Format("'{0}' (QML): {1}", execFileName, msg)); + } + + bool IDebuggerEventSink.QueryRuntimeFrozen() + { + var debugMode = new DBGMODE[1]; + int res = VSConstants.S_FALSE; + vsDebuggerThreadDispatcher + .BeginInvoke(new Action(() => res = VsDebugger.GetMode(debugMode)), new object[0]) + .Wait(); + + if (res != VSConstants.S_OK) + return false; + return (debugMode[0] != DBGMODE.DBGMODE_Run); + } + + void IDebuggerEventSink.NotifyError(string errorMessage) + { + OutputWriteLine(errorMessage); + } + + int IDebugEventCallback2.Event( + IDebugEngine2 pEngine, + IDebugProcess2 pProcess, + IDebugProgram2 pProgram, + IDebugThread2 pThread, + IDebugEvent2 pEvent, + ref Guid riidEvent, + uint dwAttrib) + { + if (pEngine == Engine) + return VSConstants.S_OK; + + if (pProcess == null && pProgram == null) + return VSConstants.S_OK; + + if (pProcess == null) { + if (pProgram.GetProcess(out pProcess) != VSConstants.S_OK || pProcess == null) + return VSConstants.S_OK; + } + + var pProcessId = new AD_PROCESS_ID[1]; + if (pProcess.GetPhysicalProcessId(pProcessId) != VSConstants.S_OK) + return VSConstants.S_OK; + + if (pProcessId[0].dwProcessId != NativeProcId) + return VSConstants.S_OK; + + if (riidEvent == typeof(IDebugProgramDestroyEvent2).GUID) + TerminateProcess(); + + return VSConstants.S_OK; + } + + void IDebuggerEventSink.NotifyClientDisconnected() + { + TerminateProcess(); + } + + bool terminated = false; + void TerminateProcess() + { + if (!terminated) { + terminated = true; + var engineLaunch = Engine as IDebugEngineLaunch2; + engineLaunch.TerminateProcess(this as IDebugProcess2); + } + } + + + #region //////////////////// Execution Control //////////////////////////////////////////// + + public int /*IDebugProgram3*/ Continue(IDebugThread2 pThread) + { + Debugger.Run(); + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ ExecuteOnThread(IDebugThread2 pThread) + { + Debugger.Run(); + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ Step( + IDebugThread2 pThread, + enum_STEPKIND sk, + enum_STEPUNIT Step) + { + if (sk == enum_STEPKIND.STEP_OVER) + Debugger.StepOver(); + else if (sk == enum_STEPKIND.STEP_INTO) + Debugger.StepInto(); + else if (sk == enum_STEPKIND.STEP_OUT) + Debugger.StepOut(); + else + return VSConstants.E_FAIL; + return VSConstants.S_OK; + } + + void IDebuggerEventSink.NotifyBreak() + { + BreakAllProcesses = false; + DebugEvent.Send(new StepCompleteEvent(this)); + } + + #endregion //////////////////// Execution Control ///////////////////////////////////////// + + + #region //////////////////// Breakpoints ////////////////////////////////////////////////// + + public void SetBreakpoint(Breakpoint breakpoint) + { + Debugger.SetBreakpoint(breakpoint); + } + + public void NotifyBreakpointSet(Breakpoint breakpoint) + { + DebugEvent.Send(new BreakpointBoundEvent(breakpoint)); + } + + public void ClearBreakpoint(Breakpoint breakpoint) + { + Debugger.ClearBreakpoint(breakpoint); + } + + public void NotifyBreakpointCleared(Breakpoint breakpoint) + { + breakpoint.Parent.DisposeBreakpoint(breakpoint); + } + + public void NotifyBreakpointHit(Breakpoint breakpoint) + { + BreakAllProcesses = false; + DebugEvent.Send(new BreakpointEvent(this, BoundBreakpointsEnum.Create(breakpoint))); + } + + static bool BreakAllProcesses + { + get + { + return ((bool)QtVsToolsPackage.Instance.Dte + .Properties["Debugging", "General"] + .Item("BreakAllProcesses") + .Value); + } + set + { + QtVsToolsPackage.Instance.Dte + .Properties["Debugging", "General"] + .Item("BreakAllProcesses") + .let_Value(value ? "True" : "False"); + } + } + + #endregion //////////////////// Breakpoints /////////////////////////////////////////////// + + + #region //////////////////// Call Stack /////////////////////////////////////////////////// + + void IDebuggerEventSink.NotifyStackContext(IList<FrameInfo> frames) + { + CurrentFrames.Clear(); + foreach (var frame in frames) { + CurrentFrames.Add(StackFrame.Create(frame.Name, frame.Number, frame.Scopes, + CodeContext.Create(Engine, this, + Engine.FileSystem[frame.QrcPath].FilePath, (uint)frame.Line))); + } + } + + public void Refresh() + { + CurrentFrames.ForEach(x => x.Refresh()); + } + + int IDebugThread2.EnumFrameInfo( + enum_FRAMEINFO_FLAGS dwFieldSpec, + uint nRadix, + out IEnumDebugFrameInfo2 ppEnum) + { + ppEnum = null; + + if (CurrentFrames == null || CurrentFrames.Count == 0) { + ppEnum = FrameInfoEnum.Create(); + return VSConstants.S_OK; + } + + var frameInfos = new List<FRAMEINFO>(); + foreach (var frame in CurrentFrames) { + var frameInfo = new FRAMEINFO[1]; + (frame as IDebugStackFrame2).GetInfo(dwFieldSpec, nRadix, frameInfo); + frameInfos.Add(frameInfo[0]); + } + + ppEnum = FrameInfoEnum.Create(frameInfos); + return VSConstants.S_OK; + } + + #endregion //////////////////// Call Stack //////////////////////////////////////////////// + + + #region //////////////////// Info ///////////////////////////////////////////////////////// + + class ProgramInfo : InfoHelper<ProgramInfo> + { + public uint? ThreadId { get; set; } + public uint? SuspendCount { get; set; } + public uint? ThreadState { get; set; } + public string Priority { get; set; } + public string Name { get; set; } + public string Location { get; set; } + public string DisplayName { get; set; } + public uint? DisplayNamePriority { get; set; } + public uint? ThreadCategory { get; set; } + public uint? AffinityMask { get; set; } + public int? PriorityId { get; set; } + public string ModuleName { get; set; } + public string ModuleUrl { get; set; } + } + + ProgramInfo Info + { + get + { + return new ProgramInfo + { + ThreadId = Debugger.ThreadId, + SuspendCount = 0, + ThreadCategory = 0, + AffinityMask = 0, + PriorityId = 0, + ThreadState = (uint)enum_THREADSTATE.THREADSTATE_RUNNING, + Priority = "Normal", + Location = "", + Name = Name, + DisplayName = Name, + DisplayNamePriority = 10, // Give this display name a higher priority + // than the default (0) so that it will + // actually be displayed + ModuleName = Path.GetFileName(ExecPath), + ModuleUrl = ExecPath + + }; + } + } + + static readonly ProgramInfo.Mapping MappingToTHREADPROPERTIES = + + #region //////////////////// THREADPROPERTIES <-- ProgramInfo ///////////////////////////// + // r: Ref<THREADPROPERTIES> + // f: enum_THREADPROPERTY_FIELDS + // i: ProgramInfo + // v: value of i.<<property>> + + new ProgramInfo.Mapping<THREADPROPERTIES, enum_THREADPROPERTY_FIELDS> + ((r, f) => r.s.dwFields |= f) + { + { enum_THREADPROPERTY_FIELDS.TPF_ID, + (r, v) => r.s.dwThreadId = v, i => i.ThreadId }, + + { enum_THREADPROPERTY_FIELDS.TPF_SUSPENDCOUNT, + (r, v) => r.s.dwSuspendCount = v, i => i.SuspendCount }, + + { enum_THREADPROPERTY_FIELDS.TPF_STATE, + (r, v) => r.s.dwThreadState = v, i => i.ThreadState }, + + { enum_THREADPROPERTY_FIELDS.TPF_PRIORITY, + (r, v) => r.s.bstrPriority = v, i => i.Priority }, + + { enum_THREADPROPERTY_FIELDS.TPF_NAME, + (r, v) => r.s.bstrName = v, i => i.Name }, + + { enum_THREADPROPERTY_FIELDS.TPF_LOCATION, + (r, v) => r.s.bstrLocation = v, i => i.Location }, + }; + + #endregion //////////////////// THREADPROPERTIES <-- ProgramInfo ////////////////////////// + + + static readonly ProgramInfo.Mapping MappingToTHREADPROPERTIES100 = + + #region //////////////////// THREADPROPERTIES100 <-- ProgramInfo ////////////////// + // r: Ref<THREADPROPERTIES100> + // f: enum_THREADPROPERTY_FIELDS100 + // i: ProgramInfo + // v: value of i.<<property>> + + new ProgramInfo.Mapping<THREADPROPERTIES100, enum_THREADPROPERTY_FIELDS100> + ((r, f) => r.s.dwFields |= (uint)f) + { + { enum_THREADPROPERTY_FIELDS100.TPF100_ID, + (r, v) => r.s.dwThreadId = v, i => i.ThreadId }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_SUSPENDCOUNT, + (r, v) => r.s.dwSuspendCount = v, i => i.SuspendCount }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_STATE, + (r, v) => r.s.dwThreadState = v, i => i.ThreadState }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_PRIORITY, + (r, v) => r.s.bstrPriority = v, i => i.Priority }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_NAME, + (r, v) => r.s.bstrName = v, i => i.Name }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_LOCATION, + (r, v) => r.s.bstrLocation = v, i => i.Location }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_DISPLAY_NAME, + (r, v) => r.s.bstrDisplayName = v, i => i.DisplayName }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_DISPLAY_NAME, + enum_THREADPROPERTY_FIELDS100.TPF100_DISPLAY_NAME_PRIORITY, + (r, v) => r.s.DisplayNamePriority = v, i => i.DisplayNamePriority }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_CATEGORY, + (r, v) => r.s.dwThreadCategory = v, i => i.ThreadCategory }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_AFFINITY, + (r, v) => r.s.AffinityMask = v, i => i.AffinityMask }, + + { enum_THREADPROPERTY_FIELDS100.TPF100_PRIORITY_ID, + (r, v) => r.s.priorityId = v, i => i.PriorityId }, + }; + + #endregion //////////////////// THREADPROPERTIES100 <-- ProgramInfo /////////////////////// + + + static readonly ProgramInfo.Mapping MappingToMODULE_INFO = + + #region //////////////////// MODULE_INFO <-- ProgramInfo ////////////////////////////////// + // r: Ref<MODULE_INFO> + // f: enum_MODULE_INFO_FIELDS + // i: ProgramInfo + // v: value of i.<<property>> + + new ProgramInfo.Mapping<MODULE_INFO, enum_MODULE_INFO_FIELDS> + ((r, bit) => r.s.dwValidFields |= bit) + { + { enum_MODULE_INFO_FIELDS.MIF_NAME, + (r, v) => r.s.m_bstrName = v, i => i.ModuleName }, + + { enum_MODULE_INFO_FIELDS.MIF_URL, + (r, v) => r.s.m_bstrUrl = v, i => i.ModuleUrl }, + }; + + #endregion //////////////////// MODULE_INFO <-- ProgramInfo /////////////////////////////// + + + public int /*IDebugProgram3*/ GetName(out string pbstrName) + { + pbstrName = Program.Name; + return VSConstants.S_OK; + } + + int IDebugProgramNode2.GetProgramName(out string pbstrProgramName) + { + return GetName(out pbstrProgramName); + } + + int IDebugProgramNode2.GetEngineInfo(out string pbstrEngine, out Guid pguidEngine) + { + pbstrEngine = "QML"; + pguidEngine = QmlEngine.Id; + return VSConstants.S_OK; + } + + int IDebugProgramNode2.GetHostPid(AD_PROCESS_ID[] pHostProcessId) + { + pHostProcessId[0].ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; + pHostProcessId[0].guidProcessId = ProcessId; + return VSConstants.S_OK; + } + + int IDebugProcess2.GetPhysicalProcessId(AD_PROCESS_ID[] pProcessId) + { + pProcessId[0].ProcessIdType = (uint)enum_AD_PROCESS_ID.AD_PROCESS_ID_GUID; + pProcessId[0].guidProcessId = ProcessId; + return VSConstants.S_OK; + } + + int IDebugProcess2.GetProcessId(out Guid pguidProcessId) + { + pguidProcessId = ProcessId; + return VSConstants.S_OK; + } + + int IDebugProcess2.GetPort(out IDebugPort2 ppPort) + { + return NativeProc.GetPort(out ppPort); + } + + public int /*IDebugProgram3*/ GetProgramId(out Guid pguidProgramId) + { + pguidProgramId = ProgramId; + return VSConstants.S_OK; + } + + int IDebugThread2.GetThreadProperties( + enum_THREADPROPERTY_FIELDS dwFields, + THREADPROPERTIES[] ptp) + { + Info.Map(MappingToTHREADPROPERTIES, dwFields, out ptp[0]); + return VSConstants.S_OK; + } + + int IDebugThread100.GetThreadProperties100(uint dwFields, THREADPROPERTIES100[] ptp) + { + Info.Map(MappingToTHREADPROPERTIES100, dwFields, out ptp[0]); + return VSConstants.S_OK; + } + + int IDebugThread2.GetName(out string pbstrName) + { + pbstrName = Name; + return VSConstants.S_OK; + } + + int IDebugThread2.GetThreadId(out uint pdwThreadId) + { + pdwThreadId = (uint)Debugger.ThreadId; + return VSConstants.S_OK; + } + + public int /*IDebugModule3*/ GetInfo(enum_MODULE_INFO_FIELDS dwFields, MODULE_INFO[] pinfo) + { + Info.Map(MappingToMODULE_INFO, dwFields, out pinfo[0]); + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ EnumThreads(out IEnumDebugThreads2 ppEnum) + { + ppEnum = ThreadEnum.Create(this); + return VSConstants.S_OK; + } + + public int /*IDebugProgram3*/ EnumModules(out IEnumDebugModules2 ppEnum) + { + ppEnum = ModuleEnum.Create(this); + return VSConstants.S_OK; + } + + #endregion //////////////////// Info ////////////////////////////////////////////////////// + + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Property.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Property.cs new file mode 100644 index 0000000..c1e7c1f --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7Property.cs @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + using V4; + + sealed partial class Property : Concurrent, + + IDebugProperty2 // "This interface represents a stack frame property, a program document + // property, or some other property. The property is usually the result of + // an expression evaluation." + { + public QmlDebugger Debugger { get; private set; } + + public QmlEngine Engine { get; private set; } + public Program Program { get; private set; } + public StackFrame StackFrame { get; private set; } + public CodeContext CodeContext { get; private set; } + + public Property Parent { get; private set; } + public SortedDictionary<string, Property> Children { get; private set; } + + public int FrameNumber { get; private set; } + public int ScopeNumber { get; private set; } + public JsValue JsValue { get; private set; } + public string Name { get; private set; } + public string FullName { get; private set; } + public string Type { get; private set; } + public string Value { get; private set; } + + public static Property Create( + StackFrame frame, + int scopeNumber, + JsValue value, + Property parent = null) + { + var _this = new Property(); + return _this.Initialize(frame, scopeNumber, value, parent) ? _this : null; + } + + private Property() + { } + + private bool Initialize( + StackFrame frame, + int scopeNumber, + JsValue value, + Property parent) + { + StackFrame = frame; + Engine = frame.Engine; + Program = frame.Program; + Debugger = frame.Debugger; + CodeContext = frame.Context; + FrameNumber = frame.FrameNumber; + ScopeNumber = scopeNumber; + Parent = parent; + JsValue = value; + + if (Parent != null && Parent.JsValue is JsObject && ((JsObject)Parent.JsValue).IsArray) + Name = string.Format("[{0}]", JsValue.Name); + else + Name = JsValue.Name; + + var nameParts = new Stack<string>(new[] { Name }); + for (var p = Parent; p != null && !string.IsNullOrEmpty(p.Name); p = p.Parent) { + if (!nameParts.Peek().StartsWith("[")) + nameParts.Push("."); + nameParts.Push(p.Name); + } + FullName = string.Join("", nameParts); + + Type = JsValue.Type.ToString(); + Value = JsValue.ToString(); + + Children = new SortedDictionary<string, Property>(); + if (JsValue is JsObject) { + var obj = JsValue as JsObject; + foreach (JsValue objProp in obj.Properties.Where(x => x.HasData)) { + Children[GetChildKey(objProp.Name)] + = Create(StackFrame, ScopeNumber, objProp, this); + } + } + + return true; + } + + static string GetChildKey(string childName) + { + int childIndex; + if (int.TryParse(childName, out childIndex)) + return string.Format("{0:D9}", childIndex); + else + return childName; + } + + int IDebugProperty2.SetValueAsString(string pszValue, uint dwRadix, uint dwTimeout) + { + string expr = string.Format("{0}=({1})", FullName, pszValue); + + var value = Debugger.Evaluate(FrameNumber, expr); + if (value == null || value is JsError) + return VSConstants.S_FALSE; + + Program.Refresh(); + return VSConstants.S_OK; + } + + int IDebugProperty2.EnumChildren( + enum_DEBUGPROP_INFO_FLAGS dwFields, + uint dwRadix, + ref Guid guidFilter, + enum_DBG_ATTRIB_FLAGS dwAttribFilter, + string pszNameFilter, + uint dwTimeout, + out IEnumDebugPropertyInfo2 ppEnum) + { + ppEnum = null; + if (guidFilter != Guid.Empty && !Filter.LocalsSelected(ref guidFilter)) + return VSConstants.S_OK; + + if (JsValue is JsObjectRef) { + var obj = Debugger.Lookup(FrameNumber, ScopeNumber, JsValue as JsObjectRef); + if (obj == null) + return VSConstants.S_OK; + + JsValue = obj; + foreach (JsValue objProp in obj.Properties.Where(x => x.HasData)) { + Children[GetChildKey(objProp.Name)] + = Create(StackFrame, ScopeNumber, objProp, this); + } + } + + if (!Children.Any()) + return VSConstants.S_OK; + + ppEnum = PropertyEnum.Create(Children.Select(x => + { + var info = new DEBUG_PROPERTY_INFO[1]; + (x.Value as IDebugProperty2).GetPropertyInfo(dwFields, dwRadix, 0, + new IDebugReference2[0], 0, info); + return info[0]; + })); + return VSConstants.S_OK; + } + + + #region //////////////////// Info ///////////////////////////////////////////////////////// + + class PropertyInfo : InfoHelper<PropertyInfo> + { + public string FullName { get; set; } + public string Name { get; set; } + public string Type { get; set; } + public string Value { get; set; } + public enum_DBG_ATTRIB_FLAGS? Attribs { get; set; } + public IDebugProperty2 Property { get; set; } + } + + PropertyInfo Info + { + get + { + return new PropertyInfo + { + Name = Name, + FullName = FullName, + Type = Type, + Value = Value, + Property = this, + Attribs = ((Children.Any() || JsValue.Type == JsValue.DataType.Object) + ? enum_DBG_ATTRIB_FLAGS.DBG_ATTRIB_OBJ_IS_EXPANDABLE : 0), + }; + } + } + + static readonly PropertyInfo.Mapping MappingToDEBUG_PROPERTY_INFO = + + #region //////////////////// DEBUG_PROPERTY_INFO <-- PropertyInfo ///////////////////////// + // r: Ref<DEBUG_PROPERTY_INFO> + // f: enum_DEBUGPROP_INFO_FLAGS + // i: PropertyInfo + // v: value of i.<<property>> + + new PropertyInfo.Mapping<DEBUG_PROPERTY_INFO, enum_DEBUGPROP_INFO_FLAGS> + ((r, bit) => r.s.dwFields |= bit) + { + { enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_FULLNAME, + (r, v) => r.s.bstrFullName = v, i => i.FullName }, + + { enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_NAME, + (r, v) => r.s.bstrName = v, i => i.Name }, + + { enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_TYPE, + (r, v) => r.s.bstrType = v, i => i.Type }, + + { enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_VALUE, + (r, v) => r.s.bstrValue = v, i => i.Value }, + + { enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_ATTRIB, + (r, v) => r.s.dwAttrib |= v, i => i.Attribs }, + + { enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_PROP, + (r, v) => r.s.pProperty = v, i => i.Property }, + }; + + #endregion //////////////////// DEBUG_PROPERTY_INFO <-- PropertyInfo ////////////////////// + + + public DEBUG_PROPERTY_INFO GetInfo(enum_DEBUGPROP_INFO_FLAGS dwFields) + { + DEBUG_PROPERTY_INFO info; + Info.Map(MappingToDEBUG_PROPERTY_INFO, dwFields, out info); + return info; + } + + int IDebugProperty2.GetPropertyInfo( + enum_DEBUGPROP_INFO_FLAGS dwFields, + uint dwRadix, + uint dwTimeout, + IDebugReference2[] rgpArgs, + uint dwArgCount, + DEBUG_PROPERTY_INFO[] pPropertyInfo) + { + Info.Map(MappingToDEBUG_PROPERTY_INFO, dwFields, out pPropertyInfo[0]); + return VSConstants.S_OK; + } + + int IDebugProperty2.GetParent(out IDebugProperty2 ppParent) + { + ppParent = Parent; + return (Parent != null) ? VSConstants.S_OK : VSConstants.S_FALSE; + } + + #endregion //////////////////// Info ////////////////////////////////////////////////////// + + + #region //////////////////// Filter /////////////////////////////////////////////////////// + + public static class Filter + { + public static readonly Guid Registers + = new Guid("223ae797-bd09-4f28-8241-2763bdc5f713"); + + public static readonly Guid Locals + = new Guid("b200f725-e725-4c53-b36a-1ec27aef12ef"); + + public static readonly Guid AllLocals + = new Guid("196db21f-5f22-45a9-b5a3-32cddb30db06"); + + public static readonly Guid Args + = new Guid("804bccea-0475-4ae7-8a46-1862688ab863"); + + public static readonly Guid LocalsPlusArgs + = new Guid("e74721bb-10c0-40f5-807f-920d37f95419"); + + public static readonly Guid AllLocalsPlusArgs + = new Guid("939729a8-4cb0-4647-9831-7ff465240d5f"); + + public static bool LocalsSelected(ref Guid guidFilter) + { + return guidFilter == Locals + || guidFilter == AllLocals + || guidFilter == LocalsPlusArgs + || guidFilter == AllLocalsPlusArgs; + } + } + + #endregion //////////////////// Filter //////////////////////////////////////////////////// + } +} diff --git a/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7StackFrame.cs b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7StackFrame.cs new file mode 100644 index 0000000..8578f60 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/AD7/QmlDebugAD7StackFrame.cs @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace QtVsTools.Qml.Debug.AD7 +{ + sealed partial class StackFrame : Concurrent, + + IDebugStackFrame2, // "This interface represents a single stack frame in a call + // stack in a particular thread." + + IDebugExpressionContext2, // "This interface represents a context for expression evaluation" + + IDebugProperty2 // "This interface represents a stack frame property, a program + // document property, or some other property. The property is + // usually the result of an expression evaluation." + { + public QmlDebugger Debugger { get; private set; } + + public QmlEngine Engine { get; private set; } + public Program Program { get; private set; } + + public CodeContext Context { get; private set; } + public Dictionary<int, Dictionary<string, Property>> Properties { get; private set; } + + public string Name { get; private set; } + public int FrameNumber { get; private set; } + public IEnumerable<int> Scopes { get; private set; } + public Task InitThread { get; private set; } + + static public StackFrame Create( + string name, + int number, + IEnumerable<int> scopes, + CodeContext context) + { + var _this = new StackFrame(); + return _this.Initialize(name, number, scopes, context) ? _this : null; + } + + private StackFrame() + { } + + private bool Initialize( + string name, + int number, + IEnumerable<int> scopes, + CodeContext context) + { + Context = context; + Engine = context.Engine; + Program = context.Program; + Debugger = Program.Debugger; + Name = string.Format("{0}@{1}:{2}", name, context.FilePath, context.FileLine + 1); + FrameNumber = number; + Scopes = scopes; + InitThread = Task.Run(() => InitializeProperties()); + return true; + } + + private void InitializeProperties(bool forceScope = false) + { + Properties = Scopes.ToDictionary(x => x, x => new Dictionary<string, Property>()); + foreach (var scopeNumber in Scopes) { + var scopeVars = Debugger.RefreshScope(FrameNumber, scopeNumber, forceScope); + foreach (var scopeVar in scopeVars) { + Properties[scopeNumber] + .Add(scopeVar.Name, Property.Create(this, scopeNumber, scopeVar)); + } + } + } + + public void Refresh() + { + InitializeProperties(true); + } + + int IDebugExpressionContext2.ParseText( + string pszCode, + enum_PARSEFLAGS dwFlags, + uint nRadix, + out IDebugExpression2 ppExpr, + out string pbstrError, + out uint pichError) + { + pbstrError = ""; + pichError = 0; + ppExpr = Expression.Create(this, pszCode); + return VSConstants.S_OK; + } + + int IDebugStackFrame2.EnumProperties( + enum_DEBUGPROP_INFO_FLAGS dwFields, + uint nRadix, + ref Guid guidFilter, + uint dwTimeout, + out uint pcelt, + out IEnumDebugPropertyInfo2 ppEnum) + { + pcelt = 0; + ppEnum = null; + + if (guidFilter != Guid.Empty && !Property.Filter.LocalsSelected(ref guidFilter)) + return VSConstants.S_OK; + + InitThread.Wait(); + pcelt = 0; + ppEnum = PropertyEnum.Create(Properties + .SelectMany(x => x.Value + .Select(y => y.Value.GetInfo(dwFields)))); + + return VSConstants.S_OK; + } + + int IDebugProperty2.EnumChildren( + enum_DEBUGPROP_INFO_FLAGS dwFields, + uint dwRadix, + ref Guid guidFilter, + enum_DBG_ATTRIB_FLAGS dwAttribFilter, + string pszNameFilter, + uint dwTimeout, + out IEnumDebugPropertyInfo2 ppEnum) + { + uint pcelt; + return ((IDebugStackFrame2)this) + .EnumProperties(dwFields, dwRadix, guidFilter, dwTimeout, out pcelt, out ppEnum); + } + + #region //////////////////// Info ///////////////////////////////////////////////////////// + + class StackFrameInfo : InfoHelper<StackFrameInfo> + { + public string FunctionName { get; set; } + public string ReturnType { get; set; } + public string Arguments { get; set; } + public string Language { get; set; } + public string ModuleName { get; set; } + public ulong? MinAddress { get; set; } + public ulong? MaxAddress { get; set; } + public IDebugStackFrame2 Frame { get; set; } + public IDebugModule2 Module { get; set; } + public int? HasDebugInfo { get; set; } + public int? StaleCode { get; set; } + } + + StackFrameInfo Info + { + get + { + return new StackFrameInfo + { + FunctionName = Name, + ReturnType = "", + Arguments = "", + Language = Context.FileType.ToString(), + ModuleName = "", + MinAddress = 0, + MaxAddress = 9999, + Frame = this, + Module = Program, + HasDebugInfo = 1, + StaleCode = 0, + }; + } + } + + static readonly StackFrameInfo.Mapping MappingToFRAMEINFO = + + #region //////////////////// FRAMEINFO <-- StackFrameInfo ///////////////////////////////// + // r: Ref<FRAMEINFO> + // f: enum_FRAMEINFO_FLAGS + // i: StackFrameInfo + // v: value of i.<<property>> + + new StackFrameInfo.Mapping<FRAMEINFO, enum_FRAMEINFO_FLAGS> + ((r, bit) => r.s.m_dwValidFields |= bit) + { + { enum_FRAMEINFO_FLAGS.FIF_FUNCNAME, + (r, v) => r.s.m_bstrFuncName = v, i => i.FunctionName }, + + { enum_FRAMEINFO_FLAGS.FIF_RETURNTYPE, + (r, v) => r.s.m_bstrReturnType = v, i => i.ReturnType }, + + { enum_FRAMEINFO_FLAGS.FIF_ARGS, + (r, v) => r.s.m_bstrArgs = v, i => i.Arguments }, + + { enum_FRAMEINFO_FLAGS.FIF_LANGUAGE, + (r, v) => r.s.m_bstrLanguage = v, i => i.Language }, + + { enum_FRAMEINFO_FLAGS.FIF_MODULE, + (r, v) => r.s.m_bstrModule = v, i => i.ModuleName }, + + { enum_FRAMEINFO_FLAGS.FIF_STACKRANGE, + (r, v) => r.s.m_addrMin = v, i => i.MinAddress }, + + { enum_FRAMEINFO_FLAGS.FIF_STACKRANGE, + (r, v) => r.s.m_addrMax = v, i => i.MaxAddress }, + + { enum_FRAMEINFO_FLAGS.FIF_FRAME, + (r, v) => r.s.m_pFrame = v, i => i.Frame }, + + { enum_FRAMEINFO_FLAGS.FIF_DEBUG_MODULEP, + (r, v) => r.s.m_pModule = v, i => i.Module }, + + { enum_FRAMEINFO_FLAGS.FIF_DEBUGINFO, + (r, v) => r.s.m_fHasDebugInfo = v, i => i.HasDebugInfo }, + + { enum_FRAMEINFO_FLAGS.FIF_STALECODE, + (r, v) => r.s.m_fStaleCode = v, i => i.StaleCode }, + }; + + #endregion //////////////////// FRAMEINFO <-- StackFrameInfo ////////////////////////////// + + + int IDebugStackFrame2.GetInfo( + enum_FRAMEINFO_FLAGS dwFieldSpec, + uint nRadix, + FRAMEINFO[] pFrameInfo) + { + Info.Map(MappingToFRAMEINFO, dwFieldSpec, out pFrameInfo[0]); + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetCodeContext(out IDebugCodeContext2 ppCodeCxt) + { + ppCodeCxt = Context; + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetDocumentContext(out IDebugDocumentContext2 ppCxt) + { + ppCxt = Context; + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetName(out string pbstrName) + { + pbstrName = Name; + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetPhysicalStackRange(out ulong paddrMin, out ulong paddrMax) + { + paddrMin = ulong.MinValue; + paddrMax = ulong.MaxValue; + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetExpressionContext(out IDebugExpressionContext2 ppExprCxt) + { + ppExprCxt = this; + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetLanguageInfo(ref string pbstrLanguage, ref Guid pguidLanguage) + { + pbstrLanguage = "C++"; + pguidLanguage = NativeEngine.IdLanguageCpp; + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetThread(out IDebugThread2 ppThread) + { + ppThread = Program; + return VSConstants.S_OK; + } + + int IDebugStackFrame2.GetDebugProperty(out IDebugProperty2 ppProperty) + { + ppProperty = this; + return VSConstants.S_OK; + } + + #endregion //////////////////// Info ////////////////////////////////////////////////////// + + } +} diff --git a/QtVsTools.Package/QML/Debugging/QmlDebugLauncher.cs b/QtVsTools.Package/QML/Debugging/QmlDebugLauncher.cs new file mode 100644 index 0000000..025ee42 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/QmlDebugLauncher.cs @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Debugger.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.VCProjectEngine; +using QtVsTools.Core; +using QtVsTools.Core.QtMsBuild; +using QtVsTools.SyntaxAnalysis; +using static QtVsTools.SyntaxAnalysis.RegExpr; + +namespace QtVsTools.Qml.Debug +{ + using AD7; + using VisualStudio; + + class Launcher : Disposable, IDebugEventCallback2 + { + public static Launcher Instance { get; private set; } + IVsDebugger debugger; + IVsDebugger4 debugger4; + + HashSet<Guid> _ExcludedProcesses; + HashSet<Guid> ExcludedProcesses => _ExcludedProcesses + ?? (_ExcludedProcesses = new HashSet<Guid>()); + + public static void Initialize() + { + Instance = new Launcher(); + Instance.debugger = VsServiceProvider.GetService<IVsDebugger>(); + Instance.debugger4 = VsServiceProvider.GetService<IVsDebugger, IVsDebugger4>(); + if (Instance.debugger != null && Instance.debugger4 != null) + Instance.debugger.AdviseDebugEventCallback(Instance); + } + + protected override void DisposeManaged() + { + if (debugger != null) + debugger.UnadviseDebugEventCallback(this); + } + + int IDebugEventCallback2.Event( + IDebugEngine2 pEngine, + IDebugProcess2 pProcess, + IDebugProgram2 pProgram, + IDebugThread2 pThread, + IDebugEvent2 pEvent, + ref Guid riidEvent, + uint dwAttrib) + { + if (!QtVsToolsPackage.Instance.Options.QmlDebuggerEnabled) + return VSConstants.S_OK; + + if (riidEvent != typeof(IDebugThreadCreateEvent2).GUID + && riidEvent != typeof(IDebugProgramDestroyEvent2).GUID) { + return VSConstants.S_OK; + } + + if (pProcess == null && pProgram.GetProcess(out pProcess) != VSConstants.S_OK) + return VSConstants.S_OK; + + Guid procGuid; + if (pProcess.GetProcessId(out procGuid) != VSConstants.S_OK) + return VSConstants.S_OK; + + // Run only once per process + if (riidEvent == typeof(IDebugProgramDestroyEvent2).GUID) { + ExcludedProcesses.Remove(procGuid); + return VSConstants.S_OK; + } else if (ExcludedProcesses.Contains(procGuid)) { + return VSConstants.S_OK; + } else { + ExcludedProcesses.Add(procGuid); + } + + if (!(pEvent is IDebugLoadCompleteEvent2 || pEvent is IDebugThreadCreateEvent2)) + return VSConstants.S_OK; + + if (pProgram == null) + return VSConstants.S_OK; + + bool native; + Guid engineId = GetEngineId(pProgram); + if (engineId == NativeEngine.Id) + native = true; + else if (engineId == GdbEngine.Id) + native = false; + else + return VSConstants.S_OK; + + string execPath; + uint procId; + if (!GetProcessInfo(pProcess, native, out execPath, out procId)) + return VSConstants.S_OK; + + string execCmd; + IEnumerable<string> rccItems; + if (!GetProjectInfo(execPath, native, out execCmd, out rccItems)) + return VSConstants.S_OK; + + LaunchDebug(execPath, execCmd, procId, rccItems); + return VSConstants.S_OK; + } + + Guid GetEngineId(IDebugProgram2 pProgram) + { + string engineName; + Guid engineGuid; + if (pProgram.GetEngineInfo(out engineName, out engineGuid) != VSConstants.S_OK) + return Guid.Empty; + return engineGuid; + } + + class WslPath + { + public string Drive; + public string Path; + public static implicit operator string(WslPath wslPath) + { + return string.Format(@"{0}:\{1}", wslPath.Drive, wslPath.Path); + } + } + + static RegExpr wslPathRegex = new Token("WSLPATH", SkipWs_Disable, StartOfFile + & "/mnt/" & new Token("DRIVE", CharWord) & "/" & new Token("PATH", AnyChar.Repeat())) + { + new Rule<WslPath> + { + Update("DRIVE", (WslPath wslPath, string drive) => wslPath.Drive = drive), + Update("PATH", (WslPath wslPath, string path) => wslPath.Path = path), + } + }; + static RegExpr.Parser wslPathParser = wslPathRegex.Render(); + + bool GetProcessInfo(IDebugProcess2 pProcess, bool native, out string execPath, out uint procId) + { + execPath = ""; + procId = 0; + + string fileName; + if (pProcess.GetName(enum_GETNAME_TYPE.GN_FILENAME, out fileName) != VSConstants.S_OK) + return false; + + var pProcessId = new AD_PROCESS_ID[1]; + if (pProcess.GetPhysicalProcessId(pProcessId) != VSConstants.S_OK) + return false; + + if (native) { + execPath = Path.GetFullPath(fileName); + } else { + var wslPath = wslPathParser.Parse(fileName) + .GetValues<WslPath>("WSLPATH").FirstOrDefault(); + if (wslPath != null) + execPath = Path.GetFullPath(wslPath); + else + execPath = fileName; + } + + procId = pProcessId[0].dwProcessId; + return true; + } + + bool GetProjectInfo(string execPath, bool native, out string execCmd, out IEnumerable<string> rccItems) + { + execCmd = ""; + rccItems = null; + + foreach (var project in HelperFunctions.ProjectsInSolution(QtVsToolsPackage.Instance.Dte)) { + + var vcProject = project.Object as VCProject; + if (vcProject == null) + continue; + + var vcConfigs = vcProject.Configurations as IVCCollection; + if (vcConfigs == null) + continue; + var activeConfig = project.ConfigurationManager.ActiveConfiguration; + if (activeConfig == null) + continue; + var activeConfigId = string.Format("{0}|{1}", + activeConfig.ConfigurationName, activeConfig.PlatformName); + var vcConfig = vcConfigs.Item(activeConfigId) as VCConfiguration; + if (vcConfig == null) + continue; + + var props = vcProject as IVCBuildPropertyStorage; + + var localDebugCommand = props.GetPropertyValue("LocalDebuggerCommand", + vcConfig.Name, "UserFile"); + + var remoteDebugCommand = props.GetPropertyValue("RemoteDebuggerCommand", + vcConfig.Name, "UserFile"); + + string debugCommand = (native || string.IsNullOrEmpty(remoteDebugCommand)) + ? localDebugCommand : remoteDebugCommand; + + bool sameFile = string.Equals(execPath, Path.GetFullPath(debugCommand), + StringComparison.InvariantCultureIgnoreCase); + + if (!sameFile) + continue; + + OutputWriteLine(string.Format("Debugging project '{0}'...", vcProject.Name)); + + var qtProject = QtProject.Create(vcProject); + if (qtProject == null) { + OutputWriteLine("DISABLED: Non-Qt project"); + return false; + } + + if (!qtProject.IsQtMsBuildEnabled()) { + OutputWriteLine("DISABLED: Non-Qt/MSBuild project"); + return false; + } + + if (!qtProject.QmlDebug) { + OutputWriteLine("DISABLED: QML debugging disabled in Qt project settings"); + return false; + } + + var execArgs = props.GetPropertyValue( + native ? "LocalDebuggerCommandArguments" : "RemoteDebuggerCommandArguments", + vcConfig.Name, "UserFile"); + if (string.IsNullOrEmpty(execArgs)) { + OutputWriteLine("DISABLED: Error reading command line arguments"); + return false; + } + + var cmd = "\"" + execPath + "\" " + execArgs; + + if (!QmlDebugger.CheckCommandLine(execPath, cmd)) { + OutputWriteLine("DISABLED: Error parsing command line arguments"); + return false; + } + + OutputWriteLine("Starting QML debug session..."); + + execCmd = cmd; + rccItems = ((IVCCollection)vcProject.Files).Cast<VCFile>() + .Where(x => x.ItemType == QtRcc.ItemTypeName) + .Select(x => x.FullPath); + + return true; + } + + OutputWriteLine("DISABLED: Could not identify project being debugged"); + + return false; + } + + void OutputWriteLine(string msg) + { + Messages.Print(msg); + } + + void LaunchDebug( + string execPath, + string execCmd, + uint procId, + IEnumerable<string> rccItems) + { + var targets = new[] { new VsDebugTargetInfo4 + { + dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_CreateProcess, + bstrExe = new Uri(execPath).LocalPath, + bstrArg = execCmd, + bstrOptions = procId.ToString(), + bstrEnv = "QTRCC=" + string.Join(";", rccItems), + guidLaunchDebugEngine = QmlEngine.Id, + LaunchFlags = (uint)__VSDBGLAUNCHFLAGS5.DBGLAUNCH_BreakOneProcess, + }}; + + var processInfo = new VsDebugTargetProcessInfo[targets.Length]; + try { + debugger4.LaunchDebugTargets4((uint)targets.Length, targets, processInfo); + + } catch (System.Exception e) { + OutputWriteLine(e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/QmlDebugger.cs b/QtVsTools.Package/QML/Debugging/QmlDebugger.cs new file mode 100644 index 0000000..892a17d --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/QmlDebugger.cs @@ -0,0 +1,545 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using QtVsTools.SyntaxAnalysis; +using static QtVsTools.SyntaxAnalysis.RegExpr; +using RegExprParser = QtVsTools.SyntaxAnalysis.RegExpr.Parser; + +namespace QtVsTools.Qml.Debug +{ + using V4; + + struct FrameInfo + { + public int Number; + public string QrcPath; + public int Line; + public string Name; + public List<int> Scopes; + } + + interface IDebuggerEventSink + { + bool QueryRuntimeFrozen(); + void NotifyClientDisconnected(); + void NotifyStackContext(IList<FrameInfo> frames); + void NotifyBreak(); + void NotifyError(string errorMessage); + } + + interface IBreakpoint + { + string QrcPath { get; } + uint Line { get; } + void NotifySet(); + void NotifyClear(); + void NotifyBreak(); + void NotifyError(string errorMessage); + } + + class QmlDebugger : Disposable, IMessageEventSink + { + IDebuggerEventSink sink; + ProtocolDriver driver; + string connectionHostName; + ushort connectionHostPortFrom; + ushort connectionHostPortTo; + string connectionFileName; + bool connectionBlock; + + List<Request> outbox; + Dictionary<int, IBreakpoint> breakpoints; + + public bool Started { get; private set; } + + public bool Running { get; private set; } + + public string Version { get; private set; } + + public uint? ThreadId { get { return driver.ThreadId; } } + + public static QmlDebugger Create(IDebuggerEventSink sink, string execPath, string args) + { + var _this = new QmlDebugger(); + return _this.Initialize(sink, execPath, args) ? _this : null; + } + + private QmlDebugger() + { } + + private bool Initialize(IDebuggerEventSink sink, string execPath, string args) + { + this.sink = sink; + if (sink == null) + return false; + + if (!ParseCommandLine(execPath, args, + out connectionHostPortFrom, out connectionHostPortTo, + out connectionHostName, out connectionFileName, out connectionBlock)) { + return false; + } + + driver = ProtocolDriver.Create(this); + if (driver == null) + return false; + + outbox = new List<Request>(); + breakpoints = new Dictionary<int, IBreakpoint>(); + return true; + } + + protected override void DisposeManaged() + { + driver.Dispose(); + } + + void ConnectToDebugger() + { + if (!string.IsNullOrEmpty(connectionFileName)) + driver.StartLocalServer(connectionFileName).WaitOne(); + else + driver.Connect(connectionHostName, connectionHostPortFrom).WaitOne(); + + if (driver.ConnectionState != DebugClientState.Connected) { + sink.NotifyClientDisconnected(); + return; + } + + var reqVersion = Message.Create<VersionRequest>(driver); + var resVersion = reqVersion.Send(); + if (resVersion != null) + ThreadSafe(() => Version = resVersion.Body.Version); + + foreach (var request in ThreadSafe(() => outbox.ToList())) + request.Send(); + + ThreadSafe(() => outbox.Clear()); + + Message.Send<ConnectMessage>(driver); + } + + bool IMessageEventSink.QueryRuntimeFrozen() + { + return sink.QueryRuntimeFrozen(); + } + + public void Run() + { + EnterCriticalSection(); + + if (!Started) { + Running = Started = true; + LeaveCriticalSection(); + Task.Run(() => ConnectToDebugger()); + + } else if (!Running) { + Running = true; + LeaveCriticalSection(); + Request.Send<ContinueRequest>(driver); + + } else { + LeaveCriticalSection(); + } + } + + public void StepOver() + { + var reqContinue = Message.Create<ContinueRequest>(driver); + reqContinue.Arguments.StepAction = ContinueRequest.StepAction.Next; + reqContinue.Send(); + } + + public void StepInto() + { + var reqContinue = Message.Create<ContinueRequest>(driver); + reqContinue.Arguments.StepAction = ContinueRequest.StepAction.StepIn; + reqContinue.Send(); + } + + public void StepOut() + { + var reqContinue = Message.Create<ContinueRequest>(driver); + reqContinue.Arguments.StepAction = ContinueRequest.StepAction.StepOut; + reqContinue.Send(); + } + + public void SetBreakpoint(IBreakpoint breakpoint) + { + var setBreakpoint = Message.Create<SetBreakpointRequest>(driver); + setBreakpoint.Arguments.TargetType = SetBreakpointRequest.TargetType.ScriptRegExp; + setBreakpoint.Arguments.Target = breakpoint.QrcPath; + setBreakpoint.Arguments.Line = (int)breakpoint.Line; + setBreakpoint.Tag = breakpoint; + if (driver.ConnectionState == DebugClientState.Connected) + setBreakpoint.SendAsync(); + else + ThreadSafe(() => outbox.Add(setBreakpoint)); + } + + void SetBreakpointResponded(SetBreakpointRequest reqSetBreak) + { + System.Diagnostics.Debug.Assert(reqSetBreak.Response != null); + + var breakpoint = reqSetBreak.Tag as IBreakpoint; + System.Diagnostics.Debug.Assert(breakpoint != null); + + if (reqSetBreak.Response.Success) { + ThreadSafe(() => breakpoints[reqSetBreak.Response.Body.Breakpoint] = breakpoint); + breakpoint.NotifySet(); + } else { + breakpoint.NotifyError(reqSetBreak.Response.Message); + } + } + + public void ClearBreakpoint(IBreakpoint breakpoint) + { + var breakpointNum = ThreadSafe(() => breakpoints + .ToDictionary(x => x.Value, x => x.Key)); + + if (!breakpointNum.ContainsKey(breakpoint)) + return; + + var reqClearBreak = Message.Create<ClearBreakpointRequest>(driver); + reqClearBreak.Arguments.Breakpoint = breakpointNum[breakpoint]; + reqClearBreak.SendAsync(); + } + + void RefreshFrames() + { + var frames = new List<FrameInfo>(); + currentScope = null; + + var reqBacktrace = Message.Create<BacktraceRequest>(driver); + var resBacktrace = reqBacktrace.Send(); + if (resBacktrace != null && resBacktrace.Success) { + + foreach (var frameRef in resBacktrace.Body.Frames) { + var reqFrame = Message.Create<FrameRequest>(driver); + reqFrame.Arguments.FrameNumber = frameRef.Index; + + var resFrame = reqFrame.Send(); + if (resFrame == null) + continue; + + var frame = new FrameInfo + { + Number = resFrame.Frame.Index, + Name = resFrame.Frame.Function, + QrcPath = resFrame.Frame.Script, + Line = resFrame.Frame.Line, + Scopes = new List<int>() + }; + + foreach (var scope in resFrame.Frame.Scopes + .Where(x => x.Type != Scope.ScopeType.Global)) { + frame.Scopes.Add(scope.Index); + } + + frames.Add(frame); + } + } else if (resBacktrace != null) { + sink.NotifyError(resBacktrace.Message); + } else { + sink.NotifyError("Error sending 'backtrace' message to QML runtime."); + } + sink.NotifyStackContext(frames); + } + + void BreakNotified(BreakEvent evtBreak) + { + Running = false; + + RefreshFrames(); + + if (evtBreak.Body.Breakpoints == null || evtBreak.Body.Breakpoints.Count == 0) { + sink.NotifyBreak(); + + } else { + foreach (int breakpointId in evtBreak.Body.Breakpoints) { + IBreakpoint breakpoint; + if (!breakpoints.TryGetValue(breakpointId, out breakpoint)) + continue; + breakpoint.NotifyBreak(); + } + } + } + + Scope currentScope = null; + + Scope MoveToScope(int frameNumber, int scopeNumber) + { + lock (CriticalSection) { + if (currentScope != null + && currentScope.FrameIndex == frameNumber + && currentScope.Index == scopeNumber) { + return currentScope; + } + + var reqScope = Message.Create<ScopeRequest>(driver); + reqScope.Arguments.FrameNumber = frameNumber; + reqScope.Arguments.ScopeNumber = scopeNumber; + + var resScope = reqScope.Send(); + if (resScope == null) + return null; + + return currentScope = resScope.Scope; + } + } + + public IEnumerable<JsValue> RefreshScope( + int frameNumber, + int scopeNumber, + bool forceScope = false) + { + if (forceScope) + currentScope = null; + + var vars = new SortedList<string, JsValue>(); + lock (CriticalSection) { + + var scope = MoveToScope(frameNumber, scopeNumber); + if (scope == null) + return null; + + var scopeObj = ((JsValue)scope.Object) as JsObject; + if (scopeObj == null) + return null; + + scopeObj.Properties + .Where(x => x.HasData && !string.IsNullOrEmpty(((JsValue)x).Name)) + .Select(x => new { name = ((JsValue)x).Name, value = (JsValue)x }) + .ToList().ForEach(x => vars.Add(x.name, x.value)); + + if (scope.Type == Scope.ScopeType.Local) { + var reqEval = Message.Create<EvaluateRequest>(driver); + reqEval.Arguments.Expression = "this"; + reqEval.Arguments.Frame = frameNumber; + + var resEval = reqEval.Send(); + if (resEval != null && resEval.Result.HasData) { + JsValue resValue = resEval.Result; + resValue.Name = "this"; + vars.Add(resValue.Name, resValue); + } + } + } + return vars.Values; + } + + public JsObject Lookup(int frameNumber, int scopeNumber, JsObjectRef objRef) + { + if (MoveToScope(frameNumber, scopeNumber) == null) + return null; + + var reqLookup = Message.Create<LookupRequest>(driver); + reqLookup.Arguments.Handles = new List<int> { objRef.Ref }; + + var resLookup = reqLookup.Send(); + if (resLookup == null) + return null; + + var defObj = resLookup.Objects.Values.FirstOrDefault(); + if (!defObj.HasData) + return null; + + JsValue obj = defObj; + if (!(obj is JsObject)) + return null; + + obj.Name = objRef.Name; + return obj as JsObject; + } + + public JsValue Evaluate(int frameNumber, string expression) + { + var reqEval = Message.Create<EvaluateRequest>(driver); + reqEval.Arguments.Expression = expression; + reqEval.Arguments.Frame = frameNumber; + + var resEval = reqEval.Send(); + if (resEval == null) + return new JsError { Message = "ERROR: Expression evaluation failed" }; + if (!resEval.Success) + return new JsError { Message = resEval.Message }; + + if (!resEval.Result.HasData) + return new JsError { Message = "ERROR: Cannot read data" }; + + return resEval.Result; + } + + void IMessageEventSink.NotifyStateTransition( + DebugClient client, + DebugClientState oldState, + DebugClientState newState) + { + if (oldState != DebugClientState.Unavailable + && newState == DebugClientState.Disconnected) { + Task.Run(() => sink.NotifyClientDisconnected()); + } + } + + void IMessageEventSink.NotifyRequestResponded(Request msgRequest) + { + if (msgRequest is SetBreakpointRequest) + Task.Run(() => SetBreakpointResponded(msgRequest as SetBreakpointRequest)); + } + + void IMessageEventSink.NotifyEvent(Event msgEvent) + { + if (msgEvent is BreakEvent) + Task.Run(() => BreakNotified(msgEvent as BreakEvent)); + } + + void IMessageEventSink.NotifyMessage(Message msg) + { + System.Diagnostics.Debug + .Assert(msg is ConnectMessage, "Unexpected message"); + } + + public static bool CheckCommandLine(string execPath, string args) + { + ushort portFrom; + ushort portTo; + string hostName; + string fileName; + bool block; + return ParseCommandLine( + execPath, args, out portFrom, out portTo, out hostName, out fileName, out block); + } + + /// <summary> + /// Connection parameters for QML debug session + /// </summary> + class ConnectParams + { + public ushort Port { get; set; } + public ushort? MaxPort { get; set; } + public string Host { get; set; } + public string File { get; set; } + public bool Block { get; set; } + } + + enum TokenId { ConnectParams, Port, MaxPort, Host, File, Block } + + /// <summary> + /// Regex-based parser for QML debug connection parameters + /// </summary> + static RegExprParser ConnectParamsParser => _ConnectParamsParser ?? ( + _ConnectParamsParser = new Token(TokenId.ConnectParams, RxConnectParams) + { + new Rule<ConnectParams> + { + Update(TokenId.Port, (ConnectParams conn, ushort n) => conn.Port = n), + Update(TokenId.MaxPort, (ConnectParams conn, ushort n) => conn.MaxPort = n), + Update(TokenId.Host, (ConnectParams conn, string s) => conn.Host = s), + Update(TokenId.File, (ConnectParams conn, string s) => conn.File = s), + Update(TokenId.Block, (ConnectParams conn, bool b) => conn.Block = b) + } + } + .Render()); + static RegExprParser _ConnectParamsParser; + + /// <summary> + /// Regular expression for parsing connection parameters string in the form: + /// + /// -qmljsdebugger=port:<port_num>[,port_max][,host:<address>][,file:<name>][,block] + /// + /// </summary> + static RegExpr RxConnectParams => + "-qmljsdebugger=" + & ((RxPort | RxHost | RxFile) & RxDelim).Repeat(atLeast: 1) & RxBlock.Optional(); + + static RegExpr RxPort => + "port:" & new Token(TokenId.Port, CharDigit.Repeat(atLeast: 1)) + { + new Rule<ushort> { Capture(token => ushort.Parse(token)) } + } + & ( + "," & new Token(TokenId.MaxPort, CharDigit.Repeat(atLeast: 1)) + { + new Rule<ushort> { Capture(token => ushort.Parse(token)) } + } + ).Optional(); + + static RegExpr RxHost => + "host:" & new Token(TokenId.Host, (~CharSet[CharSpace, Chars[","]]).Repeat(atLeast: 1)); + + static RegExpr RxFile => + "file:" & new Token(TokenId.File, (~CharSet[CharSpace, Chars[","]]).Repeat(atLeast: 1)); + + static RegExpr RxBlock => + new Token(TokenId.Block, "block") + { + new Rule<bool> { Capture(token => true) } + }; + + static RegExpr RxDelim => + ("," & !LookAhead[CharSpace | EndOfLine]) | LookAhead[CharSpace | EndOfLine]; + + /// <summary> + /// Extract QML debug connection parameters from command line args + /// </summary> + public static bool ParseCommandLine( + string execPath, + string args, + out ushort portFrom, + out ushort portTo, + out string hostName, + out string fileName, + out bool block) + { + portFrom = portTo = 0; + hostName = fileName = ""; + block = false; + + ConnectParams connParams = ConnectParamsParser + .Parse(args) + .GetValues<ConnectParams>(TokenId.ConnectParams) + .FirstOrDefault(); + + if (connParams == null) + return false; + + portFrom = connParams.Port; + if (connParams.MaxPort.HasValue) + portTo = connParams.MaxPort.Value; + hostName = connParams.Host; + fileName = connParams.File; + block = connParams.Block; + + return true; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/QmlFileSystem.cs b/QtVsTools.Package/QML/Debugging/QmlFileSystem.cs new file mode 100644 index 0000000..4b33faf --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/QmlFileSystem.cs @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace QtVsTools.Qml.Debug +{ + struct QmlFile + { + public string QrcPath; + public string FilePath; + } + + class FileSystem : Concurrent + { + Dictionary<string, QmlFile> files; + + public static FileSystem Create() + { + return new FileSystem + { + files = new Dictionary<string, QmlFile>() + }; + } + + static readonly string[] KNOWN_EXTENSIONS = new string[] { ".qml", ".js" }; + + private FileSystem() + { } + + public IEnumerable<string> QrcPaths + { + get + { + return files.Values + .GroupBy(x => x.QrcPath) + .Select(x => x.Key); + } + } + + string QrcPath(string prefix, string filePath) + { + if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith("/")) + prefix += "/"; + + while (!string.IsNullOrEmpty(prefix) && prefix[0] == '/') + prefix = prefix.Substring(1); + + return string.Format("qrc:///{0}{1}", prefix, filePath); + } + + public void RegisterRccFile(string rccFilePath) + { + XDocument rccXml; + try { + var xmlText = File.ReadAllText(rccFilePath, Encoding.UTF8); + var settings = new XmlReaderSettings + { + DtdProcessing = DtdProcessing.Ignore + }; + using (var reader = XmlReader.Create(new StringReader(xmlText), settings)) { + rccXml = XDocument.Load(reader); + } + } catch (Exception e) { + System.Diagnostics.Debug.WriteLine( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + return; + } + + var files = rccXml + .Elements("RCC") + .Elements("qresource") + .SelectMany(x => x.Elements("file") + .Select(y => new + { + Prefix = x.Attribute("prefix"), + Alias = y.Attribute("alias"), + Path = ((string)y) + .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + }) + .Where(z => KNOWN_EXTENSIONS.Contains( + Path.GetExtension(z.Path), StringComparer.InvariantCultureIgnoreCase))); + + foreach (var file in files) { + string qrcPath; + if (file.Alias != null) + qrcPath = (string)file.Alias; + else if (!Path.IsPathRooted(file.Path)) + qrcPath = file.Path.Replace(@"\", "/"); + else + continue; + + string qrcPathPrefix = (file.Prefix != null) ? ((string)file.Prefix) : ""; + if (!string.IsNullOrEmpty(qrcPathPrefix) && !qrcPathPrefix.EndsWith("/")) + qrcPathPrefix += "/"; + + while (!string.IsNullOrEmpty(qrcPathPrefix) && qrcPathPrefix[0] == '/') + qrcPathPrefix = qrcPathPrefix.Substring(1); + + var qmlFile = new QmlFile + { + FilePath = Path.Combine(Path.GetDirectoryName(rccFilePath), file.Path), + QrcPath = string.Format("qrc:///{0}{1}", qrcPathPrefix, qrcPath) + }; + + this.files[qmlFile.QrcPath.ToLower()] = qmlFile; + this.files[qmlFile.FilePath.ToUpper()] = qmlFile; + } + } + + QmlFile FromQrcPath(string qrcPath) + { + // Normalize qrc path: + // - Only pre-condition is that qrcPath have a "qrc:" prefix + // - It might have any number of '/' after that, or none at all + // - A "qrc:///" prefix is required to match the mapping key + // - to enforce this, the "qrc:" prefix is removed, as well as any leading '/' + // - then the "normalized" prefix "qrc:///" is added + if (!qrcPath.StartsWith("qrc:")) + return default(QmlFile); + qrcPath = qrcPath.Substring("qrc:".Length); + + while (!string.IsNullOrEmpty(qrcPath) && qrcPath[0] == '/') + qrcPath = qrcPath.Substring(1); + + qrcPath = string.Format("qrc:///{0}", qrcPath); + + QmlFile file; + if (!files.TryGetValue(qrcPath, out file)) + return default(QmlFile); + + return file; + } + + QmlFile FromFileUrl(string fileUrl) + { + string filePath = fileUrl.Substring("file://".Length); + + while (!string.IsNullOrEmpty(filePath) && filePath[0] == '/') + filePath = filePath.Substring(1); + + if (!File.Exists(filePath)) + return default(QmlFile); + + return new QmlFile + { + QrcPath = fileUrl, + FilePath = filePath + .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) + }; + } + + QmlFile FromFilePath(string filePath) + { + string fullPath; + try { + fullPath = Path.GetFullPath(filePath).ToUpper(); + } catch { + return default(QmlFile); + } + + QmlFile file; + if (files.TryGetValue(fullPath, out file)) + return file; + + return new QmlFile + { + FilePath = fullPath, + QrcPath = new Uri(fullPath).ToString().ToLower() + }; + } + + public QmlFile this[string path] + { + get + { + if (path.StartsWith("qrc:", StringComparison.InvariantCultureIgnoreCase)) + return FromQrcPath(path.ToLower()); + else if (path.StartsWith("file:", StringComparison.InvariantCultureIgnoreCase)) + return FromFileUrl(path); + else + return FromFilePath(path.ToUpper()); + } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Backtrace.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Backtrace.cs new file mode 100644 index 0000000..77a5785 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Backtrace.cs @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + [DataContract] + sealed class BacktraceRequest : Request<BacktraceResponse, BacktraceRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "request", + // "command" : "backtrace", + // "arguments" : { "fromFrame" : <number> + // "toFrame" : <number> + // "bottom" : <boolean, set to true if the bottom of the + // stack is requested> + // } + // } + public const string REQ_COMMAND = "backtrace"; + public BacktraceRequest() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "fromFrame")] + public int FromFrame { get; set; } + + [DataMember(Name = "toFrame")] + public int ToFrame { get; set; } + + [DataMember(Name = "bottom")] + public bool Bottom { get; set; } + } + } + + [DataContract] + sealed class BacktraceResponse : Response<BacktraceResponse.BodyStruct> + { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "backtrace", + // "body" : { "fromFrame" : <number> + // "toFrame" : <number> + // "totalFrames" : <number> + // "frames" : <array of frames - see frame request for details> + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + public const string REQ_COMMAND = BacktraceRequest.REQ_COMMAND; + public BacktraceResponse() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class BodyStruct + { + [DataMember(Name = "fromFrame")] + public int FromFrame { get; set; } + + [DataMember(Name = "toFrame")] + public int ToFrame { get; set; } + + [DataMember(Name = "totalFrames")] + public int TotalFrames { get; set; } + + [DataMember(Name = "frames")] + public List<Frame> Frames { get; set; } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Break.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Break.cs new file mode 100644 index 0000000..31eee51 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Break.cs @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + [DataContract] + sealed class BreakEvent : Event<BreakEvent.BodyStruct> + { + // "v8message" + // { "seq" : <number>, + // "type" : "event", + // "event" : "break", + // "body" : { "invocationText" : <string>, + // "sourceLine" : <int>, + // "sourceLineText" : <string>, + // "script" : { "name" : <string> + // }, + // "breakpoints" : [ <int>, + // ... + // ] + // } + // } + public const string EV_TYPE = "break"; + public BreakEvent() : base() + { + EventType = EV_TYPE; + } + + [DataContract] + public class BodyStruct + { + [DataMember(Name = "invocationText")] + public string InvocationText { get; set; } + + [DataMember(Name = "sourceLine")] + public int SourceLine { get; set; } + + [DataMember(Name = "sourceLineText")] + public string SourceLineText { get; set; } + + [DataMember(Name = "script")] + public ScriptStruct Script { get; set; } + + [DataMember(Name = "breakpoints")] + public List<int> Breakpoints { get; set; } + + [DataContract] + public class ScriptStruct + { + [DataMember(Name = "name")] + public string Name { get; set; } + } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4ClearBreakpoint.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4ClearBreakpoint.cs new file mode 100644 index 0000000..96b9bb2 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4ClearBreakpoint.cs @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + [DataContract] + sealed class ClearBreakpointRequest + : Request<ClearBreakpointResponse, ClearBreakpointRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "request", + // "command" : "clearbreakpoint", + // "arguments" : { "breakpoint" : <number of the break point to clear> + // } + // } + public const string REQ_COMMAND = "clearbreakpoint"; + public ClearBreakpointRequest() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "breakpoint")] + public int Breakpoint { get; set; } + } + } + + [DataContract] + sealed class ClearBreakpointResponse : Response + { + public const string REQ_COMMAND = ClearBreakpointRequest.REQ_COMMAND; + public ClearBreakpointResponse() : base() + { + Command = REQ_COMMAND; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Connect.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Connect.cs new file mode 100644 index 0000000..e8e3f63 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Connect.cs @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + [DataContract] + sealed class ConnectMessage : Message + { + // "connect" + // { "redundantRefs" : <bool>, + // "namesAsObjects" : <bool> + // } + public const string MSG_TYPE = "connect"; + public ConnectMessage() : base() + { + Type = MSG_TYPE; + } + + [DataMember(Name = "redundantRefs")] + public bool RedundantRefs { get; set; } + + [DataMember(Name = "namesAsObjects")] + public bool NamesAsObjects { get; set; } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Continue.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Continue.cs new file mode 100644 index 0000000..905adff --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Continue.cs @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.Serialization; +using System.Threading; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + sealed class ContinueRequest : Request<ContinueResponse, ContinueRequest.ArgumentsStruct> + { + // "v8request" + // { "seq" : <number>, + // "type" : "request", + // "command" : "continue", + // "arguments" : { "stepaction" : <"in", "next" or "out">, + // "stepcount" : <number of steps (default 1)> + // } + // } + public const string REQ_COMMAND = "continue"; + public ContinueRequest() : base() + { + Command = REQ_COMMAND; + } + + public enum StepAction + { + [EnumString(default(string))] Continue = 0, + [EnumString("in")] StepIn, + [EnumString("next")] Next, + [EnumString("out")] StepOut + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "stepaction", EmitDefaultValue = false)] + string StepActionString { get; set; } + + public StepAction StepAction + { + get { return SerializableEnum.Deserialize<StepAction>(StepActionString); } + set { StepActionString = SerializableEnum.Serialize<StepAction>(value); } + } + + [DataMember(Name = "stepcount", EmitDefaultValue = false)] + public int? StepCount { get; set; } + } + } + + [DataContract] + sealed class ContinueResponse : Response + { + // "v8message" + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "continue", + // "running" : <is the VM running after sending this response>, + // "success" : true + // } + public const string REQ_COMMAND = ContinueRequest.REQ_COMMAND; + public ContinueResponse() : base() + { + Command = REQ_COMMAND; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Disconnect.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Disconnect.cs new file mode 100644 index 0000000..8213594 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Disconnect.cs @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + [DataContract] + sealed class DisconnectRequest : Request<DisconnectResponse> + { + // "v8request" + // { "seq" : <number>, + // "type" : "request", + // "command" : "disconnect" + // } + public const string REQ_COMMAND = "disconnect"; + public DisconnectRequest() : base() + { + Command = REQ_COMMAND; + } + } + + [DataContract] + sealed class DisconnectResponse : Response + { + // "v8message" + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "disconnect", + // "running" : <is the VM running after sending this response>, + // "success" : true + // } + public const string REQ_COMMAND = DisconnectRequest.REQ_COMMAND; + public DisconnectResponse() : base() + { + Command = REQ_COMMAND; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Evaluate.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Evaluate.cs new file mode 100644 index 0000000..c864f6e --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Evaluate.cs @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + sealed class EvaluateRequest : Request<EvaluateResponse, EvaluateRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "request", + // "command" : "evaluate", + // "arguments" : { "expression" : <expression to evaluate>, + // "frame" : <number>, + // "global" : <boolean>, + // "disable_break" : <boolean>, + // "context" : <object id> + // } + // } + public const string REQ_COMMAND = "evaluate"; + public EvaluateRequest() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "expression")] + public string Expression { get; set; } + + [DataMember(Name = "frame")] + public int Frame { get; set; } + + [DataMember(Name = "global", EmitDefaultValue = false)] + public bool? Global { get; set; } + + [DataMember(Name = "disable_break", EmitDefaultValue = false)] + public bool? DisableBreak { get; set; } + } + } + + [DataContract] + sealed class EvaluateResponse : Response + { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "evaluate", + // "body" : ... + // "running" : <is the VM running after sending this response> + // "success" : true + // } + public const string REQ_COMMAND = EvaluateRequest.REQ_COMMAND; + public EvaluateResponse() : base() + { + Command = REQ_COMMAND; + } + + [DataMember(Name = "body")] + public DeferredObject<JsValue> Result { get; set; } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Exception.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Exception.cs new file mode 100644 index 0000000..aa04c6f --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Exception.cs @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + sealed class ExceptionEvent : Event<ExceptionEvent.BodyStruct> + { + // "v8message" + // { "seq" : <number>, + // "type" : "event", + // "event" : "break", + // "body" : { "sourceLine" : <int>, + // "script" : { "name" : <string> + // }, + // "text" : <string>, + // "exception" : <object> + // } + // } + public const string EV_TYPE = "exception"; + public ExceptionEvent() : base() + { + EventType = EV_TYPE; + } + + [DataContract] + public class BodyStruct + { + [DataMember(Name = "sourceLine")] + public int SourceLine { get; set; } + + [DataMember(Name = "script")] + public ScriptStruct Script { get; set; } + + [DataMember(Name = "text")] + public string Text { get; set; } + + [DataMember(Name = "exception")] + public DeferredObject<JsValue> Exception { get; set; } + + [DataContract] + public class ScriptStruct + { + [DataMember(Name = "name")] + public string Name { get; set; } + } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Frame.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Frame.cs new file mode 100644 index 0000000..0b62a1e --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Frame.cs @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + sealed class Frame + { + // { "index" : <frame number>, + // "receiver" : <frame receiver>, + // "func" : <function invoked>, + // "script" : <script for the function>, + // "constructCall" : <boolean indicating whether the function was called as + // constructor>, + // "debuggerFrame" : <boolean indicating whether this is an internal debugger frame>, + // "arguments" : [ { name: <name of the argument - missing of anonymous argument>, + // value: <value of the argument> + // }, + // ... <the array contains all the arguments> + // ], + // "locals" : [ { name: <name of the local variable>, + // value: <value of the local variable> + // }, + // ... <the array contains all the locals> + // ], + // "position" : <source position>, + // "line" : <source line>, + // "column" : <source column within the line>, + // "sourceLineText" : <text for current source line>, + // "scopes" : [ <array of scopes, see scope request below for format> ], + // } + [DataMember(Name = "index")] + public int Index { get; set; } + + [DataMember(Name = "receiver")] + public DeferredObject<JsValue> Receiver { get; set; } + + [DataMember(Name = "func")] + public string Function { get; set; } + + [DataMember(Name = "script")] + public string Script { get; set; } + + [DataMember(Name = "constructCall")] + public bool IsConstructCall { get; set; } + + [DataMember(Name = "debuggerFrame")] + public bool IsDebuggerFrame { get; set; } + + [DataMember(Name = "arguments")] + public List<VariableStruct> Arguments { get; set; } + + [DataMember(Name = "locals")] + public List<VariableStruct> Locals { get; set; } + + [DataMember(Name = "position")] + public string Position { get; set; } + + [DataMember(Name = "line")] + public int Line { get; set; } + + [DataMember(Name = "column")] + public int Column { get; set; } + + [DataMember(Name = "sourceLineText")] + public string SourceLineText { get; set; } + + [DataMember(Name = "scopes")] + public List<Scope> Scopes { get; set; } + + [DataContract] + public sealed class VariableStruct + { + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "value")] + public string Value { get; set; } + } + + } + + [DataContract] + sealed class FrameRequest : Request<FrameResponse, FrameRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "request", + // "command" : "frame", + // "arguments" : { "number" : <frame number> } + // } + public const string REQ_COMMAND = "frame"; + public FrameRequest() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "number")] + public int FrameNumber { get; set; } + } + } + + [DataContract] + sealed class FrameResponse : Response + { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "frame", + // "body" : { "index" : <frame number>, + // "receiver" : <frame receiver>, + // "func" : <function invoked>, + // "script" : <script for the function>, + // "constructCall" : <boolean indicating whether the function was + // called as constructor>, + // "debuggerFrame" : <boolean indicating whether this is an internal + // debugger frame>, + // "arguments" : [ { name: <name of the argument - missing of + // anonymous argument>, + // value: <value of the argument> + // }, + // ... <the array contains all the arguments> + // ], + // "locals" : [ { name: <name of the local variable>, + // value: <value of the local variable> + // }, + // ... <the array contains all the locals> + // ], + // "position" : <source position>, + // "line" : <source line>, + // "column" : <source column within the line>, + // "sourceLineText" : <text for current source line>, + // "scopes" : [ <array of scopes, see scope request below for + // format> ], + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + public const string REQ_COMMAND = FrameRequest.REQ_COMMAND; + public FrameResponse() : base() + { + Command = REQ_COMMAND; + } + + [DataMember(Name = "body")] + public Frame Frame { get; set; } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsObject.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsObject.cs new file mode 100644 index 0000000..a5a9272 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsObject.cs @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + class JsRef<TJsObject> : JsValue + where TJsObject : JsRef<TJsObject> + { + protected JsRef() + { + Type = DataType.Object; + Ref = null; + } + + [DataMember(Name = "ref")] + public int? Ref { get; set; } + + [DataMember(Name = "value")] + public int PropertyCount { get; set; } + + protected override bool? IsCompatible(JsValue obj) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(obj) == false) + return false; + + var _that = obj as JsRef<TJsObject>; + if (_that == null) + return null; + + return true; + } + } + + [DataContract] + class JsObjectRef : JsRef<JsObjectRef> + { + public JsObjectRef() + { } + + protected override bool? IsCompatible(JsValue obj) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(obj) == false) + return false; + + var _that = obj as JsObjectRef; + if (_that == null) + return null; + + return ((JsRef<JsObjectRef>)_that).Ref.HasValue; + } + + public new int Ref + { + get { return base.Ref.HasValue ? base.Ref.Value : 0; } + set { base.Ref = value; } + } + } + + [DataContract] + class JsObject : JsRef<JsObject> + { + // { "handle" : <handle>, + // "type" : "object", + // "className" : <Class name, ECMA-262 property [[Class]]>, + // "constructorFunction" : {"ref":<handle>}, + // "protoObject" : {"ref":<handle>}, + // "prototypeObject" : {"ref":<handle>}, + // "properties" : [ {"name" : <name>, + // "ref" : <handle> + // }, + // ... + // ] + // } + public JsObject() + { } + + protected override bool? IsCompatible(JsValue obj) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(obj) == false) + return false; + + var _that = obj as JsObject; + if (_that == null) + return null; + + return !_that.Ref.HasValue; + } + + [DataMember(Name = "className")] + public string ClassName { get; set; } + + [DataMember(Name = "constructorFunction")] + public DeferredObject<JsValue> Constructor { get; set; } + + [DataMember(Name = "protoObject")] + public DeferredObject<JsValue> ProtoObject { get; set; } + + [DataMember(Name = "prototypeObject")] + public DeferredObject<JsValue> PrototypeObject { get; set; } + + [DataMember(Name = "properties")] + public List<DeferredObject<JsValue>> Properties { get; set; } + + public IDictionary<string, JsValue> PropertiesByName + { + get + { + if (Properties == null) + return null; + + return Properties + .Where(x => x.Object != null + && !string.IsNullOrEmpty(x.Object.Name)) + .Select(x => x.Object) + .GroupBy(x => x.Name) + .ToDictionary(x => x.Key, x => x.First()); + } + } + + public bool IsArray + { + get + { + return !Properties.Where((x, i) => x.HasData + && ((JsValue)x).Name != i.ToString()).Any(); + } + } + } + + [DataContract] + class FunctionStruct : JsObject + { + // { "handle" : <handle>, + // "type" : "function", + // "className" : "Function", + // "constructorFunction" : {"ref":<handle>}, + // "protoObject" : {"ref":<handle>}, + // "prototypeObject" : {"ref":<handle>}, + // "name" : <function name>, + // "inferredName" : <inferred function name for anonymous functions> + // "source" : <function source>, + // "script" : <reference to function script>, + // "scriptId" : <id of function script>, + // "position" : <function begin position in script>, + // "line" : <function begin source line in script>, + // "column" : <function begin source column in script>, + // "properties" : [ {"name" : <name>, + // "ref" : <handle> + // }, + // ... + // ] + // } + public FunctionStruct() + { + Type = DataType.Function; + } + + [DataMember(Name = "inferredName")] + public string InferredName { get; set; } + + [DataMember(Name = "source")] + public string Source { get; set; } + + [DataMember(Name = "script")] + public string Script { get; set; } + + [DataMember(Name = "scriptId")] + public string ScriptId { get; set; } + + [DataMember(Name = "position")] + public string Position { get; set; } + + [DataMember(Name = "line")] + public int Line { get; set; } + + [DataMember(Name = "column")] + public int Column { get; set; } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsValue.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsValue.cs new file mode 100644 index 0000000..12de1a1 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4JsValue.cs @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.CodeDom; +using System.CodeDom.Compiler; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + class JsValue : Serializable<JsValue> + { + // { "handle" : <handle>, + // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" + // or "frame"> + // } + protected JsValue() + { } + + [DataMember(Name = "handle")] + public int Handle { get; set; } + + public enum DataType + { + [EnumString("undefined")] Undefined = 0, + [EnumString("null")] Null, + [EnumString("boolean")] Boolean, + [EnumString("number")] Number, + [EnumString("string")] String, + [EnumString("object")] Object, + [EnumString("function")] Function, + [EnumString("frame")] Frame + } + + [DataMember(Name = "type")] + protected string TypeString { get; set; } + + public DataType Type + { + get { return SerializableEnum.Deserialize<DataType>(TypeString); } + set { TypeString = SerializableEnum.Serialize<DataType>(value); } + } + + [DataMember(Name = "name")] + public string Name { get; set; } + + public override string ToString() + { + return string.Empty; + } + + protected override bool? IsCompatible(JsValue that) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(that) == false) + return false; + + if (that == null) + return false; + if (string.IsNullOrEmpty(TypeString)) + return null; + return (this.TypeString == that.TypeString); + } + + public static JsValue Create<T>(T value) + { + return (JsPrimitive<T>)value; + } + + protected static readonly CodeDomProvider JScriptProvider + = CodeDomProvider.CreateProvider("JScript"); + } + + [DataContract] + [SkipDeserialization] + class JsError : JsValue + { + public string Message { get; set; } + + public JsError() + { + Type = DataType.Undefined; + } + + protected override bool? IsCompatible(JsValue that) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + return false; + } + + public override string ToString() + { + return Message; + } + } + + [DataContract] + [SkipDeserialization] + class JsPrimitive : JsValue + { + public static string Format(object obj) + { + using (var writer = new StringWriter()) { + JScriptProvider.GenerateCodeFromExpression( + new CodePrimitiveExpression(obj), writer, null); + return writer.ToString(); + } + } + } + + [DataContract] + class JsUndefined : JsPrimitive + { + // {"handle":<handle>,"type":"undefined"} + public JsUndefined() + { + Type = DataType.Undefined; + } + + public override string ToString() + { + return "undefined"; + } + } + + [DataContract] + class JsNull : JsPrimitive + { + // {"handle":<handle>,"type":"null"} + public JsNull() + { + Type = DataType.Null; + } + + public override string ToString() + { + return "null"; + } + } + + [DataContract] + class JsNumberSymbolic : JsPrimitive + { + // {"handle":<handle>,"type":"null"} + public JsNumberSymbolic() + { + Type = DataType.Number; + } + + [DataMember(Name = "value")] + public virtual string Value { get; set; } + + protected override bool? IsCompatible(JsValue obj) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(obj) == false) + return false; + + var that = obj as JsNumberSymbolic; + if (that == null) + return null; + + var symbolicValues = new[] { "NaN", "Infinity", "+Infinity", "-Infinity" }; + return symbolicValues.Contains(that.Value); + } + + public override string ToString() + { + return Value; + } + } + + [DataContract] + [SkipDeserialization] + class JsPrimitive<T> : JsPrimitive + { + // { "handle" : <handle>, + // "type" : <"boolean", "number" or "string"> + // "value" : <JSON encoded value> + // } + protected JsPrimitive() + { } + + [DataMember(Name = "value")] + public virtual T Value { get; set; } + + public static implicit operator JsPrimitive<T>(T value) + { + foreach (var subType in SubClass.Get(typeof(JsPrimitive<T>)).SubTypes) { + var valueType = subType.GetGenericArguments().FirstOrDefault(); + if (valueType.IsAssignableFrom(typeof(T))) { + var _this = CreateInstance(subType) as JsPrimitive<T>; + if (_this == null) + return null; + _this.Value = value; + return _this; + } + } + return null; + } + + public static implicit operator T(JsPrimitive<T> _this) + { + return _this.Value; + } + + public override string ToString() + { + return Value.ToString(); + } + } + + [DataContract] + class JsBoolean : JsPrimitive<bool> + { + public JsBoolean() + { + Type = DataType.Boolean; + } + + public override string ToString() + { + return Value ? "true" : "false"; + } + } + + [DataContract] + class JsNumber : JsPrimitive<decimal> + { + public JsNumber() + { + Type = DataType.Number; + } + + public override string ToString() + { + if (Value - Math.Floor(Value) == 0) + return string.Format("{0:0}", Value); + return Format(Value); + } + } + + [DataContract] + class JsString : JsPrimitive<string> + { + public JsString() + { + Type = DataType.String; + } + + public override string ToString() + { + return Format(Value); + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Lookup.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Lookup.cs new file mode 100644 index 0000000..4d3b167 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Lookup.cs @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + sealed class LookupRequest : Request<LookupResponse, LookupRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "request", + // "command" : "lookup", + // "arguments" : { "handles" : <array of handles>, + // "includeSource" : <boolean indicating whether + // the source will be included when + // script objects are returned>, + // } + // } + public const string REQ_COMMAND = "lookup"; + public LookupRequest() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "handles")] + public List<int> Handles { get; set; } + + [DataMember(Name = "includeSource", EmitDefaultValue = false)] + public bool? IncludeSource { get; set; } + } + } + + [DataContract] + sealed class LookupResponse : Response + { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "lookup", + // "body" : <array of serialized objects indexed using their handle> + // "running" : <is the VM running after sending this response> + // "success" : true + // } + public const string REQ_COMMAND = LookupRequest.REQ_COMMAND; + public LookupResponse() : base() + { + Command = REQ_COMMAND; + } + + [DataMember(Name = "body")] + public Dictionary<string, DeferredObject<JsValue>> Objects { get; set; } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Message.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Message.cs new file mode 100644 index 0000000..ad1a5b9 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Message.cs @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + class Message : Serializable<Message> + { + // <message type> + // ... + protected Message() + { } + + public static T Create<T>(ProtocolDriver driver) + where T : Message, new() + { + return new T { Driver = driver }; + } + + public static bool Send<T>(ProtocolDriver driver, Action<T> initMsg = null) + where T : Message, new() + { + T _this = Create<T>(driver); + if (initMsg != null) + initMsg(_this); + return _this.Send(); + } + + public string Type { get; set; } + + protected sealed override void InitializeObject(object initArgs) + { + if (initArgs is string) + Type = initArgs as string; + } + + protected override bool? IsCompatible(Message that) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (that == null) + return false; + if (string.IsNullOrEmpty(Type)) + return null; + return (this.Type == that.Type); + } + + protected ProtocolDriver Driver { get; private set; } + + public virtual bool Send() + { + return Driver.SendMessage(this); + } + } + + [DataContract] + class Request : Message + { + // "v8request" + // { "seq" : <number>, + // "type" : "request", + // "command" : <command> + // ... + // } + public const string MSG_TYPE = "v8request"; + public const string MSG_SUBTYPE = "request"; + protected Request() : base() + { + Type = MSG_TYPE; + SubType = MSG_SUBTYPE; + } + + [DataMember(Name = "type")] + public string SubType { get; set; } + + [DataMember(Name = "command")] + public string Command { get; set; } + + [DataMember(Name = "seq")] + public int SequenceNum { get; set; } + + protected override bool? IsCompatible(Message msg) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(msg) == false) + return false; + + var that = msg as Request; + if (that == null) + return null; + + if (string.IsNullOrEmpty(SubType)) + return null; + + if (string.IsNullOrEmpty(Command)) + return null; + + return this.SubType == that.SubType && this.Command == that.Command; + } + + Response response = null; + public Response Response + { + get { return response; } + set { Atomic(() => response == null, () => response = value); } + } + + object tag = null; + public object Tag + { + get { return tag; } + set { Atomic(() => tag == null, () => tag = value); } + } + + public virtual ProtocolDriver.PendingRequest SendAsync() + { + return Driver.SendRequest(this); + } + + public new Response Send() + { + return SendAsync().WaitForResponse(); + } + + public static new Response Send<T>(ProtocolDriver driver, Action<T> initMsg = null) + where T : Request, new() + { + T _this = Create<T>(driver); + if (initMsg != null) + initMsg(_this); + return _this.Send(); + } + } + + [DataContract] + [SkipDeserialization] + class Request<TResponse> : Request + where TResponse : Response + { + public new TResponse Response + { get { return base.Response as TResponse; } } + + public new virtual TResponse Send() + { + var pendingRequest = SendAsync(); + if (!pendingRequest.RequestSent) + return null; + + if (pendingRequest.WaitForResponse() == null) + return null; + + return Response; + } + } + + [DataContract] + [SkipDeserialization] + class Request<TResponse, TArgs> : Request<TResponse> + where TResponse : Response + where TArgs : class, new() + { + [DataMember(Name = "arguments", EmitDefaultValue = false)] + TArgs args = null; + + public virtual TArgs Arguments + { + get { return ThreadSafe(() => (args != null) ? args : (args = new TArgs())); } + set { args = value; } + } + } + + [DataContract] + class ServerMessage : Message + { + // "v8message" + // { "seq" : <number>, + // "type" : <string> + // ... + // } + public const string MSG_TYPE = "v8message"; + protected ServerMessage() : base() + { + Type = MSG_TYPE; + } + + [DataMember(Name = "type")] + public string SubType { get; set; } + + [DataMember(Name = "seq")] + public int SequenceNum { get; set; } + + protected override bool? IsCompatible(Message msg) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(msg) == false) + return false; + + var that = msg as ServerMessage; + if (that == null) + return null; + + if (string.IsNullOrEmpty(SubType)) + return null; + + return this.SubType == that.SubType; + } + } + + [DataContract] + class Response : ServerMessage + { + // "v8message" + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : <command>, + // "running" : <is the VM running after sending this response>, + // "success" : <success?>, + // "message" : [error message] + // ... + // } + public const string MSG_SUBTYPE = "response"; + protected Response() : base() + { + SubType = MSG_SUBTYPE; + } + + [DataMember(Name = "command")] + public string Command { get; set; } + + [DataMember(Name = "request_seq")] + public int RequestSeq { get; set; } + + [DataMember(Name = "success")] + public bool Success { get; set; } + + [DataMember(Name = "running")] + public bool Running { get; set; } + + [DataMember(Name = "message")] + public string Message { get; set; } + + protected override bool? IsCompatible(Message msg) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(msg) == false) + return false; + + var that = msg as Response; + if (that == null) + return null; + + // If response is unsuccessful, no need to continue searching, just use this class, + // it already has all the data needed for error processing (i.e. the error message) + if (!that.Success) + return true; + + if (string.IsNullOrEmpty(Command)) + return null; + + return this.Command == that.Command; + } + } + + [DataContract] + [SkipDeserialization] + class Response<TBody> : Response + where TBody : class, new() + { + [DataMember(Name = "body", EmitDefaultValue = false)] + TBody body = null; + + public TBody Body + { + get { return ThreadSafe(() => (body != null) ? body : (body = new TBody())); } + set { body = value; } + } + } + + [DataContract] + class Event : ServerMessage + { + // "v8message" + // { "seq" : <number>, + // "type" : "event", + // "event" : <string>, + // ... + // } + public const string MSG_SUBTYPE = "event"; + protected Event() : base() + { + SubType = MSG_SUBTYPE; + } + + [DataMember(Name = "event")] + public string EventType { get; set; } + + protected override bool? IsCompatible(Message msg) + { + System.Diagnostics.Debug.Assert(IsPrototype); + + if (base.IsCompatible(msg) == false) + return false; + + var that = msg as Event; + if (that == null) + return null; + + if (string.IsNullOrEmpty(EventType)) + return null; + + return this.EventType == that.EventType; + } + } + + [DataContract] + [SkipDeserialization] + class Event<TBody> : Event + where TBody : class, new() + { + [DataMember(Name = "body", EmitDefaultValue = false)] + TBody body = null; + + public TBody Body + { + get { return ThreadSafe(() => (body != null) ? body : (body = new TBody())); } + set { body = value; } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Scope.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Scope.cs new file mode 100644 index 0000000..e042b6c --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Scope.cs @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + sealed class Scope + { + // { "index" : <index of this scope in the scope chain. Index 0 is the top scope + // and the global scope will always have the highest index for a + // frame>, + // "frameIndex" : <index of the frame>, + // "type" : <type of the scope: + // 0: Global + // 1: Local + // 2: With + // 3: Closure + // 4: Catch >, + // "object" : <the scope object defining the content of the scope. + // For local and closure scopes this is transient objects, + // which has a negative handle value> + // } + [DataMember(Name = "index")] + public int Index { get; set; } + + [DataMember(Name = "frameIndex")] + public int FrameIndex { get; set; } + + [DataContract] + public enum ScopeType + { + Global = 0, + Local = 1, + With = 2, + Closure = 3, + Catch = 4 + } + + [DataMember(Name = "type")] + public ScopeType Type { get; set; } + + [DataMember(Name = "object")] + public DeferredObject<JsValue> Object { get; set; } + } + + [DataContract] + sealed class ScopeRequest : Request<ScopeResponse, ScopeRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "request", + // "command" : "scope", + // "arguments" : { "number" : <scope number> + // "frameNumber" : <frame number, optional uses selected + // frame if missing> + // } + // } + public const string REQ_COMMAND = "scope"; + public ScopeRequest() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "number")] + public int ScopeNumber { get; set; } + + [DataMember(Name = "frameNumber", EmitDefaultValue = false)] + public int? FrameNumber { get; set; } + } + } + + [DataContract] + sealed class ScopeResponse : Response + { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "scope", + // "body" : { "index" : <index of this scope in the scope chain. Index 0 is + // the top scope + // and the global scope will always have the highest + // index for a + // frame>, + // "frameIndex" : <index of the frame>, + // "type" : <type of the scope: + // 0: Global + // 1: Local + // 2: With + // 3: Closure + // 4: Catch >, + // "object" : <the scope object defining the content of the scope. + // For local and closure scopes this is transient + // objects, + // which has a negative handle value> + // } + // "running" : <is the VM running after sending this response> + // "success" : true + // } + public const string REQ_COMMAND = ScopeRequest.REQ_COMMAND; + public ScopeResponse() : base() + { + Command = REQ_COMMAND; + } + + [DataMember(Name = "body")] + public Scope Scope { get; set; } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetBreakpoint.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetBreakpoint.cs new file mode 100644 index 0000000..f5da850 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetBreakpoint.cs @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + [DataContract] + sealed class SetBreakpointRequest + : Request<SetBreakpointResponse, SetBreakpointRequest.ArgumentsStruct> + { + // "v8request" + // { "seq" : <number>, + // "type" : "request", + // "command" : "setbreakpoint", + // "arguments" : { "type" : <"function" or "script" or "scriptId" + // or "scriptRegExp">, + // "target" : <function expression or script identification>, + // "line" : <line in script or function>, + // "column" : <character position within the line>, + // "enabled" : <initial enabled state. True or false, default is + // true>, + // "condition" : <string with break point condition>, + // "ignoreCount" : <number specifying the number of break point hits to + // ignore, default value is 0> + // } + // } + public const string REQ_COMMAND = "setbreakpoint"; + public SetBreakpointRequest() : base() + { + Command = REQ_COMMAND; + } + + public enum TargetType + { + [EnumString("function")] Function = 0, + [EnumString("script")] Script, + [EnumString("scriptId")] ScriptId, + [EnumString("scriptRegExp")] ScriptRegExp + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "type")] + private string TargetTypeString { get; set; } + + public TargetType TargetType + { + get { return SerializableEnum.Deserialize<TargetType>(TargetTypeString); } + set { TargetTypeString = SerializableEnum.Serialize<TargetType>(value); } + } + + [DataMember(Name = "target")] + public string Target { get; set; } + + [DataMember(Name = "line")] + public int Line { get; set; } + + [DataMember(Name = "column", EmitDefaultValue = false)] + public int? Column { get; set; } + + [DataMember(Name = "condition", EmitDefaultValue = false)] + public string Condition { get; set; } + + [DataMember(Name = "enabled")] + public bool Enabled { get; set; } + + [DataMember(Name = "ignoreCount", EmitDefaultValue = false)] + public int? IgnoreCount { get; set; } + + public ArgumentsStruct() + { + Enabled = true; + } + } + } + + [DataContract] + sealed class SetBreakpointResponse : Response<SetBreakpointResponse.BodyStruct> + { + // "v8message" + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "setbreakpoint", + // "body" : { "type" : <"function" or "script">, + // "breakpoint" : <break point number of the new break point>, + // }, + // "running" : <is the VM running after sending this response>, + // "success" : true + // } + public const string REQ_COMMAND = SetBreakpointRequest.REQ_COMMAND; + public SetBreakpointResponse() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class BodyStruct + { + [DataMember(Name = "type")] + public string Type { get; set; } + + [DataMember(Name = "breakpoint")] + public int Breakpoint { get; set; } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetExceptionBreak.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetExceptionBreak.cs new file mode 100644 index 0000000..f9d07ab --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4SetExceptionBreak.cs @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + using Json; + + public enum ExceptionBreakType + { + [EnumString("all")] All = 0, + [EnumString("uncaught")] Uncaught, + } + + [DataContract] + sealed class SetExceptionBreakRequest + : Request<SetExceptionBreakResponse, SetExceptionBreakRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "request", + // "command" : "setexceptionbreak", + // "arguments" : { "type" : <string: "all", or "uncaught">, + // "enabled" : <optional bool: enables the break type if true> + // } + // } + public const string REQ_COMMAND = "setexceptionbreak"; + public SetExceptionBreakRequest() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class ArgumentsStruct + { + [DataMember(Name = "type")] + private string TypeString { get; set; } + + public ExceptionBreakType ExceptionBreakType + { + get { return SerializableEnum.Deserialize<ExceptionBreakType>(TypeString); } + set { TypeString = SerializableEnum.Serialize<ExceptionBreakType>(value); } + } + + [DataMember(Name = "enabled")] + public bool Enabled { get; set; } + } + } + + [DataContract] + sealed class SetExceptionBreakResponse : Response<SetExceptionBreakRequest.ArgumentsStruct> + { + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "setexceptionbreak", + // "body" : { "type" : <string: "all" or "uncaught" corresponding to the + // request.>, + // "enabled" : <bool: true if the break type is currently enabled + // as a result of the request> + // } + // "running" : true + // "success" : true + // } + public const string REQ_COMMAND = SetExceptionBreakRequest.REQ_COMMAND; + public SetExceptionBreakResponse() : base() + { + Command = REQ_COMMAND; + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Version.cs b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Version.cs new file mode 100644 index 0000000..0fcb060 --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/Messages/QmlDebugV4Version.cs @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Runtime.Serialization; + +namespace QtVsTools.Qml.Debug.V4 +{ + [DataContract] + sealed class VersionRequest : Request<VersionResponse> + { + // "v8request" + // { "seq" : <number>, + // "type" : "request", + // "command" : "version" + // } + public const string REQ_COMMAND = "version"; + public VersionRequest() : base() + { + Command = REQ_COMMAND; + } + } + + [DataContract] + sealed class VersionResponse : Response<VersionResponse.BodyStruct> + { + // "v8message" + // { "seq" : <number>, + // "type" : "response", + // "request_seq" : <number>, + // "command" : "version", + // "body" : { "UnpausedEvaluate" : <bool>, + // "ContextEvaluate" : <bool>, + // "V8Version" : <string> + // }, + // "running" : <is the VM running after sending this response>, + // "success" : true + // } + public const string REQ_COMMAND = VersionRequest.REQ_COMMAND; + public VersionResponse() : base() + { + Command = REQ_COMMAND; + } + + [DataContract] + public class BodyStruct + { + [DataMember(Name = "UnpausedEvaluate")] + public bool UnpausedEvaluate { get; set; } + + [DataMember(Name = "ContextEvaluate")] + public bool ContextEvaluate { get; set; } + + [DataMember(Name = "V8Version")] + public string Version { get; set; } + } + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Client.cs b/QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Client.cs new file mode 100644 index 0000000..abbf6de --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Client.cs @@ -0,0 +1,421 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using QtVsTools.Options; +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace QtVsTools.Qml.Debug.V4 +{ + enum DebugClientState { Unavailable, Disconnected, Connecting, Connected, Disconnecting } + + interface IConnectionEventSink + { + bool QueryRuntimeFrozen(); + + void NotifyStateTransition( + DebugClient client, + DebugClientState oldState, + DebugClientState newState); + + void NotifyMessageReceived( + DebugClient client, + string messageType, + byte[] messageParams); + } + + class DebugClient : Finalizable + { + IConnectionEventSink sink; + IntPtr client; + Task clientThread; + EventWaitHandle clientCreated = new EventWaitHandle(false, EventResetMode.ManualReset); + EventWaitHandle clientConnected; + + public uint? ThreadId { get; private set; } + + DebugClientState state = DebugClientState.Unavailable; + public DebugClientState State + { + get + { + if (clientThread == null || clientThread.Status != TaskStatus.Running) + return DebugClientState.Unavailable; + + return state; + } + set + { + if (state != value) { + var oldState = state; + state = value; + Task.Run(() => sink.NotifyStateTransition(this, oldState, value)); + } + } + } + + public static DebugClient Create(IConnectionEventSink sink) + { + var _this = new DebugClient(); + return _this.Initialize(sink) ? _this : null; + } + + private DebugClient() + { } + + private bool Initialize(IConnectionEventSink sink) + { + this.sink = sink; + + Task.WaitAny(new[] + { + // Try to start client thread + // Unblock if thread was abruptly terminated (e.g. DLL not found) + clientThread = Task.Run(() => ClientThread()), + + // Unblock if client was created (i.e. client thread is running) + Task.Run(() => clientCreated.WaitOne()) + }); + + if (State == DebugClientState.Unavailable) { + // Client thread did not start + clientCreated.Set(); + Dispose(); + return false; + } + + return true; + } + + protected override void DisposeManaged() + { + clientCreated.Dispose(); + + EnterCriticalSection(); + if (clientConnected != null) { + LeaveCriticalSection(); + clientConnected.Dispose(); + } + } + + protected override void DisposeUnmanaged() + { + if (State != DebugClientState.Unavailable) { + NativeMethods.DebugClientShutdown(client); + clientThread.Wait(); + } + } + + private void ClientThread() + { + ThreadId = NativeMethods.GetCurrentThreadId(); + + var clientCreated = + new NativeMethods.QmlDebugClientCreated(ClientCreated); + var clientDestroyed = + new NativeMethods.QmlDebugClientDestroyed(ClientDestroyed); + var clientConnected = + new NativeMethods.QmlDebugClientConnected(ClientConnected); + var clientDisconnected = + new NativeMethods.QmlDebugClientDisconnected(ClientDisconnected); + var clientMessageReceived = + new NativeMethods.QmlDebugClientMessageReceived(ClientMessageReceived); + try { + NativeMethods.DebugClientThread( + clientCreated, clientDestroyed, + clientConnected, clientDisconnected, + clientMessageReceived); + } finally { + State = DebugClientState.Unavailable; + GC.KeepAlive(clientCreated); + GC.KeepAlive(clientDestroyed); + GC.KeepAlive(clientConnected); + GC.KeepAlive(clientDisconnected); + GC.KeepAlive(clientMessageReceived); + } + } + + public EventWaitHandle Connect(string hostName, ushort hostPort) + { + if (State != DebugClientState.Disconnected) + return null; + + clientConnected = new EventWaitHandle(false, EventResetMode.ManualReset); + State = DebugClientState.Connecting; + if (string.IsNullOrEmpty(hostName)) + hostName = "localhost"; + var hostNameData = Encoding.UTF8.GetBytes(hostName); + + uint timeout = (uint)QtVsToolsPackage.Instance.Options.QmlDebuggerTimeout; + Task.Run(() => + { + var connectTimer = new System.Diagnostics.Stopwatch(); + connectTimer.Start(); + + var probe = new Socket( + AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + while (!probe.Connected + && (timeout == 0 || connectTimer.ElapsedMilliseconds < timeout)) { + try { + probe.Connect(hostName, hostPort); + } catch { + Thread.Sleep(3000); + } + } + + if (probe.Connected) { + probe.Disconnect(false); + + NativeMethods.DebugClientConnect(client, + hostNameData, hostNameData.Length, hostPort); + connectTimer.Restart(); + + uint connectionTimeout = Math.Max(3000, timeout / 20); + while (!clientConnected.WaitOne(1000)) { + + if (sink.QueryRuntimeFrozen()) { + connectTimer.Restart(); + + } else { + if (connectTimer.ElapsedMilliseconds > connectionTimeout) { + if (!Disposing) + clientConnected.Set(); + + if (Atomic(() => State == DebugClientState.Connected, + () => State = DebugClientState.Disconnecting)) { + NativeMethods.DebugClientDisconnect(client); + } + + } else { + NativeMethods.DebugClientConnect(client, + hostNameData, hostNameData.Length, hostPort); + } + } + } + } + }); + + return clientConnected; + } + + public EventWaitHandle StartLocalServer(string fileName) + { + if (State != DebugClientState.Disconnected) + return null; + + clientConnected = new EventWaitHandle(false, EventResetMode.ManualReset); + State = DebugClientState.Connecting; + var fileNameData = Encoding.UTF8.GetBytes(fileName); + if (!NativeMethods.DebugClientStartLocalServer(client, + fileNameData, fileNameData.Length)) { + return null; + } + + uint timeout = (uint)QtVsToolsPackage.Instance.Options.QmlDebuggerTimeout; + if (timeout != 0) { + Task.Run(() => + { + var connectTimer = new System.Diagnostics.Stopwatch(); + connectTimer.Start(); + + while (!clientConnected.WaitOne(100)) { + + if (sink.QueryRuntimeFrozen()) { + connectTimer.Restart(); + + } else { + if (connectTimer.ElapsedMilliseconds > timeout) { + if (!Disposing) + clientConnected.Set(); + + if (Atomic(() => State == DebugClientState.Connected, + () => State = DebugClientState.Disconnecting)) { + NativeMethods.DebugClientDisconnect(client); + } + } + } + } + }); + } + + return clientConnected; + } + + public bool Disconnect() + { + if (State != DebugClientState.Connected) + return false; + State = DebugClientState.Disconnecting; + return NativeMethods.DebugClientDisconnect(client); + } + + public bool SendMessage(string messageType, byte[] messageParams) + { + if (State != DebugClientState.Connected) + return false; + var messageTypeData = Encoding.UTF8.GetBytes(messageType); + if (messageParams == null) + messageParams = new byte[0]; + + System.Diagnostics.Debug.WriteLine(string.Format(">> {0} {1}", + messageType, Encoding.UTF8.GetString(messageParams))); + + return NativeMethods.DebugClientSendMessage(client, + messageTypeData, messageTypeData.Length, + messageParams, messageParams.Length); + } + + void ClientCreated(IntPtr qmlDebugClient) + { + if (client != IntPtr.Zero || Disposing) + return; + + client = qmlDebugClient; + State = DebugClientState.Disconnected; + clientCreated.Set(); + } + + void ClientDestroyed(IntPtr qmlDebugClient) + { + if (qmlDebugClient != client) + return; + State = DebugClientState.Unavailable; + } + + void ClientConnected(IntPtr qmlDebugClient) + { + if (qmlDebugClient != client || Disposing) + return; + State = DebugClientState.Connected; + clientConnected.Set(); + } + + void ClientDisconnected(IntPtr qmlDebugClient) + { + if (qmlDebugClient != client) + return; + State = DebugClientState.Disconnected; + } + + void ClientMessageReceived( + IntPtr qmlDebugClient, + byte[] messageTypeData, + int messageTypeLength, + byte[] messageParamsData, + int messageParamsLength) + { + if (Disposed) + return; + if (qmlDebugClient != client) + return; + var messageType = Encoding.UTF8.GetString(messageTypeData); + + System.Diagnostics.Debug.WriteLine(string.Format("<< {0} {1}", + messageType, Encoding.UTF8.GetString(messageParamsData))); + + sink.NotifyMessageReceived(this, messageType, messageParamsData); + } + + #region //////////////////// Native Methods /////////////////////////////////////////////// + + internal static class NativeMethods + { + public delegate void QmlDebugClientCreated(IntPtr qmlDebugClient); + public delegate void QmlDebugClientDestroyed(IntPtr qmlDebugClient); + public delegate void QmlDebugClientConnected(IntPtr qmlDebugClient); + public delegate void QmlDebugClientDisconnected(IntPtr qmlDebugClient); + public delegate void QmlDebugClientMessageReceived( + IntPtr qmlDebugClient, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] messageTypeData, + int messageTypeLength, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] messageParamsData, + int messageParamsLength); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlDebugClientThread")] + public static extern bool DebugClientThread( + QmlDebugClientCreated clientCreated, + QmlDebugClientDestroyed clientDestroyed, + QmlDebugClientConnected clientConnected, + QmlDebugClientDisconnected clientDisconnected, + QmlDebugClientMessageReceived clientMessageReceived); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlDebugClientDisconnect")] + public static extern bool DebugClientDisconnect(IntPtr qmlDebugClient); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlDebugClientConnect")] + public static extern bool DebugClientConnect( + IntPtr qmlDebugClient, + [MarshalAs(UnmanagedType.LPArray)] byte[] hostNameData, + int hostNameLength, + ushort hostPort); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlDebugClientStartLocalServer")] + public static extern bool DebugClientStartLocalServer( + IntPtr qmlDebugClient, + [MarshalAs(UnmanagedType.LPArray)] byte[] fileNameData, + int fileNameLength); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlDebugClientSendMessage")] + public static extern bool DebugClientSendMessage( + IntPtr qmlDebugClient, + [MarshalAs(UnmanagedType.LPArray)] byte[] messageTypeData, + int messageTypeLength, + [MarshalAs(UnmanagedType.LPArray)] byte[] messageParamsData, + int messageParamsLength); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlDebugClientShutdown")] + public static extern bool DebugClientShutdown(IntPtr qmlDebugClient); + + [DllImport("kernel32.dll")] + public static extern uint GetCurrentThreadId(); + } + + #endregion //////////////////// Native Methods //////////////////////////////////////////// + + } +} diff --git a/QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Protocol.cs b/QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Protocol.cs new file mode 100644 index 0000000..07315df --- /dev/null +++ b/QtVsTools.Package/QML/Debugging/V4/QmlDebugV4Protocol.cs @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace QtVsTools.Qml.Debug.V4 +{ + interface IMessageEventSink + { + bool QueryRuntimeFrozen(); + + void NotifyStateTransition( + DebugClient client, + DebugClientState oldState, + DebugClientState newState); + + void NotifyRequestResponded(Request req); + void NotifyEvent(Event evt); + void NotifyMessage(Message msg); + } + + class ProtocolDriver : Finalizable, IConnectionEventSink + { + IMessageEventSink sink; + DebugClient client; + int nextRequestSeq = 0; + Dictionary<int, PendingRequest> pendingRequests = new Dictionary<int, PendingRequest>(); + Task eventHandlingThread; + EventWaitHandle eventReceived = new EventWaitHandle(false, EventResetMode.AutoReset); + ConcurrentQueue<Event> eventQueue = new ConcurrentQueue<Event>(); + + public uint? ThreadId { get { return client.ThreadId; } } + + public static ProtocolDriver Create(IMessageEventSink sink) + { + var _this = new ProtocolDriver(); + return _this.Initialize(sink) ? _this : null; + } + + private ProtocolDriver() + { } + + private bool Initialize(IMessageEventSink sink) + { + this.sink = sink; + eventHandlingThread = Task.Run(() => EventHandlingThread()); + client = DebugClient.Create(this); + if (client == null) + return false; + + return true; + } + + protected override void DisposeManaged() + { + foreach (var req in ThreadSafe(() => pendingRequests.Values.ToList())) + req.Dispose(); + + ThreadSafe(() => pendingRequests.Clear()); + client.Dispose(); + } + + protected override void DisposeFinally() + { + eventReceived.Set(); + eventHandlingThread.Wait(); + eventReceived.Dispose(); + } + + private void EventHandlingThread() + { + while (!Disposing) { + eventReceived.WaitOne(); + Event evt; + while (!Disposing && eventQueue.TryDequeue(out evt)) + sink.NotifyEvent(evt); + } + } + + public DebugClientState ConnectionState + { + get { return client.State; } + } + + bool IConnectionEventSink.QueryRuntimeFrozen() + { + return sink.QueryRuntimeFrozen(); + } + + public EventWaitHandle Connect(string hostName, ushort hostPort) + { + return client.Connect(hostName, hostPort); + } + + public EventWaitHandle StartLocalServer(string fileName) + { + return client.StartLocalServer(fileName); + } + + public bool Disconnect() + { + return client.Disconnect(); + } + + public bool SendMessage(Message message) + { + if (client.State != DebugClientState.Connected) + return false; + + var messageParams = message.Serialize(); + + System.Diagnostics.Debug + .Assert(messageParams != null, "Empty message data"); + + return client.SendMessage(message.Type, messageParams); + } + + public PendingRequest SendRequest(Request request) + { + ThreadSafe(() => request.SequenceNum = nextRequestSeq++); + + var pendingRequest = new PendingRequest(request); + ThreadSafe(() => pendingRequests[request.SequenceNum] = pendingRequest); + + if (!SendMessage(request as Message)) { + ThreadSafe(() => pendingRequests.Remove(request.SequenceNum)); + pendingRequest.Dispose(); + return new PendingRequest(); + } + + return pendingRequest; + } + + void IConnectionEventSink.NotifyStateTransition( + DebugClient client, + DebugClientState oldState, + DebugClientState newState) + { + sink.NotifyStateTransition(client, oldState, newState); + } + + void IConnectionEventSink.NotifyMessageReceived( + DebugClient client, + string messageType, + byte[] messageParams) + { + if (client != this.client || Disposing) + return; + + var msg = Message.Deserialize(messageType, messageParams); + if (msg == null) + return; + + if (msg is Response) { + var msgResponse = msg as Response; + EnterCriticalSection(); + PendingRequest pendingRequest = null; + if (pendingRequests.TryGetValue(msgResponse.RequestSeq, out pendingRequest)) { + pendingRequests.Remove(msgResponse.RequestSeq); + LeaveCriticalSection(); + pendingRequest.SetResponse(msgResponse); + sink.NotifyRequestResponded(pendingRequest.Request); + pendingRequest.Dispose(); + } else { + LeaveCriticalSection(); + sink.NotifyMessage(msg); + } + + } else if (msg is Event) { + var msgEvent = msg as Event; + eventQueue.Enqueue(msgEvent); + eventReceived.Set(); + + } else { + sink.NotifyMessage(msg); + } + } + + #region //////////////////// PendingRequest /////////////////////////////////////////////// + + public class PendingRequest : Finalizable + { + public Request Request { get; private set; } + EventWaitHandle responded; + + public PendingRequest() + { + Request = null; + responded = null; + } + + public PendingRequest(Request req) + { + responded = new EventWaitHandle(false, EventResetMode.ManualReset); + Request = req; + } + + public bool RequestSent + { + get { return Request != null; } + } + + public bool ResponseReceived + { + get { return Request != null && Request.Response != null; } + } + + public Response WaitForResponse() + { + if (Request == null) + return null; + if (responded != null) + responded.WaitOne(); + return Request.Response; + } + + protected override void DisposeManaged() + { + if (Request == null) + return; + + if (responded != null) + responded.Dispose(); + } + + public void SetResponse(Response res) + { + if (Request == null || Disposing) + return; + + Request.Response = res; + if (responded != null) + responded.Set(); + } + } + + #endregion //////////////////// PendingRequest //////////////////////////////////////////// + + } +} diff --git a/QtVsTools.Package/QML/Parser/QmlParserDiagnostics.cs b/QtVsTools.Package/QML/Parser/QmlParserDiagnostics.cs new file mode 100644 index 0000000..484dc03 --- /dev/null +++ b/QtVsTools.Package/QML/Parser/QmlParserDiagnostics.cs @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Qml +{ + using Syntax; + + public enum DiagnosticMessageKind { Warning, Error } + + /// <summary> + /// Represents a syntax error issued by the QML parser + /// </summary> + public class DiagnosticMessage + { + public DiagnosticMessageKind Kind { get; set; } + public SourceLocation Location { get; set; } + public DiagnosticMessage(DiagnosticMessageKind kind, int offset, int length) + { + Kind = kind; + Location = new SourceLocation + { + Offset = offset, + Length = length + }; + } + } +} diff --git a/QtVsTools.Package/QML/Parser/QmlParserInterop.cs b/QtVsTools.Package/QML/Parser/QmlParserInterop.cs new file mode 100644 index 0000000..8ff5d87 --- /dev/null +++ b/QtVsTools.Package/QML/Parser/QmlParserInterop.cs @@ -0,0 +1,567 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// This file contains the integration with the Qt Declarative parser. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; + +namespace QtVsTools.Qml +{ + using Syntax; + + /// <summary> + /// Implements the integration with the Qt declarative parser, including: + /// * Managed-unmanaged interop with the vsqml DLL; + /// * Unmarshaling of syntax element data types (e.g. AST nodes); + /// * Visitor role for AST traversal. + /// </summary> + class Parser : IDisposable + { + internal static class NativeMethods + { + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlGetTokens")] + internal static extern bool GetTokens( + IntPtr qmlText, + int qmlTextLength, + ref IntPtr tokens, + ref int tokensLength); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlFreeTokens")] + internal static extern bool FreeTokens(IntPtr tokens); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlParse")] + internal static extern bool Parse( + IntPtr qmlText, + int qmlTextLength, + ref IntPtr parser, + ref bool parsedCorrectly, + ref IntPtr diagnosticMessages, + ref int diagnosticMessagesLength, + ref IntPtr comments, + ref int commentsLength); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlFreeParser")] + internal static extern bool FreeParser(IntPtr parser); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlFreeDiagnosticMessages")] + internal static extern bool FreeDiagnosticMessages(IntPtr diagnosticMessages); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlFreeComments")] + internal static extern bool FreeComments(IntPtr comments); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlGetAstVisitor")] + internal static extern IntPtr GetAstVisitor(); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlFreeAstVisitor")] + internal static extern bool FreeAstVisitor(IntPtr astVisitor); + + internal delegate bool Callback( + IntPtr astVisitor, + int nodeKind, + IntPtr node, + bool beginVisit, + IntPtr nodeData, + int nodeDataLength); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlSetAstVisitorCallback")] + internal static extern bool SetAstVisitorCallback( + IntPtr astVisitor, + int nodeKindFilter, + Callback visitCallback); + + [DllImport("vsqml", + CallingConvention = CallingConvention.Cdecl, + EntryPoint = "qmlAcceptAstVisitor")] + internal static extern bool AcceptAstVisitor( + IntPtr parser, + IntPtr node, + IntPtr astVisitor); + } + + /// <summary> + /// List of "interesting" AST node types. To optimize the managed-unmanaged interop, + /// during AST traversal, only these node types will be reported. + /// </summary> + static readonly List<AstNodeKind> CallbackFilters = + new List<AstNodeKind> { + AstNodeKind.UiImport, + AstNodeKind.UiQualifiedId, + AstNodeKind.UiObjectDefinition, + AstNodeKind.UiObjectBinding, + AstNodeKind.UiObjectInitializer, + AstNodeKind.UiScriptBinding, + AstNodeKind.UiArrayBinding, + AstNodeKind.UiPublicMember, + AstNodeKind.FieldMemberExpression, + AstNodeKind.IdentifierExpression + }; + + IntPtr qmlTextPtr = IntPtr.Zero; + IntPtr qmlParserPtr = IntPtr.Zero; + + List<Token> tokens; + public IEnumerable<Token> Tokens + { + get { return tokens; } + } + + List<DiagnosticMessage> diagnosticMessages; + public IEnumerable<DiagnosticMessage> DiagnosticMessages + { + get { return diagnosticMessages; } + } + + public int FirstErrorOffset { get; private set; } + + List<AstNode> visitedNodes; + public IEnumerable<AstNode> AstNodes { get { return visitedNodes; } } + + public bool ParsedCorrectly { get; private set; } + + Dictionary<IntPtr, AstNode> nodesBytPtr; + Dictionary<IntPtr, List<KeyValuePair<AstNode, PropertyInfo>>> pendingDereferences; + + Parser() + { + tokens = new List<Token>(); + diagnosticMessages = new List<DiagnosticMessage>(); + nodesBytPtr = new Dictionary<IntPtr, AstNode>(); + pendingDereferences = new Dictionary<IntPtr, + List<KeyValuePair<AstNode, PropertyInfo>>>(); + + visitedNodes = new List<AstNode>(); + } + + public static Parser Parse(string qmlText) + { + var parser = new Parser(); + parser.Work(qmlText); + return parser; + } + + void Work(string qmlText) + { + // The Qt Declarative parser ignores CR's. However, the Visual Studio editor does not. + // To ensure that offsets are compatible, CR's are replaced with spaces. + string qmlTextNormalized = qmlText.Replace('\r', ' '); + var qmlTextData = Encoding.UTF8.GetBytes(qmlTextNormalized); + + qmlTextPtr = Marshal.AllocHGlobal(qmlTextData.Length); + Marshal.Copy(qmlTextData, 0, qmlTextPtr, qmlTextData.Length); + + IntPtr tokensPtr = IntPtr.Zero; + int tokensLength = 0; + + NativeMethods.GetTokens(qmlTextPtr, qmlTextData.Length, + ref tokensPtr, ref tokensLength); + + if (tokensPtr != IntPtr.Zero) { + var tokensData = new byte[tokensLength]; + Marshal.Copy(tokensPtr, tokensData, 0, tokensLength); + + using (var rdr = new BinaryReader(new MemoryStream(tokensData))) { + while (rdr.BaseStream.Position + (3 * sizeof(int)) <= tokensLength) { + int kind = rdr.ReadInt32(); + int offset = rdr.ReadInt32(); + int length = rdr.ReadInt32(); + tokens.Add(Token.Create((TokenKind)kind, offset, length)); + } + } + NativeMethods.FreeTokens(tokensPtr); + } + + bool parsedCorrectly = false; + IntPtr diagnosticMessagesPtr = IntPtr.Zero; + int diagnosticMessagesLength = 0; + IntPtr commentsPtr = IntPtr.Zero; + int commentsLength = 0; + + NativeMethods.Parse(qmlTextPtr, qmlTextData.Length, + ref qmlParserPtr, ref parsedCorrectly, + ref diagnosticMessagesPtr, ref diagnosticMessagesLength, + ref commentsPtr, ref commentsLength); + + ParsedCorrectly = parsedCorrectly; + + if (diagnosticMessagesPtr != IntPtr.Zero) { + var diagnosticMessagesData = new byte[diagnosticMessagesLength]; + Marshal.Copy( + diagnosticMessagesPtr, + diagnosticMessagesData, 0, diagnosticMessagesLength); + + FirstErrorOffset = qmlTextNormalized.Length + 1; + using (var rdr = new BinaryReader(new MemoryStream(diagnosticMessagesData))) { + while (rdr.BaseStream.Position + (3 * sizeof(int)) <= diagnosticMessagesLength) { + var kind = (DiagnosticMessageKind)rdr.ReadInt32(); + int offset = rdr.ReadInt32(); + int length = rdr.ReadInt32(); + diagnosticMessages.Add(new DiagnosticMessage(kind, offset, length)); + if (kind == DiagnosticMessageKind.Error && offset < FirstErrorOffset) + FirstErrorOffset = offset; + } + } + NativeMethods.FreeDiagnosticMessages(diagnosticMessagesPtr); + } + + if (commentsPtr != IntPtr.Zero) { + var commentsData = new byte[commentsLength]; + Marshal.Copy(commentsPtr, commentsData, 0, commentsLength); + + using (var rdr = new BinaryReader(new MemoryStream(commentsData))) { + while (rdr.BaseStream.Position + (2 * sizeof(int)) <= commentsLength) { + int offset = rdr.ReadInt32(); + int length = rdr.ReadInt32(); + tokens.Add(Token.Create(TokenKind.T_COMMENT, offset, length)); + } + } + NativeMethods.FreeComments(commentsPtr); + } + + var astVisitor = NativeMethods.GetAstVisitor(); + var callback = new NativeMethods.Callback(VisitorCallback); + + foreach (var callbackFilter in CallbackFilters) + NativeMethods.SetAstVisitorCallback(astVisitor, (int)callbackFilter, callback); + + NativeMethods.AcceptAstVisitor(qmlParserPtr, IntPtr.Zero, astVisitor); + + while (pendingDereferences.Count > 0) { + var deref = pendingDereferences.First(); + NativeMethods.AcceptAstVisitor(qmlParserPtr, deref.Key, astVisitor); + pendingDereferences.Remove(deref.Key); + } + + GC.KeepAlive(callback); + NativeMethods.FreeAstVisitor(astVisitor); + } + + SourceLocation UnmarshalLocation(BinaryReader nodeData) + { + try { + return new SourceLocation + { + Offset = nodeData.ReadInt32(), + Length = nodeData.ReadInt32(), + }; + } catch (Exception) { + return new SourceLocation(); + } + } + + void UnmarshalPointer(BinaryReader nodeData, AstNode node, PropertyInfo nodeProperty) + { + if (nodeData == null || node == null || nodeProperty == null) + return; + + IntPtr ptrRef; + try { + long ptrHi = nodeData.ReadInt32(); + long ptrLo = nodeData.ReadInt32(); + ptrRef = new IntPtr((ptrHi << 32) | (ptrLo & 0xFFFFFFFFL)); + } catch (Exception) { + return; + } + + if (ptrRef == IntPtr.Zero) + return; + + AstNode nodeRef; + if (nodesBytPtr.TryGetValue(ptrRef, out nodeRef)) { + nodeProperty.SetValue(node, nodeRef); + } else { + List<KeyValuePair<AstNode, PropertyInfo>> pendingRefList; + if (!pendingDereferences.TryGetValue(ptrRef, out pendingRefList)) { + pendingDereferences[ptrRef] = pendingRefList = + new List<KeyValuePair<AstNode, PropertyInfo>>(); + } + pendingRefList.Add(new KeyValuePair<AstNode, PropertyInfo>(node, nodeProperty)); + } + } + + void UnmarshalNode(BinaryReader nodeData, AstNode node) + { + node.FirstSourceLocation = UnmarshalLocation(nodeData); + node.LastSourceLocation = UnmarshalLocation(nodeData); + } + + AstNode UnmarshalNode(BinaryReader nodeData, AstNodeKind nodeKind) + { + var node = new AstNode(nodeKind); + UnmarshalNode(nodeData, node); + return node; + } + + UiImport UnmarshalUiImport(BinaryReader nodeData) + { + var node = new UiImport(); + UnmarshalNode(nodeData, node); + node.ImportToken = UnmarshalLocation(nodeData); + node.FileNameToken = UnmarshalLocation(nodeData); + node.VersionToken = UnmarshalLocation(nodeData); + node.AsToken = UnmarshalLocation(nodeData); + node.ImportIdToken = UnmarshalLocation(nodeData); + node.SemicolonToken = UnmarshalLocation(nodeData); + return node; + } + + UiQualifiedId UnmarshalUiQualifiedId(BinaryReader nodeData) + { + var node = new UiQualifiedId(); + UnmarshalNode(nodeData, node); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Next)); + node.IdentifierToken = UnmarshalLocation(nodeData); + return node; + } + + UiObjectDefinition UnmarshalUiObjectDefinition(BinaryReader nodeData) + { + var node = new UiObjectDefinition(); + UnmarshalNode(nodeData, node); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedTypeNameId)); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Initializer)); + return node; + } + + UiObjectBinding UnmarshalUiObjectBinding(BinaryReader nodeData) + { + var node = new UiObjectBinding(); + UnmarshalNode(nodeData, node); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedId)); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedTypeNameId)); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Initializer)); + node.ColonToken = UnmarshalLocation(nodeData); + return node; + } + + UiScriptBinding UnmarshalUiScriptBinding(BinaryReader nodeData) + { + var node = new UiScriptBinding(); + UnmarshalNode(nodeData, node); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedId)); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Statement)); + node.ColonToken = UnmarshalLocation(nodeData); + return node; + } + + UiArrayBinding UnmarshalUiArrayBinding(BinaryReader nodeData) + { + var node = new UiArrayBinding(); + UnmarshalNode(nodeData, node); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedId)); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Members)); + node.ColonToken = UnmarshalLocation(nodeData); + node.LBracketToken = UnmarshalLocation(nodeData); + node.RBracketToken = UnmarshalLocation(nodeData); + return node; + } + + UiPublicMember UnmarshalUiPublicMember(BinaryReader nodeData) + { + var node = new UiPublicMember(); + UnmarshalNode(nodeData, node); + node.Type = (UiPublicMemberType)nodeData.ReadInt32(); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.MemberType)); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Statement)); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Binding)); + node.IsDefaultMember = (nodeData.ReadInt32() != 0); + node.IsReadonlyMember = (nodeData.ReadInt32() != 0); + UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Parameters)); + node.DefaultToken = UnmarshalLocation(nodeData); + node.ReadonlyToken = UnmarshalLocation(nodeData); + node.PropertyToken = UnmarshalLocation(nodeData); + node.TypeModifierToken = UnmarshalLocation(nodeData); + node.TypeToken = UnmarshalLocation(nodeData); + node.IdentifierToken = UnmarshalLocation(nodeData); + node.ColonToken = UnmarshalLocation(nodeData); + node.SemicolonToken = UnmarshalLocation(nodeData); + return node; + } + + /// <summary> + /// This delegate method is called during the AST traversal when entering/exiting a node. + /// </summary> + /// <param name="astVisitor">Unmanaged AST visitor object ref</param> + /// <param name="nodeKind">Type of node being visited</param> + /// <param name="nodePtr">Node object ref</param> + /// <param name="beginVisit">"true" when entering node, "false" when exiting</param> + /// <param name="nodeDataPtr">Serialized content of AST node</param> + /// <param name="nodeDataLength">Length in bytes of serialized content</param> + /// <returns></returns> + bool VisitorCallback( + IntPtr astVisitor, + int nodeKind, + IntPtr nodePtr, + bool beginVisit, + IntPtr nodeDataPtr, + int nodeDataLength) + { + if (!beginVisit) + return true; + + AstNode node = null; + var nodeData = new byte[nodeDataLength]; + Marshal.Copy(nodeDataPtr, nodeData, 0, nodeDataLength); + using (var rdr = new BinaryReader(new MemoryStream(nodeData))) { + switch ((AstNodeKind)nodeKind) { + case AstNodeKind.UiImport: + node = UnmarshalUiImport(rdr); + break; + case AstNodeKind.UiQualifiedId: + node = UnmarshalUiQualifiedId(rdr); + break; + case AstNodeKind.UiObjectDefinition: + node = UnmarshalUiObjectDefinition(rdr); + break; + case AstNodeKind.UiObjectBinding: + node = UnmarshalUiObjectBinding(rdr); + break; + case AstNodeKind.UiScriptBinding: + node = UnmarshalUiScriptBinding(rdr); + break; + case AstNodeKind.UiArrayBinding: + node = UnmarshalUiArrayBinding(rdr); + break; + case AstNodeKind.UiPublicMember: + node = UnmarshalUiPublicMember(rdr); + break; + default: + node = UnmarshalNode(rdr, (AstNodeKind)nodeKind); + break; + } + } + if (node == null) + return true; + + visitedNodes.Add(node); + nodesBytPtr[nodePtr] = node; + + List<KeyValuePair<AstNode, PropertyInfo>> derefs; + if (pendingDereferences.TryGetValue(nodePtr, out derefs)) { + foreach (var deref in derefs) { + try { + deref.Value.SetValue(deref.Key, node); + } catch (Exception) { } + } + pendingDereferences.Remove(nodePtr); + } + + return true; + } + + void FreeManaged() + { + } + + void FreeUnmanaged() + { + if (qmlParserPtr != IntPtr.Zero) { + NativeMethods.FreeParser(qmlParserPtr); + qmlParserPtr = IntPtr.Zero; + } + if (qmlTextPtr != IntPtr.Zero) { + Marshal.FreeHGlobal(qmlTextPtr); + qmlTextPtr = IntPtr.Zero; + } + } + + #region IDisposable + bool disposed = false; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (disposing) + FreeManaged(); + + FreeUnmanaged(); + + disposed = true; + } + + ~Parser() + { + Dispose(false); + } + #endregion + + /// <summary> + /// Get a reference to a static or instance class member from a member access lambda. + /// Adapted from: + /// https://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string + /// </summary> + /// <param name="memberLambda"> + /// Lambda expression of the form: '() => Class.Member'or '() => object.Member' + /// </param> + /// <returns>Reference to the class member</returns> + public static MemberInfo GetMemberRef<T>(Expression<Func<T>> memberLambda) + { + var me = memberLambda.Body as MemberExpression; + if (me == null) + return null; + return me.Member; + } + + public static PropertyInfo GetPropertyRef<T>(Expression<Func<T>> memberLambda) + { + return GetMemberRef(memberLambda) as PropertyInfo; + } + } +} diff --git a/QtVsTools.Package/QML/Syntax/QmlAst.cs b/QtVsTools.Package/QML/Syntax/QmlAst.cs new file mode 100644 index 0000000..1b3b1d1 --- /dev/null +++ b/QtVsTools.Package/QML/Syntax/QmlAst.cs @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// The data types in this file represent nodes in the QML abstract sytax tree (AST). +/// They correspond to classes defined in qqmljsast_p.h + +namespace QtVsTools.Qml.Syntax +{ + public enum AstNodeKind + { + #region Copied from qqmljsast_p.h + Undefined, + + ArgumentList, + ArrayPattern, + ArrayMemberExpression, + BinaryExpression, + Block, + BreakStatement, + CallExpression, + CaseBlock, + CaseClause, + CaseClauses, + Catch, + ConditionalExpression, + ContinueStatement, + DebuggerStatement, + DefaultClause, + DeleteExpression, + DoWhileStatement, + ElementList, + Elision, + EmptyStatement, + Expression, + ExpressionStatement, + FalseLiteral, + SuperLiteral, + FieldMemberExpression, + Finally, + ForEachStatement, + ForStatement, + FormalParameterList, + FunctionBody, + FunctionDeclaration, + FunctionExpression, + ClassExpression, + ClassDeclaration, + IdentifierExpression, + IdentifierPropertyName, + ComputedPropertyName, + IfStatement, + LabelledStatement, + NameSpaceImport, + ImportSpecifier, + ImportsList, + NamedImports, + ImportClause, + FromClause, + ImportDeclaration, + Module, + ExportSpecifier, + ExportsList, + ExportClause, + ExportDeclaration, + NewExpression, + NewMemberExpression, + NotExpression, + NullExpression, + YieldExpression, + NumericLiteral, + NumericLiteralPropertyName, + ObjectPattern, + PostDecrementExpression, + PostIncrementExpression, + PreDecrementExpression, + PreIncrementExpression, + Program, + PropertyDefinitionList, + PropertyGetterSetter, + PropertyName, + PropertyNameAndValue, + RegExpLiteral, + ReturnStatement, + StatementList, + StringLiteral, + StringLiteralPropertyName, + SwitchStatement, + TemplateLiteral, + TaggedTemplate, + ThisExpression, + ThrowStatement, + TildeExpression, + TrueLiteral, + TryStatement, + TypeOfExpression, + UnaryMinusExpression, + UnaryPlusExpression, + VariableDeclaration, + VariableDeclarationList, + VariableStatement, + VoidExpression, + WhileStatement, + WithStatement, + NestedExpression, + ClassElementList, + PatternElement, + PatternElementList, + PatternProperty, + PatternPropertyList, + + + UiArrayBinding, + UiImport, + UiObjectBinding, + UiObjectDefinition, + UiObjectInitializer, + UiObjectMemberList, + UiArrayMemberList, + UiPragma, + UiProgram, + UiParameterList, + UiPublicMember, + UiQualifiedId, + UiScriptBinding, + UiSourceElement, + UiHeaderItemList, + UiEnumDeclaration, + UiEnumMemberList + #endregion + } + + public class AstNode : SyntaxElement + { + public AstNodeKind Kind { get; private set; } + public AstNode(AstNodeKind kind) { Kind = kind; } + public SourceLocation FirstSourceLocation { get; set; } + public SourceLocation LastSourceLocation { get; set; } + } + + public class UiImport : AstNode + { + public UiImport() : base(AstNodeKind.UiImport) { } + public SourceLocation ImportToken { get; set; } + public SourceLocation FileNameToken { get; set; } + public SourceLocation VersionToken { get; set; } + public SourceLocation AsToken { get; set; } + public SourceLocation ImportIdToken { get; set; } + public SourceLocation SemicolonToken { get; set; } + } + + public class UiQualifiedId : AstNode + { + public UiQualifiedId() : base(AstNodeKind.UiQualifiedId) { } + public SourceLocation IdentifierToken { get; set; } + public UiQualifiedId Next { get; set; } + } + + public class UiObjectDefinition : AstNode + { + public UiObjectDefinition() : base(AstNodeKind.UiObjectDefinition) { } + public UiQualifiedId QualifiedTypeNameId { get; set; } + public AstNode /*UiObjectInitializer*/ Initializer { get; set; } + } + + public class UiObjectBinding : AstNode + { + public UiObjectBinding() : base(AstNodeKind.UiObjectBinding) { } + public UiQualifiedId QualifiedId { get; set; } + public UiQualifiedId QualifiedTypeNameId { get; set; } + public AstNode /*UiObjectInitializer*/ Initializer { get; set; } + public SourceLocation ColonToken { get; set; } + } + + public class UiScriptBinding : AstNode + { + public UiScriptBinding() : base(AstNodeKind.UiScriptBinding) { } + public UiQualifiedId QualifiedId { get; set; } + public AstNode /*Statement*/ Statement { get; set; } + public SourceLocation ColonToken { get; set; } + } + + public class UiArrayBinding : AstNode + { + public UiArrayBinding() : base(AstNodeKind.UiArrayBinding) { } + public UiQualifiedId QualifiedId { get; set; } + public AstNode /*UiArrayMemberList*/ Members { get; set; } + public SourceLocation ColonToken { get; set; } + public SourceLocation LBracketToken { get; set; } + public SourceLocation RBracketToken { get; set; } + } + + public enum UiPublicMemberType { Signal, Property }; + + public class UiPublicMember : AstNode + { + public UiPublicMember() : base(AstNodeKind.UiPublicMember) { } + public UiPublicMemberType Type { get; set; } + public UiQualifiedId MemberType { get; set; } + public AstNode /*Statement*/ Statement { get; set; } + public AstNode /*UiObjectMember*/ Binding { get; set; } + public bool IsDefaultMember { get; set; } + public bool IsReadonlyMember { get; set; } + public AstNode /*UiParameterList*/ Parameters { get; set; } + public SourceLocation DefaultToken { get; set; } + public SourceLocation ReadonlyToken { get; set; } + public SourceLocation PropertyToken { get; set; } + public SourceLocation TypeModifierToken { get; set; } + public SourceLocation TypeToken { get; set; } + public SourceLocation IdentifierToken { get; set; } + public SourceLocation ColonToken { get; set; } + public SourceLocation SemicolonToken { get; set; } + } + +} diff --git a/QtVsTools.Package/QML/Syntax/QmlSyntax.cs b/QtVsTools.Package/QML/Syntax/QmlSyntax.cs new file mode 100644 index 0000000..1035815 --- /dev/null +++ b/QtVsTools.Package/QML/Syntax/QmlSyntax.cs @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/// This file contains definitions of basic QML syntax elements. + +namespace QtVsTools.Qml.Syntax +{ + /// <summary> + /// Reference to a location in the source code parsed by the QML parser. + /// Maps one-to-one with the Span concept in the Visual Studio SDK. + /// </summary> + public struct SourceLocation + { + public int Offset; + public int Length; + } + + /// <summary> + /// Token constants from the Qt Declarative parser. + /// </summary> + public enum TokenKind + { + #region Copied from qqmljsgrammar_p.h + EOF_SYMBOL = 0, + REDUCE_HERE = 125, + T_AND = 1, + T_AND_AND = 2, + T_AND_EQ = 3, + T_ARROW = 93, + T_AS = 110, + T_AUTOMATIC_SEMICOLON = 62, + T_BREAK = 4, + T_CASE = 5, + T_CATCH = 6, + T_CLASS = 98, + T_COLON = 7, + T_COMMA = 8, + T_COMMENT = 91, + T_COMPATIBILITY_SEMICOLON = 92, + T_CONST = 86, + T_CONTINUE = 9, + T_DEBUGGER = 88, + T_DEFAULT = 10, + T_DELETE = 11, + T_DIVIDE_ = 12, + T_DIVIDE_EQ = 13, + T_DO = 14, + T_DOT = 15, + T_ELLIPSIS = 95, + T_ELSE = 16, + T_ENUM = 94, + T_EQ = 17, + T_EQ_EQ = 18, + T_EQ_EQ_EQ = 19, + T_ERROR = 114, + T_EXPORT = 101, + T_EXTENDS = 99, + T_FALSE = 85, + T_FEED_JS_EXPRESSION = 118, + T_FEED_JS_MODULE = 120, + T_FEED_JS_SCRIPT = 119, + T_FEED_JS_STATEMENT = 117, + T_FEED_UI_OBJECT_MEMBER = 116, + T_FEED_UI_PROGRAM = 115, + T_FINALLY = 20, + T_FOR = 21, + T_FORCE_BLOCK = 122, + T_FORCE_DECLARATION = 121, + T_FOR_LOOKAHEAD_OK = 123, + T_FROM = 102, + T_FUNCTION = 22, + T_GE = 23, + T_GET = 112, + T_GT = 24, + T_GT_GT = 25, + T_GT_GT_EQ = 26, + T_GT_GT_GT = 27, + T_GT_GT_GT_EQ = 28, + T_IDENTIFIER = 29, + T_IF = 30, + T_IMPORT = 108, + T_IN = 31, + T_INSTANCEOF = 32, + T_LBRACE = 33, + T_LBRACKET = 34, + T_LE = 35, + T_LET = 87, + T_LPAREN = 36, + T_LT = 37, + T_LT_LT = 38, + T_LT_LT_EQ = 39, + T_MINUS = 40, + T_MINUS_EQ = 41, + T_MINUS_MINUS = 42, + T_MULTILINE_STRING_LITERAL = 90, + T_NEW = 43, + T_NOT = 44, + T_NOT_EQ = 45, + T_NOT_EQ_EQ = 46, + T_NO_SUBSTITUTION_TEMPLATE = 103, + T_NULL = 83, + T_NUMERIC_LITERAL = 47, + T_OF = 111, + T_ON = 124, + T_OR = 48, + T_OR_EQ = 49, + T_OR_OR = 50, + T_PLUS = 51, + T_PLUS_EQ = 52, + T_PLUS_PLUS = 53, + T_PRAGMA = 109, + T_PROPERTY = 68, + T_PUBLIC = 107, + T_QUESTION = 54, + T_RBRACE = 55, + T_RBRACKET = 56, + T_READONLY = 70, + T_REMAINDER = 57, + T_REMAINDER_EQ = 58, + T_RESERVED_WORD = 89, + T_RETURN = 59, + T_RPAREN = 60, + T_SEMICOLON = 61, + T_SET = 113, + T_SIGNAL = 69, + T_STAR = 63, + T_STAR_EQ = 66, + T_STAR_STAR = 64, + T_STAR_STAR_EQ = 65, + T_STATIC = 100, + T_STRING_LITERAL = 67, + T_SUPER = 97, + T_SWITCH = 71, + T_TEMPLATE_HEAD = 104, + T_TEMPLATE_MIDDLE = 105, + T_TEMPLATE_TAIL = 106, + T_THIS = 72, + T_THROW = 73, + T_TILDE = 74, + T_TRUE = 84, + T_TRY = 75, + T_TYPEOF = 76, + T_VAR = 77, + T_VOID = 78, + T_WHILE = 79, + T_WITH = 80, + T_XOR = 81, + T_XOR_EQ = 82, + T_YIELD = 96, + + ACCEPT_STATE = 1008, + RULE_COUNT = 586, + STATE_COUNT = 1009, + TERMINAL_COUNT = 126, + NON_TERMINAL_COUNT = 213, + + GOTO_INDEX_OFFSET = 1009, + GOTO_INFO_OFFSET = 6012, + GOTO_CHECK_OFFSET = 6012 + #endregion + } + + public abstract class SyntaxElement { } + + /// <summary> + /// Represents a token identified by the QML lexer + /// </summary> + public class Token : SyntaxElement + { + public TokenKind Kind { get; private set; } + public SourceLocation Location { get; private set; } + protected Token() { } + public static Token Create(TokenKind kind, int offset, int length) + { + return Create(kind, new SourceLocation + { + Offset = offset, + Length = length, + }); + } + + public static Token Create(TokenKind kind, SourceLocation location) + { + Token token; + switch (kind) { + #region case KEYWORD: + case TokenKind.T_AS: + case TokenKind.T_BREAK: + case TokenKind.T_CASE: + case TokenKind.T_CATCH: + case TokenKind.T_CONST: + case TokenKind.T_CONTINUE: + case TokenKind.T_DEFAULT: + case TokenKind.T_DELETE: + case TokenKind.T_DO: + case TokenKind.T_ELSE: + case TokenKind.T_ENUM: + case TokenKind.T_FALSE: + case TokenKind.T_FINALLY: + case TokenKind.T_FOR: + case TokenKind.T_FUNCTION: + case TokenKind.T_IF: + case TokenKind.T_IMPORT: + case TokenKind.T_IN: + case TokenKind.T_INSTANCEOF: + case TokenKind.T_LET: + case TokenKind.T_NEW: + case TokenKind.T_NULL: + case TokenKind.T_ON: + case TokenKind.T_PRAGMA: + case TokenKind.T_PROPERTY: + case TokenKind.T_PUBLIC: + case TokenKind.T_READONLY: + case TokenKind.T_RESERVED_WORD: + case TokenKind.T_RETURN: + case TokenKind.T_SET: + case TokenKind.T_SIGNAL: + case TokenKind.T_SWITCH: + case TokenKind.T_THIS: + case TokenKind.T_THROW: + case TokenKind.T_TRUE: + case TokenKind.T_TRY: + case TokenKind.T_TYPEOF: + case TokenKind.T_VAR: + case TokenKind.T_VOID: + case TokenKind.T_WHILE: + case TokenKind.T_WITH: + #endregion + token = new KeywordToken(); + break; + case TokenKind.T_NUMERIC_LITERAL: + token = new NumberToken(); + break; + case TokenKind.T_MULTILINE_STRING_LITERAL: + case TokenKind.T_STRING_LITERAL: + token = new StringToken(); + break; + case TokenKind.T_COMMENT: + token = new CommentToken(); + break; + default: + token = new Token(); + break; + } + token.Kind = kind; + token.Location = location; + return token; + } + } + + public class KeywordToken : Token { } + + public class NumberToken : Token { } + + public class StringToken : Token { } + + public class CommentToken : Token { } +} diff --git a/QtVsTools.Package/QtMenus.vsct_TT b/QtVsTools.Package/QtMenus.vsct_TT new file mode 100644 index 0000000..76d066f --- /dev/null +++ b/QtVsTools.Package/QtMenus.vsct_TT @@ -0,0 +1,725 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vsct" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <!-- + This is the file that defines the actual layout and type of the commands. + It is divided in different sections (e.g. command definition, command + placement, ...), with each defining a specific set of properties. + See the comment before each section for more details about how to + use it. + --> + + <!-- + The VSCT compiler (the tool that translates this file into the binary + format that VisualStudio will consume) has the ability to run a preprocessor + on the vsct file; this preprocessor is (usually) the C++ preprocessor, so + it is possible to define includes and macros with the same syntax used + in C++ files. Using this ability of the compiler here, we include some files + defining some of the constants that we will use inside the file. + --> + + <!-- This is the file that defines the IDs for all the commands exposed by VisualStudio. --> + <Extern href="stdidcmd.h"/> + + <!-- This header contains the command ids for the menus provided by the shell. --> + <Extern href="vsshlids.h"/> + + <!-- + The Commands section is where commands, menus, and menu groups are defined. + This section uses a Guid to identify the package that provides the command defined inside it. + --> + <Commands package="PackageGuid"> + <!-- + Inside this section we have different sub-sections: one for the menus, another + for the menu groups, one for the buttons (the actual commands), one for the combos + and the last one for the bitmaps used. Each element is identified by a command id that + is a unique pair of guid and numeric identifier; the guid part of the identifier is usually + called "command set" and is used to group different command inside a logically related + group; your package should define its own command set in order to avoid collisions + with command ids defined by other packages. + --> + <Menus> + <Menu guid="MainMenuGuid" id="QtMainMenu" priority="0x700" type="Menu"> + <Parent guid="guidSHLMainMenu" id="IDG_VS_MM_TOOLSADDINS" /> + <Strings> + <ButtonText>Qt VS Tools</ButtonText> + </Strings> + </Menu> + + <Menu guid="ProjectContextMenuGuid" id="QtProjectSubMenu" priority="0x0100" type="Menu"> + <Parent guid="ProjectContextMenuGuid" id="QtProjectSubMenuGroup" /> + <Strings> + <ButtonText>Qt</ButtonText> + </Strings> + </Menu> + + <Menu guid="SolutionContextMenuGuid" id="QtSolutionSubMenu" priority="0x0100" type="Menu"> + <Parent guid="SolutionContextMenuGuid" id="QtSolutionSubMenuGroup" /> + <Strings> + <ButtonText>Qt</ButtonText> + </Strings> + </Menu> + </Menus> + + <!-- + In this section you can define new menu groups. A menu group is a container for + other menus or buttons (commands); from a visual point of view you can see the + group as the part of a menu contained between two lines. The parent of a group + must be a menu. + --> + <Groups> + + <!-- Region Main menu groups --> + + <Group guid="MainMenuGuid" id="VersionMenuGroup" priority="0x0500"> + <Parent guid="MainMenuGuid" id="QtMainMenu"/> + </Group> + <Group guid="MainMenuGuid" id="LaunchMenuGroup" priority="0x0600"> + <Parent guid="MainMenuGuid" id="QtMainMenu"/> + </Group> + <Group guid="MainMenuGuid" id="ProjectMenuGroup" priority="0x0600"> + <Parent guid="MainMenuGuid" id="QtMainMenu"/> + </Group> + <Group guid="MainMenuGuid" id="OthersMenuGroup" priority="0x0600"> + <Parent guid="MainMenuGuid" id="QtMainMenu"/> + </Group> + <Group guid="MainMenuGuid" id="OptionsMenuGroup" priority="0x0600"> + <Parent guid="MainMenuGuid" id="QtMainMenu"/> + </Group> + + <!-- Endregion Main menu groups --> + + <!-- Region Solution context menu groups --> + + <Group guid="SolutionContextMenuGuid" id="QtSolutionSubMenuGroup" priority="0x0600"> + <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_SOLNNODE" /> + </Group> + <Group guid="SolutionContextMenuGuid" id="SolutionContextQtMenuGroup" priority="0x0600"> + <Parent guid="SolutionContextMenuGuid" id="QtSolutionSubMenu"/> + </Group> + <Group guid="SolutionContextMenuGuid" id="SolutionContextTsMenuGroup" priority="0x0600"> + <Parent guid="SolutionContextMenuGuid" id="QtSolutionSubMenu" /> + </Group> + + <!-- Endregion Solution context menu groups --> + + <!-- Region Project context menu groups --> + + <Group guid="ProjectContextMenuGuid" id="QtProjectSubMenuGroup" priority="0x0600"> + <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE" /> + </Group> + <Group guid="ProjectContextMenuGuid" id="ProjectContextProjectMenuGroup" priority="0x0600"> + <Parent guid="ProjectContextMenuGuid" id="QtProjectSubMenu"/> + </Group> + <Group guid="ProjectContextMenuGuid" id="ProjectContextTsMenuGroup" priority="0x0600"> + <Parent guid="ProjectContextMenuGuid" id="QtProjectSubMenu"/> + </Group> + <Group guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" priority="0x0600"> + <Parent guid="ProjectContextMenuGuid" id="QtProjectSubMenu"/> + </Group> + <Group guid="ProjectContextMenuGuid" id="ProjectContextAddNewQtClassMenuGroup" priority="0x0600"> + <Parent guid="guidSHLMainMenu" id="cmdidShellWindowNavigate7"/> + </Group> + + <!-- Endregion Project context menu groups --> + + <Group guid="ProjectContextMenuGuid" id="ProjectContextAddNewQtClassMenuGroup" priority="0x0600"> + <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_PROJECT" /> + </Group> + + <Group guid="ItemContextMenuGuid" id="ItemContextTsMenuGroup" priority="0x0600"> + <Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_ITEMNODE" /> + </Group> + + </Groups> + + <!-- + This section defines the elements the user can interact with, like a menu command or a button + or combo box in a toolbar. + --> + <Buttons> + <!-- + To define a menu group you have to specify its ID, the parent menu and its display priority. + The command is visible and enabled by default. If you need to change the visibility, status, + etc, you can use the CommandFlag node. You can add more than one CommandFlag node e.g.: + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + If you do not want an image next to your command, remove the Icon node /> + --> + + <!-- Region Main menu buttons --> + + <Button guid="MainMenuGuid" id="QtVersionId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="VersionMenuGroup" /> + <Icon guid="MenuImages" id="QtLogoBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>NoCustomize</CommandFlag> + <CommandFlag>NoButtonCustomize</CommandFlag> + <CommandFlag>NoKeyCustomize</CommandFlag> + <CommandFlag>TextChanges</CommandFlag> + <Strings> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <ButtonText +>Qt Visual Studio Tools version <#=QT_VS_TOOLS_VERSION_USER#>: Initializing...</ButtonText> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Strings> + </Button> + + <Button guid="MainMenuGuid" id="ViewQtHelpId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="VersionMenuGroup" /> + <Icon guid="MenuImages" id="QtLogoBitmap" /> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>qt.io</ButtonText> + </Strings> + </Button> + + <Button guid="MainMenuGuid" id="F1QtHelpId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="VersionMenuGroup" /> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>F1 Qt Help</ButtonText> + </Strings> + </Button> + + <Button guid="MainMenuGuid" id="LaunchDesignerId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="LaunchMenuGroup" /> + <Icon guid="MenuImages" id="LaunchDesignerBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Launch Qt Designer</ButtonText> + <ToolTipText>Start Qt Designer</ToolTipText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="LaunchLinguistId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="LaunchMenuGroup" /> + <Icon guid="MenuImages" id="LaunchLinguistBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Launch Qt Linguist</ButtonText> + <ToolTipText>Start Qt Linguist</ToolTipText> + </Strings> + </Button> + + <Button guid="MainMenuGuid" id="OpenProFileId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="ProjectMenuGroup" /> + <Icon guid="MenuImages" id="OpenProFileBitmap" /> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Open Qt Project File (.pro)...</ButtonText> + <ToolTipText>Open a Qt Project File (.pro), create a new solution, and add a new project to it</ToolTipText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="ImportPriFileId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="ProjectMenuGroup" /> + <Icon guid="MenuImages" id="ImportPriFileBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Import .pri File to Project...</ButtonText> + <ToolTipText>Import a .pri file and create a Qt project from it</ToolTipText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="ExportPriFileId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="ProjectMenuGroup" /> + <Icon guid="MenuImages" id="ExportProFileBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Export Project to .pri File...</ButtonText> + <ToolTipText>Export the current project to a .pri file</ToolTipText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="ExportProFileId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="ProjectMenuGroup" /> + <Icon guid="MenuImages" id="CreateProFileBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Create Basic .pro File...</ButtonText> + <ToolTipText>Export the current project to a Qt project file (.pro)</ToolTipText> + </Strings> + </Button> + + <Button guid="MainMenuGuid" id="CreateNewTsFileId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Create New Translation File</ButtonText> + <ToolTipText>Create a new translation file that you can open in Qt Linguist</ToolTipText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="ConvertToQtMsBuild" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Convert custom build steps to Qt/MSBuild</ButtonText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="ConvertToQtId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Convert Project to Qt VS Tools Project</ButtonText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="ConvertToQmakeId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Convert Project to QMake Generated Project</ButtonText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="QtProjectSettingsId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Qt Project Settings</ButtonText> + <ToolTipText>Edit Qt settings of the current project</ToolTipText> + </Strings> + </Button> + <Button guid="MainMenuGuid" id="ChangeProjectQtVersionId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Change Project's Qt Version</ButtonText> + </Strings> + </Button> + + <Button guid="MainMenuGuid" id="QtOptionsId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OptionsMenuGroup" /> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Options</ButtonText> + <ToolTipText>Configure default Qt settings</ToolTipText> + </Strings> + </Button> + + <Button guid="MainMenuGuid" id="QtVersionsId" priority="0x0100" type="Button"> + <Parent guid="MainMenuGuid" id="OptionsMenuGroup" /> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <Strings> + <ButtonText>Qt Versions</ButtonText> + <ToolTipText>Register installed versions of Qt</ToolTipText> + </Strings> + </Button> + + <!-- Endregion Main menu buttons --> + + <!-- Region Solution context menu button --> + + <Button guid="SolutionContextMenuGuid" id="lUpdateOnSolutionId" priority="0x0100" type="Button"> + <Parent guid="SolutionContextMenuGuid" id="SolutionContextTsMenuGroup" /> + <Icon guid="MenuImages" id="LaunchLinguistBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>lupdate All .ts Files in Solution</ButtonText> + </Strings> + </Button> + <Button guid="SolutionContextMenuGuid" id="lReleaseOnSolutionId" priority="0x0100" type="Button"> + <Parent guid="SolutionContextMenuGuid" id="SolutionContextTsMenuGroup" /> + <Icon guid="MenuImages" id="LaunchLinguistBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>lrelease All .ts Files in Solution</ButtonText> + </Strings> + </Button> + + <Button guid="SolutionContextMenuGuid" id="ChangeSolutionQtVersionId" priority="0x0100" type="Button"> + <Parent guid="SolutionContextMenuGuid" id="SolutionContextQtMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Change Solution's Qt Version</ButtonText> + <ToolTipText>Change the Qt version for this solution and all contained projects</ToolTipText> + </Strings> + </Button> + <Button guid="SolutionContextMenuGuid" id="SolutionConvertToQtMsBuild" priority="0x0100" type="Button"> + <Parent guid="SolutionContextMenuGuid" id="SolutionContextQtMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Convert custom build steps to Qt/MSBuild</ButtonText> + </Strings> + </Button> + <Button guid="SolutionContextMenuGuid" id="SolutionEnableProjectTracking" priority="0x0100" type="Button"> + <Parent guid="SolutionContextMenuGuid" id="SolutionContextQtMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Refresh project tracking</ButtonText> + </Strings> + </Button> + + <!-- Endregion Solution context menu button --> + + <!-- Region Project context menu button --> + + <Button guid="ProjectContextMenuGuid" id="ImportPriFileProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextProjectMenuGroup" /> + <Icon guid="MenuImages" id="ImportPriFileBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Import .pri File to Project...</ButtonText> + <ToolTipText>Import a .pri file and create a Qt project from it</ToolTipText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="ExportPriFileProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextProjectMenuGroup" /> + <Icon guid="MenuImages" id="ExportProFileBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Export Project to .pri File...</ButtonText> + <ToolTipText>Export the current project to a .pri file</ToolTipText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="ExportProFileProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextProjectMenuGroup" /> + <Icon guid="MenuImages" id="CreateProFileBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Create Basic .pro File...</ButtonText> + <ToolTipText>Export the current project to a Qt Project file (.pro)</ToolTipText> + </Strings> + </Button> + + <Button guid="ProjectContextMenuGuid" id="CreateNewTsFileProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextTsMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Create New Translation File</ButtonText> + <ToolTipText>Create a new translation file that you can open in Qt Linguist</ToolTipText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="lUpdateOnProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextTsMenuGroup" /> + <Icon guid="MenuImages" id="LaunchLinguistBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>lupdate All .ts Files in Project</ButtonText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="lReleaseOnProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextTsMenuGroup" /> + <Icon guid="MenuImages" id="LaunchLinguistBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>lrelease All .ts Files in Project</ButtonText> + </Strings> + </Button> + + <Button guid="ProjectContextMenuGuid" id="ProjectConvertToQtMsBuild" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>TextChanges</CommandFlag> + <Strings> + <ButtonText>Convert custom build steps to Qt/MSBuild</ButtonText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="ProjectRefreshIntelliSense" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <CommandFlag>TextChanges</CommandFlag> + <Strings> + <ButtonText>Refresh IntelliSense</ButtonText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="ConvertToQtProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Convert Project to Qt VS Tools Project</ButtonText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="ConvertToQmakeProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Convert Project to QMake Generated Project</ButtonText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="QtProjectSettingsProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Qt Project Settings</ButtonText> + <ToolTipText>Edit Qt settings of the current project</ToolTipText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="ChangeProjectQtVersionProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextOthersMenuGroup" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Change Project's Qt Version</ButtonText> + </Strings> + </Button> + <Button guid="ProjectContextMenuGuid" id="ProjectAddNewQtClassProjectId" priority="0x0100" type="Button"> + <Parent guid="ProjectContextMenuGuid" id="ProjectContextAddNewQtClassMenuGroup" /> + <Icon guid="MenuImages" id="AddNewQtClassBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>Add Qt Class...</ButtonText> + </Strings> + </Button> + + <!-- Endregion Project context menu button --> + + <!-- Region Item context menu buttons --> + + <Button guid="ItemContextMenuGuid" id="lUpdateOnItemId" priority="0x0100" type="Button"> + <Parent guid="ItemContextMenuGuid" id="ItemContextTsMenuGroup" /> + <Icon guid="MenuImages" id="LaunchLinguistBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>lupdate</ButtonText> + </Strings> + </Button> + <Button guid="ItemContextMenuGuid" id="lReleaseOnItemId" priority="0x0100" type="Button"> + <Parent guid="ItemContextMenuGuid" id="ItemContextTsMenuGroup" /> + <Icon guid="MenuImages" id="LaunchLinguistBitmap" /> + <CommandFlag>DefaultDisabled</CommandFlag> + <CommandFlag>DefaultInvisible</CommandFlag> + <CommandFlag>DynamicVisibility</CommandFlag> + <Strings> + <ButtonText>lrelease</ButtonText> + </Strings> + </Button> + + <!-- Endregion Item context menu buttons --> + + </Buttons> + + <!-- The bitmaps section is used to define the bitmaps that are used for the commands. --> + <Bitmaps> + <!-- + The bitmap id is defined in a way that is a little bit different from the others: + the declaration starts with a guid for the bitmap strip, then there is the resource + id of the bitmap strip containing the bitmaps and then there are the numeric ids of + the elements used inside a button definition. An important aspect of this declaration + is that the element id must be the actual index (1-based) of the bitmap inside the + bitmap strip. + --> + <Bitmap guid="MenuImages" href="Resources\menuimages.png" usedList="LaunchDesignerBitmap, + LaunchLinguistBitmap, OpenProFileBitmap, ImportPriFileBitmap, ExportProFileBitmap, + CreateProFileBitmap, QtLogoBitmap, AddNewQtClassBitmap" /> + </Bitmaps> + </Commands> + + <Symbols> + <!-- This is the package guid. --> + <GuidSymbol name="PackageGuid" value="{15021976-647e-4876-9040-2507afde45d2}" /> + + <!-- This is the guid used to group the menu commands together --> + <GuidSymbol name="MainMenuGuid" value="{58f83fff-d39d-4c66-810b-2702e1f04e73}"> + + <!-- Region Main menu buttons Ids--> + + <IDSymbol name="QtMainMenu" value="0x1020"/> + + <IDSymbol name="VersionMenuGroup" value="0x5021" /> + <IDSymbol name="QtVersionId" value="0x0500" /> + <IDSymbol name="ViewQtHelpId" value="0x0501" /> + <IDSymbol name="F1QtHelpId" value="0x0502" /> + + <IDSymbol name="LaunchMenuGroup" value="0x1021" /> + <IDSymbol name="LaunchDesignerId" value="0x0100" /> + <IDSymbol name="LaunchLinguistId" value="0x0101" /> + + <IDSymbol name="ProjectMenuGroup" value="0x1022" /> + <IDSymbol name="OpenProFileId" value="0x0102" /> + <IDSymbol name="ImportPriFileId" value="0x0103" /> + <IDSymbol name="ExportPriFileId" value="0x0104" /> + <IDSymbol name="ExportProFileId" value="0x0105" /> + + <IDSymbol name="OthersMenuGroup" value="0x1023" /> + <IDSymbol name="CreateNewTsFileId" value="0x0107" /> + <IDSymbol name="ConvertToQtMsBuild" value="0x0130" /> + <IDSymbol name="ConvertToQtId" value="0x0124" /> + <IDSymbol name="ConvertToQmakeId" value="0x0108" /> + <IDSymbol name="QtProjectSettingsId" value="0x0109" /> + <IDSymbol name="ChangeProjectQtVersionId" value="0x0126" /> + + <IDSymbol name="OptionsMenuGroup" value="0x1024" /> + <IDSymbol name="QtOptionsId" value="0x0110" /> + <IDSymbol name="QtVersionsId" value="0x0111" /> + + <!-- Endregion Main menu buttons Ids --> + + </GuidSymbol> + + <GuidSymbol name="SolutionContextMenuGuid" value="{6dcda34f-4d22-4d6a-a176-5507069c5a3e}"> + + <!-- Region Solution context menu button Ids --> + + <IDSymbol name="QtSolutionSubMenuGroup" value="0x1026" /> + <IDSymbol name="QtSolutionSubMenu" value="0x1127"/> + + <IDSymbol name="SolutionContextTsMenuGroup" value="0x1025" /> + <IDSymbol name="lUpdateOnSolutionId" value="0x0111" /> + <IDSymbol name="lReleaseOnSolutionId" value="0x0112" /> + + <IDSymbol name="SolutionContextQtMenuGroup" value="0x1126" /> + <IDSymbol name="ChangeSolutionQtVersionId" value="0x0113" /> + <IDSymbol name="SolutionConvertToQtMsBuild" value="0x0130" /> + <IDSymbol name="SolutionEnableProjectTracking" value="0x1130" /> + + <!-- Endregion Solution context menu button Ids --> + + </GuidSymbol> + + <GuidSymbol name="ProjectContextMenuGuid" value="{5732faa9-6074-4e07-b035-2816e809f50e}" > + + <!-- Region Project context menu button Ids --> + + <IDSymbol name="QtProjectSubMenuGroup" value="0x1150"/> + <IDSymbol name="QtProjectSubMenu" value="0x1100"/> + + <IDSymbol name="ProjectContextProjectMenuGroup" value="0x1027" /> + <IDSymbol name="ImportPriFileProjectId" value="0x0114" /> + <IDSymbol name="ExportPriFileProjectId" value="0x0115" /> + <IDSymbol name="ExportProFileProjectId" value="0x0116" /> + + <IDSymbol name="ProjectContextTsMenuGroup" value="0x1028" /> + <IDSymbol name="CreateNewTsFileProjectId" value="0x0117" /> + <IDSymbol name="lUpdateOnProjectId" value="0x0118" /> + <IDSymbol name="lReleaseOnProjectId" value="0x0119" /> + + <IDSymbol name="ProjectContextOthersMenuGroup" value="0x1029" /> + <IDSymbol name="ProjectConvertToQtMsBuild" value="0x0130" /> + <IDSymbol name="ProjectRefreshIntelliSense" value="0x0131" /> + <IDSymbol name="ConvertToQtProjectId" value="0x0120" /> + <IDSymbol name="ConvertToQmakeProjectId" value="0x0121" /> + <IDSymbol name="QtProjectSettingsProjectId" value="0x0122" /> + <IDSymbol name="ChangeProjectQtVersionProjectId" value="0x0123" /> + + <IDSymbol name="ProjectContextAddNewQtClassMenuGroup" value="0x1031" /> + <IDSymbol name="ProjectAddNewQtClassProjectId" value="0x0200" /> + + <!-- Endregion Project context menu button Ids --> + + </GuidSymbol> + + <GuidSymbol name="ItemContextMenuGuid" value="{9f67a0bd-ee0a-47e3-b656-5efb12e3c770}"> + + <IDSymbol name="ItemContextTsMenuGroup" value="0x1030" /> + <IDSymbol name="lUpdateOnItemId" value="0x0125" /> + <IDSymbol name="lReleaseOnItemId" value="0x0126" /> + + </GuidSymbol> + + <GuidSymbol name="MenuImages" value="{d7cf9f1c-0f37-4609-8eb3-72589dc5a5ec}" > + <IDSymbol name="LaunchDesignerBitmap" value="1" /> + <IDSymbol name="LaunchLinguistBitmap" value="2" /> + <IDSymbol name="OpenProFileBitmap" value="3" /> + <IDSymbol name="ImportPriFileBitmap" value="4" /> + <IDSymbol name="ExportProFileBitmap" value="5" /> + <IDSymbol name="CreateProFileBitmap" value="6" /> + <IDSymbol name="QtLogoBitmap" value="7" /> + <IDSymbol name="AddNewQtClassBitmap" value="8" /> + </GuidSymbol> + </Symbols> + + <VisibilityConstraints> + <VisibilityItem guid="SolutionContextMenuGuid" id="lUpdateOnSolutionId" + context="UICONTEXT_SolutionHasSingleProject" /> + <VisibilityItem guid="SolutionContextMenuGuid" id="lUpdateOnSolutionId" + context="UICONTEXT_SolutionHasMultipleProjects" /> + <VisibilityItem guid="SolutionContextMenuGuid" id="lReleaseOnSolutionId" + context="UICONTEXT_SolutionHasSingleProject" /> + <VisibilityItem guid="SolutionContextMenuGuid" id="lReleaseOnSolutionId" + context="UICONTEXT_SolutionHasMultipleProjects" /> + <VisibilityItem guid="SolutionContextMenuGuid" id="ChangeSolutionQtVersionId" + context="UICONTEXT_SolutionHasSingleProject" /> + <VisibilityItem guid="SolutionContextMenuGuid" id="ChangeSolutionQtVersionId" + context="UICONTEXT_SolutionHasMultipleProjects" /> + </VisibilityConstraints> + + <KeyBindings> + <KeyBinding guid="MainMenuGuid" id="F1QtHelpId" key1="VK_F1" mod1="ALT" editor="guidVSStd97" /> + </KeyBindings> +</CommandTable> diff --git a/QtVsTools.Package/QtMsBuild/QtModulesEditor.cs b/QtVsTools.Package/QtMsBuild/QtModulesEditor.cs new file mode 100644 index 0000000..8fbd145 --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtModulesEditor.cs @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Properties; +using Microsoft.Internal.VisualStudio.PlatformUI; +using System; +using System.ComponentModel.Composition; +using System.Linq; +using System.Threading.Tasks; + +namespace QtVsTools.QtMsBuild +{ + using QtVsTools.Core; + + [Export(typeof(IPropertyPageUIValueEditor))] + [ExportMetadata("Name", "QtModulesEditor")] + [AppliesTo("IntegratedConsoleDebugging")] + internal sealed class QtModulesEditor : IPropertyPageUIValueEditor + { + public async Task<object> EditValueAsync( + IServiceProvider serviceProvider, + IProperty ruleProperty, + object currentValue) + { + await Task.Yield(); + + var modules = QtModules.Instance.GetAvailableModules() + .Where(x => !string.IsNullOrEmpty(x.proVarQT)) + .Select(x => new QtModulesPopup.Module + { + Id = x.Id, + Name = x.Name, + IsReadOnly = !x.Selectable, + QT = x.proVarQT.Split(' ').ToHashSet(), + }) + .ToList(); + + var allQT = modules.SelectMany(x => x.QT).ToHashSet(); + var selectedQT = currentValue.ToString().Split(';').ToHashSet(); + var extraQT = selectedQT.Except(allQT); + + foreach (var module in modules) + module.IsSelected = module.QT.Intersect(selectedQT).Count() == module.QT.Count; + + var popup = new QtModulesPopup(); + popup.SetModules(modules); + + if (popup.ShowModal().GetValueOrDefault()) { + selectedQT = modules + .Where(x => x.IsSelected) + .SelectMany(x => x.QT) + .Union(extraQT) + .ToHashSet(); + } + return string.Join(";", selectedQT); + } + } +} diff --git a/QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml b/QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml new file mode 100644 index 0000000..54e13a7 --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml @@ -0,0 +1,111 @@ +<!-- +***************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +***************************************************************************** +--> +<local:VsToolsDialogWindow x:Class="QtVsTools.QtMsBuild.QtModulesPopup" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="clr-namespace:QtVsTools" + ResizeMode="CanResize" + Width="800" MaxHeight="550" + MouseDown="Window_MouseDown" + SizeToContent="WidthAndHeight" + ShowInTaskbar="False" + HasHelpButton="False" + HasMinimizeButton="False" + Title="Qt Modules" > + <Grid Margin="15"> + <Grid.RowDefinitions> + <RowDefinition Height="1*"/> + <RowDefinition Height="Auto"/> + </Grid.RowDefinitions> + <Grid Grid.Row="0"> + <Border x:Name="DropDownBorder" + Background="White" + BorderThickness="1" + BorderBrush="Gray" /> + <ListView Name="PopupListBox" + BorderThickness="0" + ScrollViewer.VerticalScrollBarVisibility="Disabled" + ScrollViewer.HorizontalScrollBarVisibility="Auto" + Margin="10,10,10,10" + KeyDown="PopupListBox_KeyDown"> + <ListView.ItemsPanel> + <ItemsPanelTemplate> + <WrapPanel Orientation="Vertical" /> + </ItemsPanelTemplate> + </ListView.ItemsPanel> + <ListView.ItemTemplate> + <DataTemplate> + <CheckBox Content="{Binding Name}" + IsChecked="{Binding IsSelected}" + IsEnabled="{Binding IsEnabled}" + VerticalAlignment="Center" + VerticalContentAlignment="Center" + Click="Module_Click" + Margin="4" + Loaded="CheckBox_Loaded"> + <CheckBox.ContentTemplate> + <DataTemplate> + <Grid> + <Rectangle Fill="#FF36B31A" + RadiusX="4" + RadiusY="4" + Height="20" /> + <Label Content="{Binding}" + Foreground="White" /> + </Grid> + </DataTemplate> + </CheckBox.ContentTemplate> + </CheckBox> + </DataTemplate> + </ListView.ItemTemplate> + <ListView.ItemContainerStyle> + <Style TargetType="ListViewItem"> + <Style.Setters> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="ListViewItem"> + <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalAlignment="{TemplateBinding VerticalContentAlignment}" + SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style.Setters> + </Style> + </ListView.ItemContainerStyle> + </ListView> + </Grid> + <WrapPanel Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Right" Margin="0,15,0,0"> + <Button IsDefault="True" Name="buttonOk" Click="buttonOk_Click" MinHeight="23" + MinWidth="74" Margin="0,0,10,0">_OK</Button> + <Button IsCancel="True" MinHeight="23" MinWidth="74">_Cancel</Button> + </WrapPanel> + </Grid> +</local:VsToolsDialogWindow> diff --git a/QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml.cs b/QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml.cs new file mode 100644 index 0000000..19d045b --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtModulesPopup.xaml.cs @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace QtVsTools.QtMsBuild +{ + public partial class QtModulesPopup : VsToolsDialogWindow + { + public class Module + { + public string Name { get; set; } + public int Id { get; set; } + public bool IsReadOnly { get; set; } + public bool IsEnabled => !IsReadOnly; + public HashSet<string> QT { get; set; } + public bool IsSelected { get; set; } + public CheckBox CheckBox { get; set; } + } + + public QtModulesPopup() + { + InitializeComponent(); + WindowStartupLocation = WindowStartupLocation.CenterOwner; + } + + public void SetModules(IEnumerable<Module> modules) + { + PopupListBox.ItemsSource = modules; + } + + private Module GetCheckBoxModule(CheckBox checkBox) + { + return (checkBox?.TemplatedParent as ContentPresenter)?.Content as Module; + } + + private void CheckBox_Loaded(object sender, RoutedEventArgs e) + { + var checkBox = sender as CheckBox; + var module = GetCheckBoxModule(checkBox); + if (module != null) + module.CheckBox = checkBox; + } + + private void Module_Click(object sender, RoutedEventArgs e) + { + var checkBox = sender as CheckBox; + var module = GetCheckBoxModule(checkBox); + if (module != null) + module.IsSelected = (checkBox.IsChecked == true); + } + + private void PopupListBox_KeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter || e.Key == Key.Space) { + var module = PopupListBox.SelectedItem as Module; + if (module != null && module.IsEnabled) + module.CheckBox.IsChecked = (module.CheckBox.IsChecked != true); + } + } + + private void Window_MouseDown(object sender, MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left) + DragMove(); + } + + private void buttonOk_Click(object sender, RoutedEventArgs e) + { + DialogResult = true; + Close(); + } + } +} diff --git a/QtVsTools.Package/QtMsBuild/QtProjectBuild.cs b/QtVsTools.Package/QtMsBuild/QtProjectBuild.cs new file mode 100644 index 0000000..f2fea0b --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtProjectBuild.cs @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Execution; +using Microsoft.Build.Framework; +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.TaskStatusCenter; +using Microsoft.VisualStudio.Threading; +using Microsoft.VisualStudio.VCProjectEngine; +using EnvDTE; + +namespace QtVsTools.QtMsBuild +{ + using Core; + using VisualStudio; + using Thread = System.Threading.Thread; + + class QtProjectBuild : Concurrent<QtProjectBuild> + { + static PunisherQueue<QtProjectBuild> _BuildQueue; + static PunisherQueue<QtProjectBuild> BuildQueue => + StaticThreadSafeInit(() => _BuildQueue, + () => _BuildQueue = new PunisherQueue<QtProjectBuild>( + getItemKey: (QtProjectBuild build) => + { + return build.ConfiguredProject; + }) + ); + + static ConcurrentStopwatch _RequestTimer; + static ConcurrentStopwatch RequestTimer => + StaticThreadSafeInit(() => _RequestTimer, () => _RequestTimer = new ConcurrentStopwatch()); + + static IVsTaskStatusCenterService _StatusCenter; + static IVsTaskStatusCenterService StatusCenter => StaticThreadSafeInit(() => _StatusCenter, + () => _StatusCenter = VsServiceProvider + .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>()); + + EnvDTE.Project Project { get; set; } + UnconfiguredProject UnconfiguredProject { get; set; } + ConfiguredProject ConfiguredProject { get; set; } + Dictionary<string, string> Properties { get; set; } + List<string> Targets { get; set; } + LoggerVerbosity LoggerVerbosity { get; set; } + + static Task BuildDispatcher { get; set; } + + public static void StartBuild( + EnvDTE.Project project, + string configName, + Dictionary<string, string> properties, + IEnumerable<string> targets, + LoggerVerbosity verbosity = LoggerVerbosity.Quiet) + { + if (project == null) + throw new ArgumentException("Project cannot be null."); + if (configName == null) + throw new ArgumentException("Configuration name cannot be null."); + + Task.Run(() => StartBuildAsync(project, configName, properties, targets, verbosity)); + } + + public static async Task StartBuildAsync( + EnvDTE.Project project, + string configName, + Dictionary<string, string> properties, + IEnumerable<string> targets, + LoggerVerbosity verbosity) + { + if (project == null) + throw new ArgumentException("Project cannot be null."); + if (configName == null) + throw new ArgumentException("Configuration name cannot be null."); + + RequestTimer.Restart(); + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Request [{2}] {3}", + DateTime.Now, Thread.CurrentThread.ManagedThreadId, + configName, project.FullName)); + } + + var tracker = QtProjectTracker.Get(project); + await tracker.Initialized; + + var knownConfigs = await tracker.UnconfiguredProject.Services + .ProjectConfigurationsService.GetKnownProjectConfigurationsAsync(); + + ConfiguredProject configuredProject = null; + foreach (var config in knownConfigs) { + var configProject = await tracker.UnconfiguredProject + .LoadConfiguredProjectAsync(config); + if (configProject.ProjectConfiguration.Name == configName) { + configuredProject = configProject; + break; + } + } + if (configuredProject == null) + throw new ArgumentException(string.Format("Unknown configuration '{0}'.", configName)); + + BuildQueue.Enqueue(new QtProjectBuild() + { + Project = project, + UnconfiguredProject = tracker.UnconfiguredProject, + ConfiguredProject = configuredProject, + Properties = properties?.ToDictionary(x => x.Key, x => x.Value), + Targets = targets?.ToList(), + LoggerVerbosity = verbosity + }); + StaticThreadSafeInit(() => BuildDispatcher, + () => BuildDispatcher = Task.Run(BuildDispatcherLoopAsync)) + .Forget(); + } + + public static void Reset() + { + BuildQueue.Clear(); + } + + static async Task BuildDispatcherLoopAsync() + { + ITaskHandler2 dispatchStatus = null; + while (!QtVsToolsPackage.Instance.Zombied) { + while (BuildQueue.IsEmpty || RequestTimer.ElapsedMilliseconds < 1000) { + if (BuildQueue.IsEmpty && dispatchStatus != null) { + dispatchStatus.Dismiss(); + dispatchStatus = null; + } + await Task.Delay(100); + } + QtProjectBuild buildRequest; + if (BuildQueue.TryDequeue(out buildRequest)) { + if (dispatchStatus == null) { + dispatchStatus = StatusCenter.PreRegister( + new TaskHandlerOptions + { + Title = "Qt VS Tools", + }, + new TaskProgressData + { + ProgressText = string.Format( + "Refreshing IntelliSense data, {0} project(s) remaining...", + BuildQueue.Count), + CanBeCanceled = true + }) + as ITaskHandler2; + dispatchStatus.RegisterTask(new Task(() => + throw new InvalidOperationException())); + } else { + dispatchStatus.Progress.Report( + new TaskProgressData + { + ProgressText = string.Format( + "Refreshing IntelliSense data, {0} project(s) remaining...", + BuildQueue.Count), + CanBeCanceled = true, + }); + } + await buildRequest.BuildAsync(); + } + if (BuildQueue.IsEmpty + || dispatchStatus?.UserCancellation.IsCancellationRequested == true) { + if (dispatchStatus != null) { + dispatchStatus.Dismiss(); + dispatchStatus = null; + } + Reset(); + } + } + } + + async Task BuildAsync() + { + if (LoggerVerbosity != LoggerVerbosity.Quiet) { + Messages.Print(clear: !QtVsToolsPackage.Instance.Options.BuildDebugInformation, activate: true, + text: string.Format( +@"== {0}: starting build... + * Properties: {1} + * Targets: {2} +", + /*{0}*/ Project.Name, + /*{1}*/ string.Join("", Properties + .Select(property => string.Format(@" + {0} = {1}", /*{0}*/ property.Key, /*{1}*/ property.Value))), + /*{2}*/ string.Join(";", Targets))); + } + + var lockService = UnconfiguredProject.ProjectService.Services.ProjectLockService; + + bool ok = false; + try { + ProjectWriteLockReleaser writeAccess; + var timer = ConcurrentStopwatch.StartNew(); + while (timer.IsRunning) { + try { + writeAccess = await lockService.WriteLockAsync(); + timer.Stop(); + } catch (InvalidOperationException) { + if (timer.ElapsedMilliseconds >= 5000) + throw; + using (var readAccess = await lockService.ReadLockAsync()) + await readAccess.ReleaseAsync(); + } + } + + using (writeAccess) { + var msBuildProject = await writeAccess.GetProjectAsync(ConfiguredProject); + + var solutionPath = QtProjectTracker.SolutionPath; + var configProps = new Dictionary<string, string>( + ConfiguredProject.ProjectConfiguration.Dimensions.ToImmutableDictionary()) + { + { "SolutionPath", solutionPath }, + { "SolutionFileName", Path.GetFileName(solutionPath) }, + { "SolutionName", Path.GetFileNameWithoutExtension(solutionPath) }, + { "SolutionExt", Path.GetExtension(solutionPath) }, + { "SolutionDir", Path.GetDirectoryName(solutionPath).TrimEnd('\\') + '\\' } + }; + + foreach (var property in Properties) + configProps[property.Key] = property.Value; + + var projectInstance = new ProjectInstance(msBuildProject.Xml, + configProps, null, new ProjectCollection()); + + var loggerVerbosity = LoggerVerbosity; + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) + loggerVerbosity = QtVsToolsPackage.Instance.Options.BuildLoggerVerbosity; + var buildParams = new BuildParameters() + { + Loggers = (loggerVerbosity != LoggerVerbosity.Quiet) + ? new[] { new QtProjectLogger() { Verbosity = loggerVerbosity } } + : null + }; + + var buildRequest = new BuildRequestData(projectInstance, + Targets.ToArray(), + hostServices: null, + flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild); + + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Build [{2}] {3}", + DateTime.Now, Thread.CurrentThread.ManagedThreadId, + ConfiguredProject.ProjectConfiguration.Name, + UnconfiguredProject.FullPath)); + Messages.Print("=== Targets"); + foreach (var target in buildRequest.TargetNames) + Messages.Print(string.Format(" {0}", target)); + Messages.Print("=== Properties"); + foreach (var property in Properties) { + Messages.Print(string.Format(" {0}={1}", + property.Key, property.Value)); + } + } + + BuildResult result = null; + while (result == null) { + try { + result = BuildManager.DefaultBuildManager.Build( + buildParams, buildRequest); + } catch (InvalidOperationException) { + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] " + + "Warning: Another build is in progress; waiting...", + DateTime.Now, + Thread.CurrentThread.ManagedThreadId, + ConfiguredProject.ProjectConfiguration.Name)); + } + await Task.Delay(3000); + } + } + + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + string resMsg; + StringBuilder resInfo = new StringBuilder(); + if (result?.OverallResult == BuildResultCode.Success) { + resMsg = "Build ok"; + } else { + resMsg = "Build FAIL"; + if (result == null) { + resInfo.AppendLine("####### Build returned 'null'"); + } else { + resInfo.AppendLine("####### Build returned 'Failure' code"); + if (result.ResultsByTarget != null) { + foreach (var tr in result.ResultsByTarget) { + var res = tr.Value; + if (res.ResultCode != TargetResultCode.Failure) + continue; + resInfo.AppendFormat("### Target '{0}' FAIL\r\n", tr.Key); + if (res.Items != null && res.Items.Length > 0) { + resInfo.AppendFormat( + "Items: {0}\r\n", string.Join(", ", res.Items + .Select(it => it.ItemSpec))); + } + var e = tr.Value?.Exception; + if (e != null) { + resInfo.AppendFormat( + "Exception: {0}\r\nStacktrace:\r\n{1}\r\n", + e.Message, e.StackTrace); + } + } + } + } + } + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] {3}\r\n{4}", + DateTime.Now, Thread.CurrentThread.ManagedThreadId, + ConfiguredProject.ProjectConfiguration.Name, + resMsg, resInfo.ToString())); + } + + if (result == null + || result.ResultsByTarget == null + || result.OverallResult != BuildResultCode.Success) { + Messages.Print(string.Format("{0}: background build FAILED!", + Path.GetFileName(UnconfiguredProject.FullPath))); + } else { + var checkResults = result.ResultsByTarget + .Where(x => Targets.Contains(x.Key)) + .Select(x => x.Value); + ok = checkResults.Any() + && checkResults.All(x => x.ResultCode == TargetResultCode.Success); + if (ok) + msBuildProject.MarkDirty(); + } + await writeAccess.ReleaseAsync(); + } + + if (ok) { + var vcProject = Project.Object as VCProject; + var vcConfigs = vcProject.Configurations as IVCCollection; + var vcConfig = vcConfigs.Item(ConfiguredProject.ProjectConfiguration.Name) as VCConfiguration; + var props = vcConfig.Rules.Item("QtRule10_Settings") as IVCRulePropertyStorage; + props.SetPropertyValue("QtLastBackgroundBuild", DateTime.UtcNow.ToString("o")); + } + } catch (Exception e) { + Messages.Print(string.Format("{0}: background build ERROR: {1}", + Path.GetFileName(UnconfiguredProject.FullPath), e.Message)); + } + + if (LoggerVerbosity != LoggerVerbosity.Quiet) { + Messages.Print(string.Format( +@" +== {0}: build {1}", + Project.Name, ok ? "successful" : "ERROR")); + } + } + } +} diff --git a/QtVsTools.Package/QtMsBuild/QtProjectIntelliSense.cs b/QtVsTools.Package/QtMsBuild/QtProjectIntelliSense.cs new file mode 100644 index 0000000..605763f --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtProjectIntelliSense.cs @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Build.Framework; +using Microsoft.VisualStudio.Threading; + +namespace QtVsTools.QtMsBuild +{ + using Core; + using Thread = System.Threading.Thread; + + static class QtProjectIntellisense + { + public static void Refresh( + EnvDTE.Project project, + string configId = null, + IEnumerable<string> selectedFiles = null) + { + if (project == null || !QtProjectTracker.IsTracked(project)) + return; + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectIntellisense({1}): Refreshing: [{2}] {3}", + DateTime.Now, Thread.CurrentThread.ManagedThreadId, + (configId != null) ? configId : "(all configs)", project.FullName)); + } + Task.Run(() => RefreshAsync(project, configId, selectedFiles)); + } + + public static async Task RefreshAsync( + EnvDTE.Project project, + string configId = null, + IEnumerable<string> selectedFiles = null) + { + if (project == null || !QtProjectTracker.IsTracked(project)) + return; + var tracker = QtProjectTracker.Get(project); + await tracker.Initialized; + + var properties = new Dictionary<string, string>(); + properties["QtVSToolsBuild"] = "true"; + if (selectedFiles != null) + properties["SelectedFiles"] = string.Join(";", selectedFiles); + var targets = new List<string> { "QtVars" }; + if (QtVsToolsPackage.Instance.Options.BuildRunQtTools) + targets.Add("Qt"); + + IEnumerable<string> configs; + if (configId != null) { + configs = new[] { configId }; + } else { + var knownConfigs = await tracker.UnconfiguredProject.Services + .ProjectConfigurationsService.GetKnownProjectConfigurationsAsync(); + configs = knownConfigs.Select(x => x.Name); + } + + foreach (var config in configs) { + await QtProjectBuild.StartBuildAsync( + project, config, properties, targets, + LoggerVerbosity.Quiet); + } + } + } +} diff --git a/QtVsTools.Package/QtMsBuild/QtProjectLogger.cs b/QtVsTools.Package/QtMsBuild/QtProjectLogger.cs new file mode 100644 index 0000000..60bc1f7 --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtProjectLogger.cs @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.Build.Framework; + +namespace QtVsTools.QtMsBuild +{ + using Core; + + class QtProjectLogger : ILogger + { + public LoggerVerbosity Verbosity { get; set; } + public string Parameters { get; set; } + + public void Initialize(IEventSource eventSource) + { + eventSource.ErrorRaised += ErrorRaised; + eventSource.WarningRaised += WarningRaised; + eventSource.MessageRaised += MessageRaised; + eventSource.TargetStarted += TargetStarted; + eventSource.TargetFinished += TargetFinished; + eventSource.TaskStarted += TaskStarted; + eventSource.TaskFinished += TaskFinished; + eventSource.AnyEventRaised += AnyEventRaised; + } + + private void ErrorRaised(object sender, BuildErrorEventArgs e) + { + if (Verbosity == LoggerVerbosity.Quiet) + return; + Messages.Print(e.Message); + } + + private void WarningRaised(object sender, BuildWarningEventArgs e) + { + if (Verbosity == LoggerVerbosity.Quiet) + return; + Messages.Print(e.Message); + } + + private void MessageRaised(object sender, BuildMessageEventArgs e) + { + if (Verbosity <= LoggerVerbosity.Quiet) + return; + if (Verbosity <= LoggerVerbosity.Minimal && e.SenderName != "Message") + return; + if (Verbosity <= LoggerVerbosity.Normal && e.Importance != MessageImportance.High) + return; + if (Verbosity <= LoggerVerbosity.Detailed + && (e.Importance == MessageImportance.Low || e.SenderName == "MSBuild")) { + return; + } + Messages.Print(e.Message); + } + + private void TargetStarted(object sender, TargetStartedEventArgs e) + { + if (Verbosity < LoggerVerbosity.Detailed) + return; + Messages.Print(e.Message); + } + + private void TargetFinished(object sender, TargetFinishedEventArgs e) + { + if (Verbosity < LoggerVerbosity.Detailed) + return; + Messages.Print(e.Message); + } + + private void TaskStarted(object sender, TaskStartedEventArgs e) + { + if (Verbosity < LoggerVerbosity.Detailed) + return; + Messages.Print(e.Message); + } + + private void TaskFinished(object sender, TaskFinishedEventArgs e) + { + if (Verbosity < LoggerVerbosity.Detailed) + return; + Messages.Print(e.Message); + } + + private void AnyEventRaised(object sender, BuildEventArgs e) + { + if (Verbosity < LoggerVerbosity.Diagnostic + || e is BuildMessageEventArgs + || e is BuildErrorEventArgs + || e is BuildWarningEventArgs + || e is TargetStartedEventArgs + || e is TargetFinishedEventArgs + || e is TaskStartedEventArgs + || e is TaskFinishedEventArgs) { + return; + } + Messages.Print(e.Message); + } + + public void Shutdown() + { + } + } +} diff --git a/QtVsTools.Package/QtMsBuild/QtProjectTracker.cs b/QtVsTools.Package/QtMsBuild/QtProjectTracker.cs new file mode 100644 index 0000000..66db5c7 --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtProjectTracker.cs @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Properties; +using Microsoft.VisualStudio.TaskStatusCenter; +using Microsoft.VisualStudio.Threading; +using EnvDTE; + +namespace QtVsTools.QtMsBuild +{ + using Core; + using VisualStudio; + using Thread = System.Threading.Thread; + using SubscriberAction = ActionBlock<IProjectVersionedValue<IProjectSubscriptionUpdate>>; + + class QtProjectTracker : Concurrent<QtProjectTracker> + { + static ConcurrentDictionary<string, QtProjectTracker> _Instances; + static ConcurrentDictionary<string, QtProjectTracker> Instances => + StaticThreadSafeInit(() => _Instances, () => + _Instances = new ConcurrentDictionary<string, QtProjectTracker>()); + + static PunisherQueue<QtProjectTracker> _InitQueue; + static PunisherQueue<QtProjectTracker> InitQueue => + StaticThreadSafeInit(() => _InitQueue, () => + _InitQueue = new PunisherQueue<QtProjectTracker>()); + + static IVsTaskStatusCenterService _StatusCenter; + static IVsTaskStatusCenterService StatusCenter => StaticThreadSafeInit(() => _StatusCenter, + () => _StatusCenter = VsServiceProvider + .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>()); + + static Task InitDispatcher { get; set; } + static ITaskHandler2 InitStatus { get; set; } + + public static string SolutionPath { get; set; } = string.Empty; + + private QtProjectTracker() + { + Initialized = new EventWaitHandle(false, EventResetMode.ManualReset); + } + + class Subscriber : IDisposable + { + public Subscriber(QtProjectTracker tracker, ConfiguredProject config) + { + Tracker = tracker; + Config = config; + Subscription = Config.Services.ProjectSubscription.JointRuleSource.SourceBlock + .LinkTo(new SubscriberAction(ProjectUpdateAsync), + ruleNames: new[] + { + "ClCompile", + "QtRule10_Settings", + "QtRule30_Moc", + "QtRule40_Rcc", + "QtRule60_Repc", + "QtRule50_Uic", + "QtRule_Translation", + "QtRule70_Deploy", + }, + initialDataAsNew: false + ); + } + + QtProjectTracker Tracker { get; set; } + ConfiguredProject Config { get; set; } + IDisposable Subscription { get; set; } + + public void Dispose() + { + Subscription?.Dispose(); + Subscription = null; + } + + async Task ProjectUpdateAsync(IProjectVersionedValue<IProjectSubscriptionUpdate> update) + { + await Tracker.OnProjectUpdateAsync(Config, update.Value); + } + } + + public EnvDTE.Project Project { get; private set; } + public UnconfiguredProject UnconfiguredProject { get; private set; } + public EventWaitHandle Initialized { get; private set; } + List<Subscriber> Subscribers { get; set; } + + public static bool IsTracked(EnvDTE.Project project) + { + return Instances.ContainsKey(project.FullName); + } + + public static void Add(EnvDTE.Project project) + { + if (!QtVsToolsPackage.Instance.Options.ProjectTracking) + return; + Get(project); + } + + public static QtProjectTracker Get(EnvDTE.Project project) + { + lock (StaticCriticalSection) { + QtProjectTracker tracker = null; + if (Instances.TryGetValue(project.FullName, out tracker)) + return tracker; + tracker = new QtProjectTracker + { + Project = project, + }; + Instances[project.FullName] = tracker; + InitQueue.Enqueue(tracker); + if (InitDispatcher == null) + InitDispatcher = Task.Run(InitDispatcherLoopAsync); + return tracker; + } + } + + public static void Reset() + { + lock (StaticCriticalSection) { + Instances.Clear(); + InitQueue.Clear(); + } + } + + static async Task InitDispatcherLoopAsync() + { + while (!QtVsToolsPackage.Instance.Zombied) { + while (InitQueue.IsEmpty) + await Task.Delay(100); + QtProjectTracker tracker; + if (InitQueue.TryDequeue(out tracker)) { + if (InitStatus == null) { + await QtVsToolsPackage.Instance.JoinableTaskFactory.SwitchToMainThreadAsync(); + tracker.BeginInitStatus(); + await TaskScheduler.Default; + } else { + tracker.UpdateInitStatus(0); + } + await tracker.InitializeAsync(); + } + if (InitStatus != null + && (InitQueue.IsEmpty + || InitStatus.UserCancellation.IsCancellationRequested)) { + if (InitStatus.UserCancellation.IsCancellationRequested) { + InitQueue.Clear(); + } + tracker.EndInitStatus(); + } + } + } + + async Task InitializeAsync() + { + int p = 0; + UpdateInitStatus(p += 10); + + await QtVsToolsPackage.Instance.JoinableTaskFactory.SwitchToMainThreadAsync(); + UpdateInitStatus(p += 10); + + var context = Project.Object as IVsBrowseObjectContext; + if (context == null) + return; + UpdateInitStatus(p += 10); + + UnconfiguredProject = context.UnconfiguredProject; + if (UnconfiguredProject == null + || UnconfiguredProject.ProjectService == null + || UnconfiguredProject.ProjectService.Services == null) + return; + await TaskScheduler.Default; + UpdateInitStatus(p += 10); + + var configs = await UnconfiguredProject.Services + .ProjectConfigurationsService.GetKnownProjectConfigurationsAsync(); + UpdateInitStatus(p += 10); + + Initialized.Set(); + + Subscribers = new List<Subscriber>(); + int n = configs.Count; + int d = (100 - p) / (n * 2); + foreach (var config in configs) { + var configProject = await UnconfiguredProject.LoadConfiguredProjectAsync(config); + UpdateInitStatus(p += d); + Subscribers.Add(new Subscriber(this, configProject)); + configProject.ProjectUnloading += OnProjectUnloading; + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectTracker({1}): Started tracking [{2}] {3}", + DateTime.Now, Thread.CurrentThread.ManagedThreadId, + config.Name, + UnconfiguredProject.FullPath)); + } + UpdateInitStatus(p += d); + } + } + + + async Task OnProjectUpdateAsync(ConfiguredProject config, IProjectSubscriptionUpdate update) + { + var changes = update.ProjectChanges.Values + .Where(x => x.Difference.AnyChanges) + .Select(x => x.Difference); + var changesCount = changes + .Select(x => x.AddedItems.Count + + x.ChangedItems.Count + + x.ChangedProperties.Count + + x.RemovedItems.Count + + x.RenamedItems.Count) + .Sum(); + var changedProps = changes.SelectMany(x => x.ChangedProperties); + if (changesCount == 0 + || (changesCount == 1 + && changedProps.Count() == 1 + && changedProps.First() == "QtLastBackgroundBuild")) { + return; + } + + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectTracker({1}): Changed [{2}] {3}", + DateTime.Now, Thread.CurrentThread.ManagedThreadId, + config.ProjectConfiguration.Name, + config.UnconfiguredProject.FullPath)); + } + await QtProjectIntellisense.RefreshAsync(Project, config.ProjectConfiguration.Name); + } + + async Task OnProjectUnloading(object sender, EventArgs args) + { + var project = sender as ConfiguredProject; + if (project == null || project.Services == null) + return; + if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) { + Messages.Print(string.Format( + "{0:HH:mm:ss.FFF} QtProjectTracker: Stopped tracking [{1}] {2}", + DateTime.Now, + project.ProjectConfiguration.Name, + project.UnconfiguredProject.FullPath)); + } + lock (CriticalSection) { + if (Subscribers != null) { + Subscribers.ForEach(s => s.Dispose()); + Subscribers.Clear(); + Subscribers = null; + } + project.ProjectUnloading -= OnProjectUnloading; + Instances.TryRemove(Project.FullName, out QtProjectTracker tracker); + } + } + + void BeginInitStatus() + { + lock (StaticCriticalSection) { + if (InitStatus != null) + return; + try { + InitStatus = StatusCenter.PreRegister( + new TaskHandlerOptions + { + Title = "Qt VS Tools: Setting up project tracking..." + }, + new TaskProgressData + { + ProgressText = string.Format("{0} ({1} projects remaining)", + Project.Name, InitQueue.Count), + CanBeCanceled = true, + PercentComplete = 0 + }) + as ITaskHandler2; + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + InitStatus.RegisterTask(new Task(() => throw new InvalidOperationException())); + } + } + + void UpdateInitStatus(int percentComplete) + { + lock (StaticCriticalSection) { + if (InitStatus == null) + return; + try { + InitStatus.Progress.Report(new TaskProgressData + { + ProgressText = string.Format("{0} ({1} project(s) remaining)", + Project.Name, InitQueue.Count), + CanBeCanceled = true, + PercentComplete = percentComplete + }); + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + } + } + + void EndInitStatus() + { + lock (StaticCriticalSection) { + if (InitStatus == null) + return; + InitStatus.Dismiss(); + InitStatus = null; + } + } + } +} diff --git a/QtVsTools.Package/QtMsBuild/QtVersionProvider.cs b/QtVsTools.Package/QtMsBuild/QtVersionProvider.cs new file mode 100644 index 0000000..f9626ca --- /dev/null +++ b/QtVsTools.Package/QtMsBuild/QtVersionProvider.cs @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Win32; +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Properties; +using Microsoft.Build.Framework.XamlTypes; + +namespace QtVsTools.QtMsBuild +{ + [ExportDynamicEnumValuesProvider("QtVersionProvider")] + [AppliesTo("IntegratedConsoleDebugging")] + internal class QtVersionProvider : + IDynamicEnumValuesProvider, + IDynamicEnumValuesGenerator + { + + [ImportingConstructor] + protected QtVersionProvider(UnconfiguredProject project) + { } + + public async Task<IDynamicEnumValuesGenerator> GetProviderAsync(IList<NameValuePair> opts) + { + return await Task.FromResult(this); + } + + public bool AllowCustomValues { get { return true; } } + + public async Task<ICollection<IEnumValue>> GetListedValuesAsync() + { + List<IEnumValue> values = new List<IEnumValue>(); + using (var qtVersions = Registry.CurrentUser.OpenSubKey(@"Software\Digia\Versions")) { + + return await Task.FromResult( + qtVersions.GetSubKeyNames() + .Select(x => new PageEnumValue(new EnumValue() + { + Name = x, + DisplayName = x + })) + .Cast<IEnumValue>() + .ToList()); + } + } + + public async Task<IEnumValue> TryCreateEnumValueAsync(string userSuppliedValue) + { + return await Task.FromResult(new PageEnumValue(new EnumValue() + { + Name = userSuppliedValue, + DisplayName = userSuppliedValue + })); + } + } +} diff --git a/QtVsTools.Package/QtVsTools.Package.csproj b/QtVsTools.Package/QtVsTools.Package.csproj new file mode 100644 index 0000000..270b6c9 --- /dev/null +++ b/QtVsTools.Package/QtVsTools.Package.csproj @@ -0,0 +1,631 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(SolutionDir)\version.targets" /> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + <UseCodebase>true</UseCodebase> + </PropertyGroup> + <PropertyGroup> + <SignAssembly>false</SignAssembly> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>QtVsTools.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <RootNamespace>QtVsTools.Package</RootNamespace> + <AssemblyName>QtVsTools</AssemblyName> + <ProjectGuid>{671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}</ProjectGuid> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <GeneratePkgDefFile>true</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>true</IncludeDebugSymbolsInLocalVSIXDeployment> + <CopyBuildOutputToOutputDirectory>true</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + <StartAction>Program</StartAction> + <StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram> + <StartArguments>/rootsuffix Exp</StartArguments> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>TRACE;DEBUG</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + <Reference Include="System" /> + <Reference Include="System.Data" /> + <Reference Include="System.Design" /> + <Reference Include="System.Drawing" /> + <Reference Include="PresentationCore" /> + <Reference Include="PresentationFramework" /> + <Reference Include="WindowsBase" /> + <Reference Include="Microsoft.VisualStudio.ProjectSystem"> + <HintPath>$(VsInstallRoot)\Common7\IDE\CommonExtensions\Microsoft\Project\Microsoft.VisualStudio.ProjectSystem.dll</HintPath> + </Reference> + <Reference Include="Microsoft.VisualStudio.Composition"> + <HintPath>$(VsInstallRoot)\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll</HintPath> + </Reference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="System.ComponentModel.Composition" + Version="$(Version_System_ComponentModel_Composition)" /> + <PackageReference Include="Stub.System.Data.SQLite.Core.NetFramework" + Version="$(Version_Stub_System_Data_SQLite_Core_NetFramework)" + GeneratePathProperty="true" /> + <PackageReference Include="Newtonsoft.Json" + Version="$(Version_Newtonsoft_Json)" /> + <PackageReference Include="Microsoft.Build" + Version="$(Version_Microsoft_Build)" /> + <PackageReference Include="Microsoft.Build.Framework" + Version="$(Version_Microsoft_Build_Framework)" /> + <PackageReference Include="Microsoft.Build.Tasks.Core" + Version="$(Version_Microsoft_Build_Tasks_Core)" /> + <PackageReference Include="Microsoft.VisualStudio.SDK" + Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" /> + <PackageReference Include="Microsoft.VSSDK.BuildTools" + Version="$(Version_Microsoft_VSSDK_BuildTools)" /> + <PackageReference Include="Microsoft.VisualStudio.Shell.Framework" + Version="$(Version_Microsoft_VisualStudio_Shell_Framework)" /> + <PackageReference Include="Microsoft.VisualStudio.Validation" + Version="$(Version_Microsoft_VisualStudio_Validation)" /> + <PackageReference Include="$(Name_Microsoft_VisualStudio_Threading)" + Version="$(Version_Microsoft_VisualStudio_Threading)" /> + <PackageReference Include="System.Collections.Immutable" + Version="$(Version_System_Collections_Immutable)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'"> + <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" + Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'"> + <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" + Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'"> + <Reference Include="Microsoft.VisualStudio.VCProjectEngine" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="..\QtVsTools.RegExpr\QtVsTools.RegExpr.csproj"> + <Project>{a2831b9b-4d3b-46cb-85df-1b5c277c17db}</Project> + <Name>QtVsTools.RegExpr</Name> + </ProjectReference> + <ProjectReference Include="..\QtMSBuild\QtMSBuild.csproj"> + <Project>{a618d28b-9352-44f4-aa71-609bf68bf871}</Project> + <Name>QtMSBuild</Name> + </ProjectReference> + <ProjectReference Include="..\QtVsTools.Core\QtVsTools.Core.csproj"> + <Project>{2621ad55-c4e9-4884-81e9-da0d00b4c6e5}</Project> + <Name>QtVsTools.Core</Name> + </ProjectReference> + <ProjectReference Include="..\QtVsTools.Wizards\QtVsTools.Wizards.csproj"> + <Project>{8df0a4c4-a32d-44a4-9da4-41a6bca80b44}</Project> + <Name>QtVsTools.Wizards</Name> + </ProjectReference> + <ProjectReference Include="..\qmakefilereader\qmakefilereader.vcxproj"> + <Project>{70711a97-d9b0-3a86-9756-9ff47337908b}</Project> + <Name>qmakefilereader</Name> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> + <ProjectReference Include="..\qrceditor\qrceditor.vcxproj"> + <Project>{4cee73c9-fcfa-3a72-a0a3-036bdbb3240f}</Project> + <Name>qrceditor</Name> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> + <ProjectReference Include="..\vsqml\vsqml.vcxproj"> + <Project>{b12702ad-abfb-343a-a199-8e24837244a3}</Project> + <Name>VsQml</Name> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> + <ProjectReference Include="..\Templates\console\QtTemplate.Project.Console.csproj"> + <Project>{71e4f2b9-3628-45e2-a664-a2f6e3aafe55}</Project> + <Name>QtTemplate.Project.Console</Name> + <VSIXSubPath>ProjectTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Project</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\gui\QtTemplate.Project.Gui.csproj"> + <Project>{3e1f9741-01d3-480a-a484-3cd373ae4a7f}</Project> + <Name>QtTemplate.Project.Gui</Name> + <VSIXSubPath>ProjectTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Project</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\designer\QtTemplate.Project.Designer.csproj"> + <Project>{191b2b91-d5fd-4138-a72a-ab0b149988c6}</Project> + <Name>QtTemplate.Project.Designer</Name> + <VSIXSubPath>ProjectTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Project</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\empty\QtTemplate.Project.Empty.csproj"> + <Project>{73c3dfb8-bd8a-4634-aad5-dc71edc37b98}</Project> + <Name>QtTemplate.Project.Empty</Name> + <VSIXSubPath>ProjectTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Project</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\lib\QtTemplate.Project.Lib.csproj"> + <Project>{191b1b91-d9fd-4138-a72a-ab0b149958c6}</Project> + <Name>QtTemplate.Project.Lib</Name> + <VSIXSubPath>ProjectTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Project</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\quick\QtTemplate.Project.Quick.csproj"> + <Project>{4833e4c7-ffff-4da5-a7a5-36c6c3840f16}</Project> + <Name>QtTemplate.Project.Quick</Name> + <VSIXSubPath>ProjectTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Project</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\server\QtTemplate.Project.Server.csproj"> + <Project>{8ae9d385-a379-4f5f-a703-3df643da6742}</Project> + <Name>QtTemplate.Project.Server</Name> + <VSIXSubPath>ProjectTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Project</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\widget\QtTemplate.Item.Widget.csproj"> + <Project>{40adfd6a-64ea-4c77-9d4b-3a91d6ab76b4}</Project> + <Name>QtTemplate.Item.Widget</Name> + <VSIXSubPath>ItemTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Item</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\dialogbuttonbottom\QtTemplate.Item.DialogButtonBottom.csproj"> + <Project>{f7407750-5f72-460f-9c53-27cf509a39b1}</Project> + <Name>QtTemplate.Item.DialogButtonBottom</Name> + <VSIXSubPath>ItemTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Item</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\dialogbuttonright\QtTemplate.Item.DialogButtonRight.csproj"> + <Project>{f2166b59-e41b-4328-b31d-9e2b9ac5a59c}</Project> + <Name>QtTemplate.Item.DialogButtonRight</Name> + <VSIXSubPath>ItemTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Item</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\mainwindow\QtTemplate.Item.MainWindow.csproj"> + <Project>{20055427-1352-44fb-8442-bf7f15f9c59e}</Project> + <Name>QtTemplate.Item.MainWindow</Name> + <VSIXSubPath>ItemTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Item</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\qmldir\QtTemplate.Item.QMLDir.csproj"> + <Project>{7af6c34b-65d2-4010-92f6-420e59dde9bf}</Project> + <Name>QtTemplate.Item.QMLDir</Name> + <VSIXSubPath>ItemTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Item</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\qml\QtTemplate.Item.QMLFile.csproj"> + <Project>{dc1ae91b-45ce-4c5b-8f77-cdb58566038f}</Project> + <Name>QtTemplate.Item.QMLFile</Name> + <VSIXSubPath>ItemTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Item</TemplateType> + </ProjectReference> + <ProjectReference Include="..\Templates\resource\QtTemplate.Item.Resource.csproj"> + <Project>{bda1cd69-624b-4d9d-9b88-acbeb14ac471}</Project> + <Name>QtTemplate.Item.Resource</Name> + <VSIXSubPath>ItemTemplates</VSIXSubPath> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + <IncludeOutputGroupsInVSIX>TemplateProjectOutputGroup%3b</IncludeOutputGroupsInVSIX> + <TemplateType>Item</TemplateType> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + <Compile Include="QtVsToolsPackage.cs" /> + <T4Template Include="QtMenus.vsct_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>QtMenus.vsct</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>QtMenus.vsct</LastGenOutput> + </T4Template> + <VSCTCompile Include="QtMenus.vsct"> + <ResourceName>Menus.ctmenu</ResourceName> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>QtMenus.vsct_TT</DependentUpon> + </VSCTCompile> + <Content Include="Resources\Vsix.ico" /> + <EmbeddedResource Include="VSPackage.resx"> + <MergeWithCTO>true</MergeWithCTO> + <ManifestResourceName>VSPackage</ManifestResourceName> + </EmbeddedResource> + <Compile Include="Package\AddTranslationDialog.cs"> + <SubType>Form</SubType> + </Compile> + <Compile Include="Package\ChangeFor.cs" /> + <Compile Include="Common\Concurrent.cs" /> + <Compile Include="Common\ConcurrentStopwatch.cs" /> + <Compile Include="Common\Disposable.cs" /> + <Compile Include="Common\PriorityQueue.cs" /> + <Compile Include="Common\NativeAPI.cs" /> + <Compile Include="Common\Prototyped.cs" /> + <Compile Include="Common\Json\DeferredObject.cs" /> + <Compile Include="Common\Json\Serializable.cs" /> + <Compile Include="Common\Json\SerializableEnum.cs" /> + <Compile Include="Common\Json\Serializer.cs" /> + <Compile Include="Common\PunisherQueue.cs" /> + <Compile Include="Common\Timestamp.cs" /> + <Compile Include="Package\DteEventsHandler.cs" /> + <Compile Include="Editors\Editor.cs" /> + <Compile Include="Editors\Editor.QtDesigner.cs" /> + <Compile Include="Editors\Editor.QtLinguist.cs" /> + <Compile Include="Editors\Editor.QtResourceEditor.cs" /> + <Compile Include="Package\ExtLoader.cs" /> + <Compile Include="Package\FormChangeQtVersion.cs"> + <SubType>Form</SubType> + </Compile> + <Compile Include="Package\FormChangeQtVersion.Designer.cs"> + <DependentUpon>FormChangeQtVersion.cs</DependentUpon> + </Compile> + <Compile Include="Package\FormProjectQtSettings.cs"> + <SubType>Form</SubType> + </Compile> + <Compile Include="Package\FormProjectQtSettings.Designer.cs"> + <DependentUpon>FormProjectQtSettings.cs</DependentUpon> + </Compile> + <Compile Include="Options\QtLegacyOptionsPage.cs"> + <SubType>Component</SubType> + </Compile> + <Compile Include="Options\QtOptionsPage.cs"> + <SubType>Component</SubType> + </Compile> + <Compile Include="Options\QtVersionsTable.cs"> + <DependentUpon>QtVersionsTable.xaml</DependentUpon> + </Compile> + <Compile Include="Options\QtVersionsPage.cs"> + <SubType>Component</SubType> + </Compile> + <Compile Include="Package\ProjectQtSettings.cs" /> + <Compile Include="Package\QMakeWrapper.cs" /> + <Compile Include="QML\Classification\QmlAsyncClassifier.cs" /> + <Compile Include="QML\Classification\QmlClassificationFormat.cs" /> + <Compile Include="QML\Classification\QmlExpressionEvalClassifier.cs" /> + <Compile Include="QML\Classification\QmlSyntaxClassifier.cs" /> + <Compile Include="QML\Classification\QmlErrorClassifier.cs" /> + <Compile Include="QML\Classification\QmlTag.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7BoilerPlate.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7InfoHelpers.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7Breakpoint.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7CodeContext.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7Enums.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7Events.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7Expression.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7Program.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7Property.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7StackFrame.cs" /> + <Compile Include="QML\Debugging\AD7\QmlDebugAD7Engine.cs" /> + <Compile Include="QML\Debugging\QmlDebugLauncher.cs" /> + <Compile Include="QML\Debugging\QmlFileSystem.cs" /> + <Compile Include="QML\Debugging\QmlDebugger.cs" /> + <Compile Include="QML\Debugging\V4\QmlDebugV4Client.cs" /> + <Compile Include="QML\Debugging\V4\QmlDebugV4Protocol.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Backtrace.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Break.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4ClearBreakpoint.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Connect.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Continue.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Disconnect.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Evaluate.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Exception.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Frame.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4JsObject.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Lookup.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Scope.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4SetBreakpoint.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4SetExceptionBreak.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Version.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4Message.cs" /> + <Compile Include="QML\Debugging\V4\Messages\QmlDebugV4JsValue.cs" /> + <Compile Include="QML\Parser\QmlParserDiagnostics.cs" /> + <Compile Include="QML\Parser\QmlParserInterop.cs" /> + <Compile Include="QML\Syntax\QmlAst.cs" /> + <Compile Include="QML\Syntax\QmlSyntax.cs" /> + <Compile Include="Package\QtHelpLinkChooser.xaml.cs"> + <DependentUpon>QtHelpLinkChooser.xaml</DependentUpon> + </Compile> + <Compile Include="Package\QtHelp.cs" /> + <Compile Include="Package\QtItemContextMenu.cs" /> + <Compile Include="Package\QtMainMenu.cs" /> + <Compile Include="Package\QtMsBuildConverter.cs" /> + <Compile Include="QtMsBuild\QtModulesEditor.cs" /> + <Compile Include="QtMsBuild\QtModulesPopup.xaml.cs"> + <DependentUpon>QtModulesPopup.xaml</DependentUpon> + </Compile> + <Compile Include="QtMsBuild\QtProjectBuild.cs" /> + <Compile Include="QtMsBuild\QtProjectIntelliSense.cs" /> + <Compile Include="QtMsBuild\QtProjectLogger.cs" /> + <Compile Include="QtMsBuild\QtVersionProvider.cs" /> + <Compile Include="QtMsBuild\QtProjectTracker.cs" /> + <Compile Include="Package\QtProjectContextMenu.cs" /> + <Compile Include="Package\QtSolutionContextMenu.cs" /> + <Compile Include="Package\SR.cs" /> + <Compile Include="Package\Translation.cs" /> + <Compile Include="Package\TranslationItem.cs" /> + <Compile Include="VisualStudio\VsShell.cs" /> + <Compile Include="Common\VsToolsDialogWindow.cs" /> + <Content Include="..\Changelog"> + <Link>Changelog</Link> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <Content Include="..\LICENSE.GPL3-EXCEPT"> + <Link>LICENSE.GPL3-EXCEPT</Link> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <T4Template Include="source.extension.vsixmanifest_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>source.extension.vsixmanifest</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>source.extension.vsixmanifest</LastGenOutput> + </T4Template> + <None Include="source.extension.vsixmanifest"> + <DependentUpon>source.extension.vsixmanifest_TT</DependentUpon> + <SubType>Designer</SubType> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + </None> + <Content Include="$(SolutionDir)\qttmlanguage\**\*.*"> + <Link>QtTmLanguage\%(RecursiveDir)%(Filename)%(Extension)</Link> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <T4Template Include="Marketplace\Overview.html_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Marketplace\Overview.html</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>Overview.html</LastGenOutput> + </T4Template> + <Content Include="Marketplace\Overview.html"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>Overview.html_TT</DependentUpon> + </Content> + <Content Include="preview.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <Content Include="qt.ico"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <Content Include="qt5.natvis.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <Content Include="qt6.natvis.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <Content Include="qtmodules.xml"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + <SubType>Designer</SubType> + </Content> + <Content Include="QtVsTools.ico" /> + <Content Include="QtVsTools.Qml.Debug.pkgdef"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <Content Include="Resources\menuimages.png" /> + <Content Include="Resources\qt.ico"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + <IncludeInVSIX>true</IncludeInVSIX> + </Content> + <EmbeddedResource Include="Package\AddTranslationDialog.resx"> + <DependentUpon>AddTranslationDialog.cs</DependentUpon> + </EmbeddedResource> + <EmbeddedResource Include="Package\FormChangeQtVersion.resx"> + <DependentUpon>FormChangeQtVersion.cs</DependentUpon> + </EmbeddedResource> + <EmbeddedResource Include="Package\FormProjectQtSettings.resx"> + <DependentUpon>FormProjectQtSettings.cs</DependentUpon> + </EmbeddedResource> + <EmbeddedResource Include="Resources.resx"> + <SubType>Designer</SubType> + </EmbeddedResource> + <T4Template Include="Package\Version.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Version.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.targets;$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>Version.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Package\Version.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>Version.cs</DependentUpon> + </Compile> + <Page Include="Options\QtVersionsTable.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + <Page Include="Package\QtHelpLinkChooser.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + <Page Include="QtMsBuild\QtModulesPopup.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Target Name="QtVsTools_PostBuild" AfterTargets="Build"> + <Error Condition="!Exists('$(TargetPath)')" Text="Build failed." /> + <PropertyGroup> + <MsvcVersion Condition="'$(VisualStudioVersion)'=='15.0'">2017</MsvcVersion> + <MsvcVersion Condition="'$(VisualStudioVersion)'=='16.0'">2019</MsvcVersion> + <MsvcVersion Condition="'$(VisualStudioVersion)'=='17.0'">2022</MsvcVersion> + </PropertyGroup> + <GetAssemblyIdentity AssemblyFiles="$(TargetPath)"> + <Output TaskParameter="Assemblies" ItemName="QtVsToolsDll" /> + </GetAssemblyIdentity> + <CreateProperty Value="$([System.Version]::Parse(%(QtVsToolsDll.Version)).Revision.ToString('D2'))"> + <Output TaskParameter="ValueSetByTask" PropertyName="QtVSToolsRevision" /> + </CreateProperty> + <CreateProperty Condition="'$(QtVSToolsRevision)' != '' AND '$(QtVSToolsRevision)' != '-1'" Value="msvc$(MsvcVersion)-$(QtVSToolsVersion)-rev.$(QtVSToolsRevision)"> + <Output TaskParameter="ValueSetByTask" PropertyName="PackageName" /> + </CreateProperty> + <CreateProperty Condition="'$(QtVSToolsRevision)' == '' OR '$(QtVSToolsRevision)' == '-1'" Value="msvc$(MsvcVersion)-$(QtVSToolsVersion)"> + <Output TaskParameter="ValueSetByTask" PropertyName="PackageName" /> + </CreateProperty> + <CreateProperty Value="qt-vsaddin-$(PackageName).vsix"> + <Output TaskParameter="ValueSetByTask" PropertyName="ReleasePackage" /> + </CreateProperty> + <Message Importance="High" Text="$(MSBuildProjectName) -> $(PackageName)" /> + <CombinePath Condition="'$(QtVSToolsDeployTarget)' != ''" BasePath="$(QtVSToolsDeployTarget)" Paths="%(QtVsToolsDll.Version)"> + <Output TaskParameter="CombinedPaths" ItemName="DeployTargetPath" /> + </CombinePath> + <ConvertToAbsolutePath Condition="'$(QtVSToolsDeployTarget)' != ''" Paths="@(DeployTargetPath)"> + <Output TaskParameter="AbsolutePaths" ItemName="DeployTargetFullPath" /> + </ConvertToAbsolutePath> + <CreateProperty Condition="'$(QtVSToolsDeployTarget)' != ''" Value="%(DeployTargetFullPath.Identity)"> + <Output TaskParameter="ValueSetByTask" PropertyName="DeployTargetFullPath" /> + </CreateProperty> + <Message Importance="High" Condition="'$(QtVSToolsDeployTarget)' != ''" Text="Deploying to $(DeployTargetFullPath)" /> + <MakeDir Condition="'$(QtVSToolsDeployTarget)' != ''" Directories="$(QtVSToolsDeployTarget)" /> + <Copy Condition="'$(QtVSToolsDeployTarget)' != ''" SourceFiles="$(TargetVsixContainer)" DestinationFiles="$(DeployTargetFullPath)\$(ReleasePackage)" /> + <PropertyGroup> + <!-- Clean up properties --> + <MsvcVersion /> + <QtVSToolsRevision /> + <PackageName /> + <ReleasePackage /> + <DeployTargetFullPath /> + </PropertyGroup> + <ItemGroup> + <!-- Clean up items --> + <QtVsToolsDll Remove="@(QtVsToolsDll)" /> + <DeployTargetPath Remove="@(DeployTargetPath)" /> + <DeployTargetFullPath Remove="@(DeployTargetFullPath)" /> + </ItemGroup> + </Target> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // NuGet native libs + // --> + <PropertyGroup + Condition="'$(PkgStub_System_Data_SQLite_Core_NetFramework)' != ''"> + <SQLitePkgDir Condition="'$(SQLitePkgDir)' == ''" + >$(PkgStub_System_Data_SQLite_Core_NetFramework)</SQLitePkgDir> + </PropertyGroup> + <PropertyGroup + Condition="'$(PkgStub_System_Data_SQLite_Core_NetFramework)' == ''"> + <NuGetGlobalPkgDir Condition="'$(NuGetGlobalPkgDir)' == ''" + >$(USERPROFILE)\.nuget\packages</NuGetGlobalPkgDir> + <SQLitePkgRoot + >$(NuGetGlobalPkgDir)\Stub.System.Data.SQLite.Core.NetFramework</SQLitePkgRoot> + <SQLitePkgDir Condition="'$(SQLitePkgDir)' == ''" + >$(SQLitePkgRoot)\$(Version_Stub_System_Data_SQLite_Core_NetFramework)</SQLitePkgDir> + </PropertyGroup> + <ItemGroup> + <Content Include="$(SQLitePkgDir)\build\net46\x64\SQLite.Interop.dll"> + <Link>SQLite.Interop\x64\SQLite.Interop.dll</Link> + <IncludeInVSIX>true</IncludeInVSIX> + <VSIXSubPath>x64\</VSIXSubPath> + </Content> + <Content Include="$(SQLitePkgDir)\build\net46\x86\SQLite.Interop.dll"> + <Link>SQLite.Interop\x86\SQLite.Interop.dll</Link> + <IncludeInVSIX>true</IncludeInVSIX> + <VSIXSubPath>x86\</VSIXSubPath> + </Content> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/QtVsTools.Package/QtVsTools.Qml.Debug.pkgdef b/QtVsTools.Package/QtVsTools.Qml.Debug.pkgdef new file mode 100644 index 0000000..9bb70b9 --- /dev/null +++ b/QtVsTools.Package/QtVsTools.Qml.Debug.pkgdef @@ -0,0 +1,21 @@ +[$RootKey$\AD7Metrics\Engine\{86102A1B-4378-4964-A7ED-21852A8AFB7F}] +"CLSID"="{FA2993E3-8B2A-40A6-8853-AC2DB2DAED5A}" +"ProgramProvider"="{F2FF34E2-7FA5-461B-9E59-B5997EE0A637}" +"Attach"=dword:00000001 +"AddressBP"=dword:00000000 +"AlwaysLoadLocal"=dword:00000001 +"AutoSelectPriority"=dword:00000004 +"CallstackBP"=dword:00000001 +"Name"="QML" +"PortSupplier"="{708C1ECA-FF48-11D2-904F-00C04FA302A1}" +@="QML" + +[$RootKey$\CLSID\{FA2993E3-8B2A-40A6-8853-AC2DB2DAED5A}] +"Assembly"="QtVsTools" +"Class"="QtVsTools.Qml.Debug.AD7.QmlEngine" +"CodeBase"="$PackageFolder$\QtVsTools.dll" + +[$RootKey$\CLSID\{F2FF34E2-7FA5-461B-9E59-B5997EE0A637}] +"Assembly"="QtVsTools" +"Class"="QtVsTools.Qml.Debug.AD7.ProgramProvider" +"CodeBase"="$PackageFolder$\QtVsTools.dll" diff --git a/QtVsTools.Package/QtVsTools.ico b/QtVsTools.Package/QtVsTools.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/QtVsTools.Package/QtVsTools.ico Binary files differ diff --git a/QtVsTools.Package/QtVsToolsPackage.cs b/QtVsTools.Package/QtVsToolsPackage.cs new file mode 100644 index 0000000..aad01be --- /dev/null +++ b/QtVsTools.Package/QtVsToolsPackage.cs @@ -0,0 +1,473 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Task = System.Threading.Tasks.Task; +using System.Windows.Forms; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Settings; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Shell.Settings; +using Microsoft.VisualStudio.Threading; +using Microsoft.Win32; +using EnvDTE; + +namespace QtVsTools +{ + using Core; + using QtMsBuild; + using SyntaxAnalysis; + using static SyntaxAnalysis.RegExpr; + using VisualStudio; + + [Guid(QtVsToolsPackage.PackageGuidString)] + [InstalledProductRegistration("#110", "#112", Version.PRODUCT_VERSION, IconResourceID = 400)] + [ProvideMenuResource("Menus.ctmenu", 1)] + [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] + [ProvideAutoLoad(UIContextGuids.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)] + [ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)] + + // Custom editor: Qt Designer + [ProvideEditorExtension(typeof(Editors.QtDesigner), + extension: ".ui", + priority: 999, + DefaultName = Editors.QtDesigner.Title)] + [ProvideEditorLogicalView(typeof(Editors.QtDesigner), + logicalViewGuid: VSConstants.LOGVIEWID.TextView_string)] + + // Custom editor: Qt Linguist + [ProvideEditorExtension(typeof(Editors.QtLinguist), + extension: ".ts", + priority: 999, + DefaultName = Editors.QtLinguist.Title)] + [ProvideEditorLogicalView(typeof(Editors.QtLinguist), + logicalViewGuid: VSConstants.LOGVIEWID.TextView_string)] + + // Custom editor: Qt Resource Editor + [ProvideEditorExtension(typeof(Editors.QtResourceEditor), + extension: ".qrc", + priority: 999, + DefaultName = Editors.QtResourceEditor.Title)] + [ProvideEditorLogicalView(typeof(Editors.QtResourceEditor), + logicalViewGuid: VSConstants.LOGVIEWID.TextView_string)] + + // Options page + [ProvideOptionPage(typeof(Options.QtOptionsPage), + "Qt", "General", 0, 0, true, Sort = 0)] + + // Qt Versions page + [ProvideOptionPage(typeof(Options.QtVersionsPage), + "Qt", "Versions", 0, 0, true, Sort = 1)] + + // Legacy options page + [ProvideOptionPage(typeof(Options.QtLegacyOptionsPage), + "Qt", "Legacy Project Format", 0, 0, true, Sort = 2)] + + public sealed class QtVsToolsPackage : AsyncPackage, IVsServiceProvider, IProjectTracker + { + public const string PackageGuidString = "15021976-647e-4876-9040-2507afde45d2"; + const StringComparison IGNORE_CASE = StringComparison.InvariantCultureIgnoreCase; + + public DTE Dte { get; private set; } + public string PkgInstallPath { get; private set; } + public Options.QtOptionsPage Options + => GetDialogPage(typeof(Options.QtOptionsPage)) as Options.QtOptionsPage; + public Options.QtLegacyOptionsPage LegacyOptions + => GetDialogPage(typeof(Options.QtLegacyOptionsPage)) as Options.QtLegacyOptionsPage; + public Editors.QtDesigner QtDesigner { get; private set; } + public Editors.QtLinguist QtLinguist { get; private set; } + public Editors.QtResourceEditor QtResourceEditor { get; private set; } + + static EventWaitHandle initDone = new EventWaitHandle(false, EventResetMode.ManualReset); + + static QtVsToolsPackage instance = null; + public static QtVsToolsPackage Instance + { + get + { + initDone.WaitOne(); + return instance; + } + } + + private string locateHelperExecutable(string exeName) + { + if (!string.IsNullOrEmpty(PkgInstallPath) && File.Exists(PkgInstallPath + exeName)) + return PkgInstallPath + exeName; + return null; + } + + private string _QMakeFileReaderPath; + public string QMakeFileReaderPath + { + get + { + if (_QMakeFileReaderPath == null) + _QMakeFileReaderPath = locateHelperExecutable("QMakeFileReader.exe"); + return _QMakeFileReaderPath; + } + } + + static readonly Stopwatch initTimer = Stopwatch.StartNew(); + static readonly HttpClient http = new HttpClient(); + const string urlDownloadQtIo = "https://download.qt.io/development_releases/vsaddin/"; + + private DteEventsHandler eventHandler; + private bool useQtTmLanguage; + private string qtTmLanguagePath; + private string visualizersPath; + + + public QtVsToolsPackage() + { + } + + protected override async Task InitializeAsync( + CancellationToken cancellationToken, + IProgress<ServiceProgressData> progress) + { + try { + var timeInitBegin = initTimer.Elapsed; + VsServiceProvider.Instance = instance = this; + QtProject.ProjectTracker = this; + Messages.JoinableTaskFactory = JoinableTaskFactory; + + // determine the package installation directory + var uri = new Uri(System.Reflection.Assembly + .GetExecutingAssembly().EscapedCodeBase); + PkgInstallPath = Path.GetDirectoryName( + Uri.UnescapeDataString(uri.AbsolutePath)) + @"\"; + + /////////////////////////////////////////////////////////////////////////////////// + // Switch to main (UI) thread + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + var timeUiThreadBegin = initTimer.Elapsed; + + if ((Dte = VsServiceProvider.GetService<DTE>()) == null) + throw new Exception("Unable to get service: DTE"); + + QtVSIPSettings.Options = Options; + + eventHandler = new DteEventsHandler(Dte); + + Qml.Debug.Launcher.Initialize(); + QtMainMenu.Initialize(this); + QtSolutionContextMenu.Initialize(this); + QtProjectContextMenu.Initialize(this); + QtItemContextMenu.Initialize(this); + RegisterEditorFactory(QtDesigner = new Editors.QtDesigner()); + RegisterEditorFactory(QtLinguist = new Editors.QtLinguist()); + RegisterEditorFactory(QtResourceEditor = new Editors.QtResourceEditor()); + QtHelp.Initialize(this); + + if (!string.IsNullOrEmpty(VsShell.InstallRootDir)) + HelperFunctions.VCPath = Path.Combine(VsShell.InstallRootDir, "VC"); + + GetTextMateLanguagePath(); + GetNatvisPath(); + + /////////////////////////////////////////////////////////////////////////////////// + // Switch to background thread + await TaskScheduler.Default; + var timeUiThreadEnd = initTimer.Elapsed; + + var vm = QtVersionManager.The(initDone); + var error = string.Empty; + if (vm.HasInvalidVersions(out error)) + Messages.Print(error); + + /////////// + // Install Qt/MSBuild files from package folder to standard location + // -> %LOCALAPPDATA%\QtMsBuild + // + var QtMsBuildDefault = Path.Combine( + Environment.GetEnvironmentVariable("LocalAppData"), "QtMsBuild"); + try { + var qtMsBuildDefaultUri = new Uri(QtMsBuildDefault + "\\"); + var qtMsBuildVsixPath = Path.Combine(PkgInstallPath, "QtMsBuild"); + var qtMsBuildVsixUri = new Uri(qtMsBuildVsixPath + "\\"); + if (qtMsBuildVsixUri != qtMsBuildDefaultUri) { + var qtMsBuildVsixFiles = Directory + .GetFiles(qtMsBuildVsixPath, "*", SearchOption.AllDirectories) + .Select(x => qtMsBuildVsixUri.MakeRelativeUri(new Uri(x))); + foreach (var qtMsBuildFile in qtMsBuildVsixFiles) { + var sourcePath = new Uri(qtMsBuildVsixUri, qtMsBuildFile).LocalPath; + var targetPath = new Uri(qtMsBuildDefaultUri, qtMsBuildFile).LocalPath; + var targetPathTemp = targetPath + ".tmp"; + Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); + File.Copy(sourcePath, targetPathTemp, overwrite: true); + //////// + // Copy Qt/MSBuild files to standard location, taking care not to + // overwrite the updated Qt props file, possibly containing user-defined + // build settings (written by the VS Property Manager). This file is + // recognized as being named "Qt.props" and containing the import + // statement for qt_private.props. + // + string qtPrivateImport = + @"<Import Project=""$(MSBuildThisFileDirectory)\qt_private.props"""; + Func<string, bool> isUpdateQtProps = (string filePath) => + { + return Path.GetFileName(targetPath).Equals("Qt.props", IGNORE_CASE) + && File.ReadAllText(targetPath).Contains(qtPrivateImport); + }; + if (!File.Exists(targetPath)) { + // Target file does not exist + // -> Create new + File.Move(targetPathTemp, targetPath); + } else if (!isUpdateQtProps(targetPath)) { + // Target file is not the updated Qt.props + // -> Overwrite + File.Replace(targetPathTemp, targetPath, null); + } else { + // Target file *is* the updated Qt.props; skip! + // -> Remove temp file + File.Delete(targetPathTemp); + } + } + } + } catch { + ///////// + // Error copying files to standard location. + // -> FAIL-SAFE: use source folder (within package) as the standard location + QtMsBuildDefault = Path.Combine(PkgInstallPath, "QtMsBuild"); + } + + /////// + // Set %QTMSBUILD% by default to point to standard location of Qt/MSBuild + // + var QtMsBuildPath = Environment.GetEnvironmentVariable("QtMsBuild"); + if (string.IsNullOrEmpty(QtMsBuildPath)) { + + Environment.SetEnvironmentVariable( + "QtMsBuild", QtMsBuildDefault, + EnvironmentVariableTarget.User); + + Environment.SetEnvironmentVariable( + "QtMsBuild", QtMsBuildDefault, + EnvironmentVariableTarget.Process); + } + + CopyTextMateLanguageFiles(); + CopyNatvisFiles(); + + Messages.Print(string.Format("\r\n" + + "== Qt Visual Studio Tools version {0}\r\n" + + "\r\n" + + " Initialized in: {1:0.##} msecs\r\n" + + " Main (UI) thread: {2:0.##} msecs\r\n" + , Version.USER_VERSION + , (initTimer.Elapsed - timeInitBegin).TotalMilliseconds + , (timeUiThreadEnd - timeUiThreadBegin).TotalMilliseconds + )); + + var devRelease = await GetLatestDevelopmentReleaseAsync(); + if (devRelease != null) { + Messages.Print(string.Format(@" + ================================================================ + Qt Visual Studio Tools version {1} PREVIEW available at: + {0}{1}/ + ================================================================", + urlDownloadQtIo, devRelease)); + } + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } finally { + initDone.Set(); + initTimer.Stop(); + } + + /////////////////////////////////////////////////////////////////////////////////// + // Switch to main (UI) thread + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + ///////// + // Check if a solution was opened during initialization. + // If so, fire solution open event. + // + if (Dte?.Solution?.IsOpen == true) + eventHandler.SolutionEvents_Opened(); + } + + protected override int QueryClose(out bool canClose) + { + if (eventHandler != null) { + eventHandler.Disconnect(); + } + return base.QueryClose(out canClose); + } + + private void GetTextMateLanguagePath() + { + var settingsManager = new ShellSettingsManager(this as System.IServiceProvider); + var store = settingsManager.GetReadOnlySettingsStore(SettingsScope.UserSettings); + useQtTmLanguage = store.GetBoolean(@"QtVsTools\Qml\TextMate", @"Enable", true); + qtTmLanguagePath = Environment. + ExpandEnvironmentVariables("%USERPROFILE%\\.vs\\Extensions\\qttmlanguage"); + } + + private void CopyTextMateLanguageFiles() + { + if (useQtTmLanguage) { + HelperFunctions.CopyDirectory(Path.Combine(PkgInstallPath, "qttmlanguage"), + qtTmLanguagePath); + } else { + Directory.Delete(qtTmLanguagePath, true); + } + + //Remove textmate-based QML syntax highlighting + var qmlTextmate = Path.Combine(qtTmLanguagePath, "qml"); + if (Directory.Exists(qmlTextmate)) { + try { + Directory.Delete(qmlTextmate, true); + } catch { } + } + } + + private void CopyNatvisFile(string filename, string qtNamespace) + { + try { + string natvis = File.ReadAllText(Path.Combine(PkgInstallPath, filename)); + + string natvisFile; + if (string.IsNullOrEmpty(qtNamespace)) { + natvis = natvis.Replace("##NAMESPACE##::", string.Empty); + natvisFile = Path.GetFileNameWithoutExtension(filename); + } else { + natvis = natvis.Replace("##NAMESPACE##", qtNamespace); + natvisFile = string.Format(filename.Substring(0, filename.IndexOf('.')) + + "_{0}.natvis", qtNamespace.Replace("::", "_")); + } + + if (!Directory.Exists(visualizersPath)) + Directory.CreateDirectory(visualizersPath); + + File.WriteAllText(Path.Combine(visualizersPath, natvisFile), + natvis, System.Text.Encoding.UTF8); + } catch (Exception e) { + Messages.Print( + e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); + } + } + + public string GetNatvisPath() + { + try { + using (var vsRootKey = Registry.CurrentUser.OpenSubKey(Dte.RegistryRoot)) { + if (vsRootKey.GetValue("VisualStudioLocation") is string vsLocation) + visualizersPath = Path.Combine(vsLocation, "Visualizers"); + } + } catch { + } + if (string.IsNullOrEmpty(visualizersPath)) { + visualizersPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), +#if VS2022 + @"Visual Studio 2022\Visualizers\"); +#elif VS2019 + @"Visual Studio 2019\Visualizers\"); +#elif VS2017 + @"Visual Studio 2017\Visualizers\"); +#endif + } + return visualizersPath; + } + + public void CopyNatvisFiles(string qtNamespace = null) + { + string[] files = { "qt5.natvis.xml", "qt6.natvis.xml" }; + foreach (var file in files) + CopyNatvisFile(file, qtNamespace); + } + + public I GetService<T, I>() + where T : class + where I : class + { + return GetService(typeof(T)) as I; + } + + public async Task<I> GetServiceAsync<T, I>() + where T : class + where I : class + { + return await GetServiceAsync(typeof(T)) as I; + } + + void IProjectTracker.AddProject(Project project) + { + QtProjectTracker.Add(project); + } + + async Task<string> GetLatestDevelopmentReleaseAsync() + { + var currentVersion = new System.Version(Version.PRODUCT_VERSION); + try { + var response = await http.GetAsync(urlDownloadQtIo); + if (!response.IsSuccessStatusCode) + return null; + + var tokenVersion = new Token("VERSION", Number & "." & Number & "." & Number) + { + new Rule<System.Version> { Capture(value => new System.Version(value)) } + }; + var regexHrefVersion = "href=\"" & tokenVersion & Chars["/"].Optional() & "\""; + var regexResponse = (regexHrefVersion | AnyChar | VertSpace).Repeat(); + var parserResponse = regexResponse.Render(); + + var responseData = await response.Content.ReadAsStringAsync(); + var devVersion = parserResponse.Parse(responseData) + .GetValues<System.Version>("VERSION") + .Where(v => currentVersion < v) + .Max(); + if (devVersion == null) + return null; + + response = await http.GetAsync( + string.Format("{0}{1}/", urlDownloadQtIo, devVersion)); + if (!response.IsSuccessStatusCode) + return null; + return devVersion.ToString(); + } catch { + return null; + } + } + } +} diff --git a/QtVsTools.Package/Resources.resx b/QtVsTools.Package/Resources.resx new file mode 100644 index 0000000..4c63e0b --- /dev/null +++ b/QtVsTools.Package/Resources.resx @@ -0,0 +1,211 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="ActionDialog_Properties" xml:space="preserve"> + <value>Properties</value> + </data> + <data name="AddQtVersionDialog_IncorrectMakefileGenerator" xml:space="preserve"> + <value>This Qt version uses an unsupported makefile generator (used: {0}, supported: MSVC.NET, MSBUILD)</value> + </data> + <data name="AddTranslationDialog_FileName" xml:space="preserve"> + <value>Filename:</value> + </data> + <data name="AddTranslationDialog_Language" xml:space="preserve"> + <value>Language:</value> + </data> + <data name="AddTranslationDialog_Title" xml:space="preserve"> + <value>Add Translation</value> + </data> + <data name="Cancel" xml:space="preserve"> + <value>&Cancel</value> + </data> + <data name="CannotFindQMake" xml:space="preserve"> + <value>Cannot find qmake. Make sure you have specified a Qt version.</value> + </data> + <data name="ConvertConfirmation" xml:space="preserve"> + <value>Do you really want to convert the selected project?</value> + </data> + <data name="ConvertTitle" xml:space="preserve"> + <value>Project Conversion</value> + </data> + <data name="ConvertAllConfirmation" xml:space="preserve"> + <value>Do you really want to convert all projects?</value> + </data> + <data name="ConvertSaveConfirmation" xml:space="preserve"> + <value>Projects must be saved before conversion. Save projects?</value> + </data> + <data name="ConvertWait" xml:space="preserve"> + <value>Converting solution to Qt/MSBuild...</value> + </data> + <data name="ConvertProgress" xml:space="preserve"> + <value>Converting solution to Qt/MSBuild... +Converting project {0}/{1}: {2}...</value> + </data> + <data name="ConvertCanceled" xml:space="preserve"> + <value>Conversion canceled. {0} projects were not converted.</value> + </data> + <data name="ErrorConvertingProject" xml:space="preserve"> + <value>Error converting project {0}</value> + </data> + <data name="CancelConvertingProject" xml:space="preserve"> + <value>Project conversion canceled.</value> + </data> + <data name="NoProjectsToConvert" xml:space="preserve"> + <value>No projects to convert.</value> + </data> + <data name="NoVSSupport" xml:space="preserve"> + <value>The default Qt version does not support Visual Studio. To import .pro files, specify Qt 5.0 or later as the default.</value> + </data> + <data name="ImportPriFileNotResolved" xml:space="preserve"> + <value>--- (importing .pri file) file: {0} cannot be resolved. Skipping file.</value> + </data> + <data name="InstalledQtVersions" xml:space="preserve"> + <value>Installed Qt Versions</value> + </data> + <data name="NoProjectOpened" xml:space="preserve"> + <value>No Project Opened</value> + </data> + <data name="OK" xml:space="preserve"> + <value>&OK</value> + </data> + <data name="IncompatibleMacros" xml:space="preserve"> + <value>The following macros are not compatible: {0}</value> + </data> + <data name="ProjectQtSettingsButtonText" xml:space="preserve"> + <value>Qt Project Settings</value> + </data> + <data name="ProjectQtVersion" xml:space="preserve"> + <value>Set Project's Qt Version</value> + </data> + <data name="ProjectQtVersionNotFoundError" xml:space="preserve"> + <value>There's no Qt version assigned to project {0} for configuration {1}/{2}. Please use the 'Qt Project Settings' editor to change the 'Version' to a valid Qt version for this platform.</value> + </data> + <data name="QtModules" xml:space="preserve"> + <value>Qt Modules</value> + </data> + <data name="SolutionQtVersion" xml:space="preserve"> + <value>Set Solution's Qt Version</value> + </data> + <data name="ExportProject_ImportPriFile" xml:space="preserve"> + <value>Import from .pri File</value> + </data> + <data name="Resources_QtVsTools" xml:space="preserve"> + <value>Qt VS Tools</value> + </data> +</root> \ No newline at end of file diff --git a/QtVsTools.Package/Resources/menuimages.png b/QtVsTools.Package/Resources/menuimages.png new file mode 100644 index 0000000..ceadad9 --- /dev/null +++ b/QtVsTools.Package/Resources/menuimages.png Binary files differ diff --git a/QtVsTools.Package/Resources/qt.ico b/QtVsTools.Package/Resources/qt.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/QtVsTools.Package/Resources/qt.ico Binary files differ diff --git a/QtVsTools.Package/VSPackage.resx b/QtVsTools.Package/VSPackage.resx new file mode 100644 index 0000000..1305dc7 --- /dev/null +++ b/QtVsTools.Package/VSPackage.resx @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** + + VS SDK Notes: This resx file contains the resources that will be consumed from your package by Visual Studio. + For example, Visual Studio will attempt to load resource '400' from this resource stream when it needs to + load your package's icon. Because Visual Studio will always look in the VSPackage.resources stream first for + resources it needs, you should put additional resources that Visual Studio will load directly into this resx + file. + + Resources that you would like to access directly from your package in a strong-typed fashion should be stored + in Resources.resx or another resx file. +--> + +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089</value> + </resheader> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089" /> + <data name="110" xml:space="preserve"> + <value>Qt Visual Studio Tools</value> + </data> + <data name="112" xml:space="preserve"> + <value>The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.</value> + </data> + <data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>Resources\qt.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b03f5f7f11d50a3a</value> + </data> +</root> diff --git a/QtVsTools.Package/VisualStudio/VsShell.cs b/QtVsTools.Package/VisualStudio/VsShell.cs new file mode 100644 index 0000000..95b809f --- /dev/null +++ b/QtVsTools.Package/VisualStudio/VsShell.cs @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace QtVsTools.VisualStudio +{ + static class VsShell + { + public static string InstallRootDir + { + get + { + Initialize(); + return _InstallRootDir; + } + } + + private static IVsShell vsShell; + private static string _InstallRootDir; + + private static void Initialize() + { + if (vsShell != null) + return; + vsShell = VsServiceProvider.GetService<IVsShell>(); + + object objProp; + int res = vsShell.GetProperty((int)__VSSPROPID2.VSSPROPID_InstallRootDir, out objProp); + if (res == VSConstants.S_OK && objProp is string) + _InstallRootDir = objProp as string; + } + + public static EnvDTE.Project GetProject(IVsHierarchy context) + { + object value; + int res = context.GetProperty( + (uint)VSConstants.VSITEMID.Root, (int)__VSHPROPID.VSHPROPID_ExtObject, out value); + if (res != VSConstants.S_OK) + return null; + + return value as EnvDTE.Project; + } + + public static EnvDTE.ProjectItem GetProjectItem(IVsHierarchy context, uint itemid) + { + object value; + int res = context.GetProperty( + itemid, (int)__VSHPROPID.VSHPROPID_ExtObject, out value); + if (res != VSConstants.S_OK) + return null; + + return value as EnvDTE.ProjectItem; + } + + public static EnvDTE.Document GetDocument(IVsHierarchy context, uint itemid) + { + return GetProjectItem(context, itemid)?.Document; + } + } +} diff --git a/QtVsTools.Package/preview.png b/QtVsTools.Package/preview.png new file mode 100644 index 0000000..7797e6e --- /dev/null +++ b/QtVsTools.Package/preview.png Binary files differ diff --git a/QtVsTools.Package/qt.ico b/QtVsTools.Package/qt.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/QtVsTools.Package/qt.ico Binary files differ diff --git a/QtVsTools.Package/qt5.natvis.xml b/QtVsTools.Package/qt5.natvis.xml new file mode 100644 index 0000000..237fbd9 --- /dev/null +++ b/QtVsTools.Package/qt5.natvis.xml @@ -0,0 +1,808 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + **************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + **************************************************************************** +--> + +<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> + + <Type Name="##NAMESPACE##::QPoint"> + <AlternativeType Name="##NAMESPACE##::QPointF"/> + <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QRect"> + <DisplayString>{{ x = {x1}, y = {y1}, width = {x2 - x1 + 1}, height = {y2 - y1 + 1} }}</DisplayString> + <Expand> + <Item Name="[x]">x1</Item> + <Item Name="[y]">y1</Item> + <Item Name="[width]">x2 - x1 + 1</Item> + <Item Name="[height]">y2 - y1 + 1</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QRectF"> + <DisplayString>{{ x = {xp}, y = {yp}, width = {w}, height = {h} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + <Item Name="[width]">w</Item> + <Item Name="[height]">h</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QSize"> + <AlternativeType Name="##NAMESPACE##::QSizeF"/> + <DisplayString>{{ width = {wd}, height = {ht} }}</DisplayString> + <Expand> + <Item Name="[width]">wd</Item> + <Item Name="[height]">ht</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QLine"> + <AlternativeType Name="##NAMESPACE##::QLineF"/> + <DisplayString>{{ start point = {pt1}, end point = {pt2} }}</DisplayString> + <Expand> + <Synthetic Name="[start point]"> + <DisplayString>{pt1}</DisplayString> + <Expand> + <ExpandedItem>pt1</ExpandedItem> + </Expand> + </Synthetic> + <Synthetic Name="[end point]"> + <DisplayString>{pt2}</DisplayString> + <Expand> + <ExpandedItem>pt2</ExpandedItem> + </Expand> + </Synthetic> + + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPolygon"> + <DisplayString>{{ size = {d->size} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <ArrayItems> + <Size>d->size</Size> + <ValuePointer>(##NAMESPACE##::QPoint*)((reinterpret_cast<char*>(d)) + d->offset)</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPolygonF"> + <DisplayString>{{ size = {d->size} }}</DisplayString> + <Expand> + <Item Name="[closed]"> + d->size > 0 + && ((((##NAMESPACE##::QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[0]).xp + == (((##NAMESPACE##::QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[d->size - 1]).xp) + && ((((##NAMESPACE##::QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[0]).yp + == (((##NAMESPACE##::QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[d->size - 1]).yp) + </Item> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <ArrayItems> + <Size>d->size</Size> + <ValuePointer>(##NAMESPACE##::QPointF*)((reinterpret_cast<char*>(d)) + d->offset)</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name ="##NAMESPACE##::QVector2D"> + <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + </Expand> + </Type> + + <Type Name ="##NAMESPACE##::QVector3D"> + <DisplayString>{{ x = {xp}, y = {yp}, z = {zp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + <Item Name="[z]">zp</Item> + </Expand> + </Type> + + <Type Name ="##NAMESPACE##::QVector4D"> + <DisplayString>{{ x = {xp}, y = {yp}, z = {zp}, w = {wp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + <Item Name="[z]">zp</Item> + <Item Name="[w]">wp</Item> + </Expand> + </Type> + + <Type Name ="##NAMESPACE##::QMatrix"> + <DisplayString> + {{ m11 = {_m11}, m12 = {_m12}, m21 = {_m21}, m22 = {_m22}, ... }} + </DisplayString> + <Expand> + <Item Name="[m11]">_m11</Item> + <Item Name="[m12]">_m12</Item> + <Item Name="[m21]">_m21</Item> + <Item Name="[m22]">_m22</Item> + <Item Name="[dx]">_dx</Item> + <Item Name="[dy]">_dy</Item> + </Expand> + </Type> + + <Type Name ="##NAMESPACE##::QMatrix4x4"> + <DisplayString> + {{ m11 = {m[0][0]}, m12 = {m[1][0]}, m13 = {m[2][0]}, m14 = {m[3][0]}, ... }} + </DisplayString> + <Expand> + <Item Name="[m11]">m[0][0]</Item> + <Item Name="[m12]">m[1][0]</Item> + <Item Name="[m13]">m[2][0]</Item> + <Item Name="[m14]">m[3][0]</Item> + <Item Name="[m21]">m[0][1]</Item> + <Item Name="[m22]">m[1][1]</Item> + <Item Name="[m23]">m[2][1]</Item> + <Item Name="[m24]">m[3][1]</Item> + <Item Name="[m31]">m[0][2]</Item> + <Item Name="[m32]">m[1][2]</Item> + <Item Name="[m33]">m[2][2]</Item> + <Item Name="[m34]">m[3][2]</Item> + <Item Name="[m41]">m[0][3]</Item> + <Item Name="[m42]">m[1][3]</Item> + <Item Name="[m43]">m[2][3]</Item> + <Item Name="[m44]">m[3][3]</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QSizePolicy"> + <DisplayString> + {{ horizontal = {static_cast<Policy>(bits.horPolicy)}, vertical = {static_cast<Policy>(bits.verPolicy)}, type = {ControlType(1 << bits.ctype)} }} + </DisplayString> + <Expand> + <Synthetic Name="[vertical policy]"> + <DisplayString>##NAMESPACE##::QSizePolicy::Policy::{static_cast<Policy>(bits.verPolicy)}</DisplayString> + </Synthetic> + <Synthetic Name="[horizontal policy]"> + <DisplayString>##NAMESPACE##::QSizePolicy::Policy::{static_cast<Policy>(bits.horPolicy)}</DisplayString> + </Synthetic> + <Synthetic Name="[control type]"> + <DisplayString>##NAMESPACE##::QSizePolicy::ControlType::{ControlType(1 << bits.ctype)}</DisplayString> + </Synthetic> + <Synthetic Name="[expanding directions]"> + <DisplayString + Condition="(static_cast<Policy>(bits.verPolicy) & ExpandFlag)"> + ##NAMESPACE##::Qt::Vertical (2) + </DisplayString> + <DisplayString + Condition="(static_cast<Policy>(bits.horPolicy) & ExpandFlag)"> + ##NAMESPACE##::Qt::Horizontal (1) + </DisplayString> + </Synthetic> + <Item Name="[vertical stretch]">static_cast<int>(bits.verStretch)</Item> + <Item Name="[horizontal stretch]">static_cast<int>(bits.horStretch)</Item> + <Item Name="[has height for width]">bits.hfw == 1</Item> + <Item Name="[has width for height]">bits.wfh == 1</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QChar"> + <DisplayString>{ucs,c}</DisplayString> + <StringView>ucs,c</StringView> + <Expand> + <Item Name="[latin 1]">ucs > 0xff ? '\0' : char(ucs),c</Item> + <Item Name="[unicode]">ucs,c</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QString"> + <DisplayString>{((reinterpret_cast<unsigned short*>(d)) + d->offset / 2),sub}</DisplayString> + <StringView>((reinterpret_cast<unsigned short*>(d)) + d->offset / 2),sub</StringView> + <Expand> + <Item Name="[size]">d->size</Item> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <ArrayItems> + <Size>d->size</Size> + <ValuePointer>((reinterpret_cast<unsigned short*>(d)) + d->offset / 2),c</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QByteArray"> + <DisplayString>{((reinterpret_cast<char*>(d)) + d->offset),sb}</DisplayString> + <StringView>((reinterpret_cast<char*>(d)) + d->offset),sb</StringView> + <Expand> + <Item Name="[size]">d->size</Item> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <ArrayItems> + <Size>d->size</Size> + <ValuePointer>((reinterpret_cast<char*>(d)) + d->offset),c</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QUrl"> + <Intrinsic Name="isEmpty" Expression="size==0"> + <Parameter Name="size" Type="int"/> + </Intrinsic> + <Intrinsic Name="memberOffset" Expression="sizeof(QAtomicInt) + sizeof(int) + (sizeof(QString) * count)"> + <Parameter Name="count" Type="int"/> + </Intrinsic> + <Intrinsic Name="scheme" Expression="*((QString*)(((char*)(d) + memberOffset(0))))" /> + <Intrinsic Name="username" Expression="*((QString*)(((char*)(d) + memberOffset(1))))" /> + <Intrinsic Name="password" Expression="*((QString*)(((char*)(d) + memberOffset(2))))" /> + <Intrinsic Name="host" Expression="*((QString*)(((char*)(d) + memberOffset(3))))" /> + <Intrinsic Name="path" Expression="*((QString*)(((char*)(d) + memberOffset(4))))" /> + <Intrinsic Name="query" Expression="*((QString*)(((char*)(d) + memberOffset(5))))" /> + <Intrinsic Name="fragment" Expression="*((QString*)(((char*)(d) + memberOffset(6))))" /> + + <DisplayString Condition="!isEmpty(scheme().d->size)">{scheme()}://{host()}{path()}</DisplayString> + <DisplayString Condition="isEmpty(scheme().d->size)">{path()}</DisplayString> + <Expand> + <Item Name="[scheme]">scheme()</Item> + <Item Name="[username]">username()</Item> + <Item Name="[password]">password()</Item> + <Item Name="[host]">host()</Item> + <Item Name="[path]">path()</Item> + <Item Name="[query]">query()</Item> + <Item Name="[fragment]">fragment()</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QBitArray"> + <DisplayString>{{ size = {(d.d->size << 3) - *((reinterpret_cast<char*>(d.d)) + d.d->offset)} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d.d->ref.atomic._q_value</Item> + <IndexListItems> + <Size>(d.d->size << 3) - *((reinterpret_cast<char*>(d.d)) + d.d->offset)</Size> + <ValueNode> + (*(reinterpret_cast<const unsigned char*>((reinterpret_cast<char*>(d.d)) + d.d->offset) + 1 + + ($i >> 3)) & (1 << ($i & 7))) != 0 + </ValueNode> + </IndexListItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QVarLengthArray<*>"> + <AlternativeType Name="##NAMESPACE##::QVarLengthArray<*, int>"/> + <DisplayString>{{ size = {s} }}</DisplayString> + <Expand> + <Item Name="[capacity]">a</Item> + <ArrayItems> + <Size>s</Size> + <ValuePointer>ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QDate"> + <DisplayString>{{ julian day = {jd} }}</DisplayString> + <Expand></Expand> + </Type> + + <Type Name="##NAMESPACE##::QTime"> + <DisplayString + Condition="mds == 1">{{ millisecond = {mds} }}</DisplayString> + <DisplayString + Condition="mds != 1">{{ milliseconds = {mds} }}</DisplayString> + <Expand> + <Item Name="[hour]" + Condition="(mds / 3600000) == 1">mds / 3600000, d</Item> + <Item Name="[hours]" + Condition="(mds / 3600000) != 1">mds / 3600000, d</Item> + <Item Name="[minute]" + Condition="((mds % 3600000) / 60000) == 1">(mds % 3600000) / 60000, d</Item> + <Item Name="[minutes]" + Condition="((mds % 3600000) / 60000) != 1">(mds % 3600000) / 60000, d</Item> + <Item Name="[second]" + Condition="((mds / 1000) % 60) == 1">(mds / 1000) % 60, d</Item> + <Item Name="[seconds]" + Condition="((mds / 1000) % 60) != 1">(mds / 1000) % 60, d</Item> + <Item Name="[millisecond]" + Condition="(mds % 1000) == 1">mds % 1000, d</Item> + <Item Name="[milliseconds]" + Condition="(mds % 1000) != 1">mds % 1000, d</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QRegularExpression"> + <DisplayString>{d.pattern}</DisplayString> + </Type> + + <Type Name="##NAMESPACE##::QSharedData"> + <Expand> + <Item Name="[referenced]">ref._q_value</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QSharedPointer<*>"> + <DisplayString>strong reference to shared pointer of type {"$T1"}</DisplayString> + <Expand> + <Item Name="[is null]">value == 0</Item> + <Item Name="[weak referenced]">d->weakref._q_value</Item> + <Item Name="[strong referenced]">d->strongref._q_value</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QSharedDataPointer<*>"> + <DisplayString>pointer to implicit shared object of type {"$T1"}</DisplayString> + <Expand> + <ExpandedItem>d</ExpandedItem> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QExplicitlySharedDataPointer<*>"> + <DisplayString>pointer to explicit shared object of type {"$T1"}</DisplayString> + <Expand> + <ExpandedItem>d</ExpandedItem> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPointer<*>"> + <DisplayString>guarded pointer to subclass of QObject of type {"$T1"}</DisplayString> + <Expand> + <Item Name="[is null]">wp.d == 0 || wp.d->strongref._q_value == 0 || wp.value == 0</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QWeakPointer<*>"> + <DisplayString>weak reference to shared pointer of type {"$T1"}</DisplayString> + <Expand> + <Item Name="[is null]">d == 0 || d->strongref._q_value == 0 || value == 0</Item> + <Item Name="[weak referenced]">d->weakref._q_value</Item> + <Item Name="[strong referenced]">d->strongref._q_value</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QScopedPointer<*>"> + <DisplayString>scoped pointer to a dynamically allocated object of type {"$T1"}</DisplayString> + <Expand> + <Item Name="[is null]">!d</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QScopedArrayPointer<*>"> + <DisplayString>scoped pointer to dynamically allocated array of objects of type {"$T1"}</DisplayString> + <Expand> + <Item Name="[is null]">!d</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPair<*,*>"> + <DisplayString>({first}, {second})</DisplayString> + <Expand> + <Item Name="[first]">first</Item> + <Item Name="[second]">second</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QVector<*>"> + <AlternativeType Name="##NAMESPACE##::QStack<*>"></AlternativeType> + <DisplayString>{{ size = {d->size} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <ArrayItems> + <Size>d->size</Size> + <ValuePointer>($T1*)((reinterpret_cast<char*>(d)) + d->offset)</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QList<*>"> + <AlternativeType Name="##NAMESPACE##::QQueue<*>"></AlternativeType> + <DisplayString>{{ size = {d->end - d->begin} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <IndexListItems> + <Size>d->end - d->begin</Size> + <ValueNode>*reinterpret_cast<$T1*>((sizeof($T1) > sizeof(void*)) + ? reinterpret_cast<Node*>(d->array + d->begin + $i)->v + : reinterpret_cast<$T1*>(d->array + d->begin + $i)) + </ValueNode> + </IndexListItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QStringList"> + <DisplayString>{{ size = {d->end - d->begin} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <IndexListItems> + <Size>d->end - d->begin</Size> + <ValueNode> + *reinterpret_cast<QString*>((sizeof(QString) > sizeof(void*)) + ? reinterpret_cast<Node*>(d->array + d->begin + $i)->v + : reinterpret_cast<QString*>(d->array + d->begin + $i)) + </ValueNode> + </IndexListItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QList<QVariant>"> + <DisplayString>{{ size = {d->end - d->begin} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <IndexListItems> + <Size>d->end - d->begin</Size> + <ValueNode> + *reinterpret_cast<QVariant*>((sizeof(QVariant) > sizeof(void*)) + ? reinterpret_cast<Node*>(d->array + d->begin + $i)->v + : reinterpret_cast<QVariant*>(d->array + d->begin + $i)) + </ValueNode> + </IndexListItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QLinkedList<*>"> + <DisplayString>{{ size = {d->size} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <LinkedListItems> + <Size>d->size</Size> + <HeadPointer>d->n</HeadPointer> + <NextPointer>n</NextPointer> + <ValueNode>(*(##NAMESPACE##::QLinkedListNode<$T1>*)this).t</ValueNode> + </LinkedListItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QMapNode<*,*>"> + <DisplayString>({key}, {value})</DisplayString> + <Expand> + <Item Name="[key]">key</Item> + <Item Name="[value]">value</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QMap<*,*>"> + <AlternativeType Name="##NAMESPACE##::QMultiMap<*,*>"/> + <DisplayString>{{ size = {d->size} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <TreeItems> + <Size>d->size</Size> + <HeadPointer>d->header.left</HeadPointer> + <LeftPointer>left</LeftPointer> + <RightPointer>right</RightPointer> + <ValueNode>*((##NAMESPACE##::QMapNode<$T1,$T2>*)this)</ValueNode> + </TreeItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QHashNode<*,*>"> + <DisplayString Condition="next == 0">(empty)</DisplayString> + <DisplayString Condition="next != 0">({key}, {value})</DisplayString> + <Expand> + <Item Name="[key]" Condition="next != 0">key</Item> + <Item Name="[value]" Condition="next != 0">value</Item> + <Item Name="[next]" Condition="next != 0">next</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QHash<*,*>"> + <AlternativeType Name="##NAMESPACE##::QMultiHash<*,*>"/> + <DisplayString>{{ size = {d->size} }}</DisplayString> + <Expand> + <ArrayItems IncludeView="buckets"> + <Size>d->numBuckets</Size> + <ValuePointer>reinterpret_cast<Node **>(d->buckets)</ValuePointer> + </ArrayItems> + <CustomListItems ExcludeView="buckets"> + <Variable Name="n" InitialValue="d->numBuckets"/> + <Variable Name="bucket" InitialValue="d->buckets"/> + <Variable Name="node" InitialValue="d->buckets[0]"/> + <Variable Name="keyValuePair" InitialValue="reinterpret_cast<Node *>(0)"/> + <Size>d->size</Size> + <Loop> + <Break Condition="n == 0"/> + <Exec>node = *(bucket++)</Exec> + <Exec>--n</Exec> + <Loop> + <Break Condition="!node || !node->next"/> + <Exec>keyValuePair = reinterpret_cast<Node *>(node)</Exec> + <Item Name="[{keyValuePair->key}]">keyValuePair->value</Item> + <Exec>node = node->next</Exec> + </Loop> + </Loop> + </CustomListItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QHashNode<*,##NAMESPACE##::QHashDummyValue>"> + <DisplayString Condition="next == 0">(empty)</DisplayString> + <DisplayString Condition="next != 0">({key})</DisplayString> + <Expand> + <Item Name="[key]" Condition="next != 0">key</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QSet<*>"> + <DisplayString>{{ size = {q_hash.d->size} }}</DisplayString> + <Expand> + <ExpandedItem>q_hash</ExpandedItem> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QCache<*,*>::Node"> + <DisplayString>({*keyPtr}, {*t})</DisplayString> + <Expand> + <Item Name="[key]">*keyPtr</Item> + <Item Name="[value]">*t</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QCache<*,*>"> + <DisplayString>{{ size = {hash.d->size} }}</DisplayString> + <Expand> + <Item Name="[max coast]">mx</Item> + <Item Name="[total coast]">total</Item> + <Item Name="[referenced]">hash.d->ref.atomic._q_value</Item> + <LinkedListItems> + <Size>hash.d->size</Size> + <HeadPointer>f</HeadPointer> + <NextPointer>n</NextPointer> + <ValueNode>*((Node*)this)</ValueNode> + </LinkedListItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QStandardItemPrivate"> + <Intrinsic Name="memberOffset" Expression="sizeof(QStandardItemModel *) + + sizeof(QStandardItem *) + + sizeof(int *) + + sizeof(int *) + + (sizeof(int) * count)"> + <Parameter Name="count" Type="int"/> + </Intrinsic> + <Intrinsic Name="rows" Expression="*((int*)(((char*)(this)) + memberOffset(0)))" /> + <Intrinsic Name="columns" Expression="*((int*)(((char*)(this)) + memberOffset(1)))" /> + </Type> + + <Type Name="##NAMESPACE##::QStandardItem"> + <DisplayString>{{ row count = {(*d_ptr.d).rows()}, column count = {(*d_ptr.d).columns()} }}</DisplayString> + <Expand> + <Item Name="[d]">d_ptr.d,!</Item> + <Item Name="[row count]">(*d_ptr.d).rows()</Item> + <Item Name="[column count]">(*d_ptr.d).columns()</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QVariant"> + <!--Region DisplayString QVariant--> + + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::UnknownType">Invalid</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::Bool">{d.data.b}</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::Int">{d.data.i}</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::UInt">{d.data.u}</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::LongLong">{d.data.ll}</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::ULongLong">{d.data.ull}</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::Double">{d.data.d}</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QChar">{d.data.c}</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVariantMap"> + {*((##NAMESPACE##::QMap<##NAMESPACE##::QString,##NAMESPACE##::QVariant>*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVariantList"> + {*((##NAMESPACE##::QList<##NAMESPACE##::QVariant>*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QString"> + {*((##NAMESPACE##::QString*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QStringList"> + {*((##NAMESPACE##::QStringList*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QByteArray"> + {*((##NAMESPACE##::QByteArray*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QBitArray"> + {*((##NAMESPACE##::QBitArray*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QDate"> + {*((##NAMESPACE##::QDate*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTime"> + {*((##NAMESPACE##::QTime*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QDateTime">DateTime</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QUrl">Url</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QLocale">Locale</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRect"> + {*((##NAMESPACE##::QRect*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRectF"> + {*((##NAMESPACE##::QRectF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QSize"> + {*((##NAMESPACE##::QSize*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QSizeF"> + {*((##NAMESPACE##::QSizeF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QLine"> + {*((##NAMESPACE##::QLine*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QLineF"> + {*((##NAMESPACE##::QLineF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPoint"> + {*((##NAMESPACE##::QPoint*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPointF"> + {*((##NAMESPACE##::QPointF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRegExp">RegExp</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRegularExpression">RegularExpression</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVariantHash"> + {*((##NAMESPACE##::QHash<##NAMESPACE##::QString,##NAMESPACE##::QVariant>*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr)))} + </DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QEasingCurve">EasingCurve</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QUuid">Uuid</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QModelIndex">ModelIndex</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::LastCoreType">LastCoreType</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QFont">Font</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPixmap">Pixmap</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QBrush">Brush</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QColor">Color</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPalette">Palette</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QImage">Image</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPolygon">Polygon</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QRegion">Region</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QBitmap">Bitmap</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QCursor">Cursor</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QKeySequence">KeySequence</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPen">Pen</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTextLength">TextLength</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTextFormat">TextFormat</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QMatrix">Matrix</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QTransform">Transform</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QMatrix4x4">Matrix4x4</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVector2D">Vector2D</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVector3D">Vector3D</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QVector4D">Vector4D</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QQuaternion">Quaternion</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QPolygonF">PolygonF</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QIcon">Icon</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::LastGuiType">LastGuiType</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::QSizePolicy">SizePolicy</DisplayString> + <DisplayString Condition="d.type == ##NAMESPACE##::QMetaType::User">UserType</DisplayString> + <DisplayString Condition="d.type == 0xffffffff">LastType</DisplayString> + + <!--End region DisplayString QVariant--> + + <!--Region DisplayView QVariant--> + + <StringView Condition="d.type == ##NAMESPACE##::QMetaType::QChar">d.data.c</StringView> + + <StringView Condition="d.type == ##NAMESPACE##::QMetaType::QString"> + *((##NAMESPACE##::QString*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </StringView> + + <StringView Condition="d.type == ##NAMESPACE##::QMetaType::QByteArray"> + *((##NAMESPACE##::QByteArray*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </StringView> + + <!--End region DisplayView QVariant--> + + <!--Region Expand QVariant--> + + <Expand> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QVariantMap"> + *((##NAMESPACE##::QMap<##NAMESPACE##::QString,##NAMESPACE##::QVariant>*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QVariantList"> + *((##NAMESPACE##::QList<##NAMESPACE##::QVariant>*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QString"> + *((##NAMESPACE##::QString*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QStringList"> + *((##NAMESPACE##::QStringList*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QByteArray"> + *((##NAMESPACE##::QByteArray*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QBitArray"> + *((##NAMESPACE##::QBitArray*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QDate"> + *((##NAMESPACE##::QDate*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QTime"> + *((##NAMESPACE##::QTime*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QRect"> + *((##NAMESPACE##::QRect*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QRectF"> + *((##NAMESPACE##::QRectF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QSize"> + *((##NAMESPACE##::QSize*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QSizeF"> + *((##NAMESPACE##::QSizeF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QLine"> + *((##NAMESPACE##::QLine*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QLineF"> + *((##NAMESPACE##::QLineF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QPoint"> + *((##NAMESPACE##::QPoint*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QPointF"> + *((##NAMESPACE##::QPointF*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + <ExpandedItem Condition="d.type == ##NAMESPACE##::QMetaType::QVariantHash"> + *((##NAMESPACE##::QHash<##NAMESPACE##::QString,##NAMESPACE##::QVariant>*)(d.is_shared ? d.data.shared->ptr + : reinterpret_cast<const void *>(&d.data.ptr))) + </ExpandedItem> + </Expand> + + <!--End region Expand QVariant--> + </Type> + +</AutoVisualizer> diff --git a/QtVsTools.Package/qt6.natvis.xml b/QtVsTools.Package/qt6.natvis.xml new file mode 100644 index 0000000..5274053 --- /dev/null +++ b/QtVsTools.Package/qt6.natvis.xml @@ -0,0 +1,379 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + **************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + **************************************************************************** +--> + +<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> + + <Type Name="##NAMESPACE##::QSpecialInteger<*>"> + <DisplayString>{val}</DisplayString> + <Expand> + <Item Name="[value]">val</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QBasicAtomicInteger<*>"> + <DisplayString>{_q_value}</DisplayString> + <Expand> + <Item Name="[value]">_q_value</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QBasicAtomicPointer<*>"> + <Intrinsic Name="isNull" Expression="value()==0" /> + <Intrinsic Name="value" Expression="_q_value.value()" /> + <DisplayString Condition="isNull()">empty</DisplayString> + <DisplayString Condition="!isNull()">{_q_value}</DisplayString> + <Expand> + <Item Name=" " Condition="!isNull()">*value()</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPoint"> + <AlternativeType Name="##NAMESPACE##::QPointF"/> + <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QRect"> + <DisplayString>{{ x = {x1}, y = {y1}, width = {x2 - x1 + 1}, height = {y2 - y1 + 1} }}</DisplayString> + <Expand> + <Item Name="[x]">x1</Item> + <Item Name="[y]">y1</Item> + <Item Name="[width]">x2 - x1 + 1</Item> + <Item Name="[height]">y2 - y1 + 1</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QRectF"> + <DisplayString>{{ x = {xp}, y = {yp}, width = {w}, height = {h} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + <Item Name="[width]">w</Item> + <Item Name="[height]">h</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QSize"> + <AlternativeType Name="##NAMESPACE##::QSizeF"/> + <DisplayString>{{ width = {wd}, height = {ht} }}</DisplayString> + <Expand> + <Item Name="[width]">wd</Item> + <Item Name="[height]">ht</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QLine"> + <AlternativeType Name="##NAMESPACE##::QLineF"/> + <DisplayString>{{ start point = {pt1}, end point = {pt2} }}</DisplayString> + <Expand> + <Synthetic Name="[start point]"> + <DisplayString>{pt1}</DisplayString> + <Expand> + <ExpandedItem>pt1</ExpandedItem> + </Expand> + </Synthetic> + <Synthetic Name="[end point]"> + <DisplayString>{pt2}</DisplayString> + <Expand> + <ExpandedItem>pt2</ExpandedItem> + </Expand> + </Synthetic> + + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPolygon"> + <DisplayString>{{ size={d->size} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <ArrayItems> + <Size>d->size</Size> + <ValuePointer>(QPoint*)((reinterpret_cast<char*>(d)) + d->offset)</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPolygonF"> + <DisplayString>{{ size={d->size} }}</DisplayString> + <Expand> + <Item Name="[closed]"> + d->size > 0 + && ((((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[0]).xp + == (((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[d->size - 1]).xp) + && ((((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[0]).yp + == (((QPointF*)((reinterpret_cast<char*>(d)) + d->offset)[d->size - 1]).yp) + </Item> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + <ArrayItems> + <Size>d->size</Size> + <ValuePointer>(QPointF*)((reinterpret_cast<char*>(d)) + d->offset)</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QVector2D"> + <DisplayString>{{ x = {xp}, y = {yp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QVector3D"> + <DisplayString>{{ x = {xp}, y = {yp}, z = {zp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + <Item Name="[z]">zp</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QVector4D"> + <DisplayString>{{ x = {xp}, y = {yp}, z = {zp}, w = {wp} }}</DisplayString> + <Expand> + <Item Name="[x]">xp</Item> + <Item Name="[y]">yp</Item> + <Item Name="[z]">zp</Item> + <Item Name="[w]">wp</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QMatrix"> + <DisplayString> + {{ m11 = {_m11}, m12 = {_m12}, m21 = {_m21}, m22 = {_m22}, ... }} + </DisplayString> + <Expand> + <Item Name="[m11]">_m11</Item> + <Item Name="[m12]">_m12</Item> + <Item Name="[m21]">_m21</Item> + <Item Name="[m22]">_m22</Item> + <Item Name="[dx]">_dx</Item> + <Item Name="[dy]">_dy</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QMatrix4x4"> + <DisplayString> + {{ m11 = {m[0][0]}, m12 = {m[1][0]}, m13 = {m[2][0]}, m14 = {m[3][0]}, ... }} + </DisplayString> + <Expand> + <Item Name="[m11]">m[0][0]</Item> + <Item Name="[m12]">m[1][0]</Item> + <Item Name="[m13]">m[2][0]</Item> + <Item Name="[m14]">m[3][0]</Item> + <Item Name="[m21]">m[0][1]</Item> + <Item Name="[m22]">m[1][1]</Item> + <Item Name="[m23]">m[2][1]</Item> + <Item Name="[m24]">m[3][1]</Item> + <Item Name="[m31]">m[0][2]</Item> + <Item Name="[m32]">m[1][2]</Item> + <Item Name="[m33]">m[2][2]</Item> + <Item Name="[m34]">m[3][2]</Item> + <Item Name="[m41]">m[0][3]</Item> + <Item Name="[m42]">m[1][3]</Item> + <Item Name="[m43]">m[2][3]</Item> + <Item Name="[m44]">m[3][3]</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QSizePolicy"> + <DisplayString> + {{ horizontal = {static_cast<Policy>(bits.horPolicy)}, vertical = {static_cast<Policy>(bits.verPolicy)}, type = {ControlType(1 << bits.ctype)} }} + </DisplayString> + <Expand> + <Synthetic Name="[vertical policy]"> + <DisplayString>QSizePolicy::Policy::{static_cast<Policy>(bits.verPolicy)}</DisplayString> + </Synthetic> + <Synthetic Name="[horizontal policy]"> + <DisplayString>QSizePolicy::Policy::{static_cast<Policy>(bits.horPolicy)}</DisplayString> + </Synthetic> + <Synthetic Name="[control type]"> + <DisplayString>QSizePolicy::ControlType::{ControlType(1 << bits.ctype)}</DisplayString> + </Synthetic> + <Synthetic Name="[expanding directions]"> + <DisplayString + Condition="(static_cast<Policy>(bits.verPolicy) & ExpandFlag)"> + Qt::Vertical (2) + </DisplayString> + <DisplayString + Condition="(static_cast<Policy>(bits.horPolicy) & ExpandFlag)"> + Qt::Horizontal (1) + </DisplayString> + </Synthetic> + <Item Name="[vertical stretch]">static_cast<int>(bits.verStretch)</Item> + <Item Name="[horizontal stretch]">static_cast<int>(bits.horStretch)</Item> + <Item Name="[has height for width]">bits.hfw == 1</Item> + <Item Name="[has width for height]">bits.wfh == 1</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QChar"> + <DisplayString>{ucs,c}</DisplayString> + <StringView>ucs,c</StringView> + <Expand> + <Item Name="[latin 1]">ucs > 0xff ? '\0' : char(ucs),c</Item> + <Item Name="[unicode]">ucs,c</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QString"> + <DisplayString>"{(reinterpret_cast<unsigned short*>(d.ptr)),sub}"</DisplayString> + <StringView>(reinterpret_cast<unsigned short*>(d.ptr)),sub</StringView> + <Expand> + <Item Name="[size]">d.size</Item> + <ArrayItems> + <Size>d.size</Size> + <ValuePointer>d.ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QByteArray"> + <DisplayString>"{((reinterpret_cast<char*>(d.ptr))),sb}"</DisplayString> + <StringView>((reinterpret_cast<char*>(d.ptr))),sb</StringView> + <Expand> + <Item Name="[size]">d.size</Item> + <ArrayItems> + <Size>d.size</Size> + <ValuePointer>d.ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QUrl"> + <Intrinsic Name="isEmpty" Expression="size==0"> + <Parameter Name="size" Type="int"/> + </Intrinsic> + <Intrinsic Name="memberOffset" Expression="sizeof(QAtomicInt) + sizeof(int) + (sizeof(QString) * count)"> + <Parameter Name="count" Type="int"/> + </Intrinsic> + <Intrinsic Name="scheme" Expression="*((QString*)(((char*)(d) + memberOffset(0))))" /> + <Intrinsic Name="username" Expression="*((QString*)(((char*)(d) + memberOffset(1))))" /> + <Intrinsic Name="password" Expression="*((QString*)(((char*)(d) + memberOffset(2))))" /> + <Intrinsic Name="host" Expression="*((QString*)(((char*)(d) + memberOffset(3))))" /> + <Intrinsic Name="path" Expression="*((QString*)(((char*)(d) + memberOffset(4))))" /> + <Intrinsic Name="query" Expression="*((QString*)(((char*)(d) + memberOffset(5))))" /> + <Intrinsic Name="fragment" Expression="*((QString*)(((char*)(d) + memberOffset(6))))" /> + + <DisplayString Condition="!isEmpty(scheme().d->size)">{scheme()}://{host()}{path()}</DisplayString> + <DisplayString Condition="isEmpty(scheme().d->size)">{path()}</DisplayString> + <Expand> + <Item Name="[scheme]">scheme()</Item> + <Item Name="[username]">username()</Item> + <Item Name="[password]">password()</Item> + <Item Name="[host]">host()</Item> + <Item Name="[path]">path()</Item> + <Item Name="[query]">query()</Item> + <Item Name="[fragment]">fragment()</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QDate"> + <DisplayString>{{ julian day = {jd} }}</DisplayString> + </Type> + + <Type Name="##NAMESPACE##::QTime"> + <Intrinsic Name="hour" Expression="mds / 3600000" /> + <Intrinsic Name="minute" Expression="(mds % 3600000) / 60000" /> + <Intrinsic Name="second" Expression="(mds / 1000) % 60" /> + <Intrinsic Name="millisecond" Expression="mds % 1000" /> + <DisplayString Condition="mds == 1">{{ millisecond = {mds} }}</DisplayString> + <DisplayString Condition="mds != 1">{{ milliseconds = {mds} }}</DisplayString> + <Expand> + <Item Name="[hour]" + Condition="(mds / 3600000) == 1">hour(), d</Item> + <Item Name="[hours]" + Condition="(mds / 3600000) != 1">hour(), d</Item> + <Item Name="[minute]" + Condition="((mds % 3600000) / 60000) == 1">minute(), d</Item> + <Item Name="[minutes]" + Condition="((mds % 3600000) / 60000) != 1">minute(), d</Item> + <Item Name="[second]" + Condition="((mds / 1000) % 60) == 1">second(), d</Item> + <Item Name="[seconds]" + Condition="((mds / 1000) % 60) != 1">second(), d</Item> + <Item Name="[millisecond]" + Condition="(mds % 1000) == 1">millisecond(), d</Item> + <Item Name="[milliseconds]" + Condition="(mds % 1000) != 1">millisecond(), d</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QPair<*,*>"> + <DisplayString>({first}, {second})</DisplayString> + <Expand> + <Item Name="[first]">first</Item> + <Item Name="[second]">second</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QList<*>"> + <AlternativeType Name="##NAMESPACE##::QVector<*>"/> + <DisplayString>{{ size={d.size} }}</DisplayString> + <Expand> + <ArrayItems> + <Size>d.size</Size> + <ValuePointer>reinterpret_cast<$T1*>(d.ptr)</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QVarLengthArray<*>"> + <DisplayString>{{ size={s} }}</DisplayString> + <Expand> + <Item Name="[capacity]">a</Item> + <ArrayItems> + <Size>s</Size> + <ValuePointer>ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QMap<*,*>"> + <AlternativeType Name="##NAMESPACE##::QMultiMap<*,*>"/> + <DisplayString>{{ size={d.d->m._Mypair._Myval2._Myval2._Mysize} }}</DisplayString> + <Expand> + <Item Name="[std::map]">d.d->m</Item> + </Expand> + </Type> + + <Type Name="##NAMESPACE##::QHash<*,*>"> + <AlternativeType Name="##NAMESPACE##::QMultiHash<*,*>"/> + <DisplayString>{{ size = {d->size} }}</DisplayString> + <Expand> + <Item Name="[referenced]">d->ref.atomic._q_value</Item> + </Expand> + </Type> + +</AutoVisualizer> diff --git a/QtVsTools.Package/qtmodules.xml b/QtVsTools.Package/qtmodules.xml new file mode 100644 index 0000000..1a56520 --- /dev/null +++ b/QtVsTools.Package/qtmodules.xml @@ -0,0 +1,600 @@ +<?xml version="1.0" encoding="utf-8"?> +<QtVsTools> + <Module Id="1"> + <Name>Core</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtCore</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>core</proVarQT> + <IncludePath>$(QTDIR)\include\QtCore</IncludePath> + <Defines>QT_CORE_LIB</Defines> + </Module> + <Module Id="2"> + <Name>Xml</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtXml</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>xml</proVarQT> + <IncludePath>$(QTDIR)\include\QtXml</IncludePath> + <Defines>QT_XML_LIB</Defines> + </Module> + <Module Id="3"> + <Name>Sql</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtSql</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>sql</proVarQT> + <IncludePath>$(QTDIR)\include\QtSql</IncludePath> + <Defines>QT_SQL_LIB</Defines> + </Module> + <Module Id="4"> + <Name>OpenGL</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtOpenGL</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>opengl</proVarQT> + <IncludePath>$(QTDIR)\include\QtOpenGL</IncludePath> + <Defines>QT_OPENGL_LIB</Defines> + </Module> + <Module Id="5"> + <Name>Network</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtNetwork</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>network</proVarQT> + <IncludePath>$(QTDIR)\include\QtNetwork</IncludePath> + <Defines>QT_NETWORK_LIB</Defines> + </Module> + <Module Id="7"> + <Name>Gui</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtGui</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>gui</proVarQT> + <IncludePath>$(QTDIR)\include\QtGui</IncludePath> + <IncludePath>$(QTDIR)\include\QtANGLE</IncludePath> + <Defines>QT_GUI_LIB</Defines> + </Module> + <Module Id="8"> + <Name>ActiveQtS</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtAxServer</LibraryPrefix> + <HasDLL>false</HasDLL> + <proVarQT>axserver</proVarQT> + <IncludePath>$(QTDIR)\include\ActiveQt</IncludePath> + <Defines>QAXSERVER</Defines> + <AdditionalLibraries>Qt5AxBase.lib</AdditionalLibraries> + <AdditionalLibrariesDebug>Qt5AxBased.lib</AdditionalLibrariesDebug> + </Module> + <Module Id="9"> + <Name>ActiveQtC</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtAxContainer</LibraryPrefix> + <HasDLL>false</HasDLL> + <proVarQT>axcontainer</proVarQT> + <IncludePath>$(QTDIR)\include\ActiveQt</IncludePath> + <AdditionalLibraries>Qt5AxBase.lib</AdditionalLibraries> + <AdditionalLibrariesDebug>Qt5AxBased.lib</AdditionalLibrariesDebug> + </Module> + <Module Id="10"> + <Name>Main</Name> + <Selectable>false</Selectable> + <LibraryPrefix>qtmain</LibraryPrefix> + <HasDLL>false</HasDLL> + </Module> + <Module Id="13"> + <Name>Svg</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtSvg</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>svg</proVarQT> + <IncludePath>$(QTDIR)\include\QtSvg</IncludePath> + <Defines>QT_SVG_LIB</Defines> + </Module> + <Module Id="14"> + <Name>Designer</Name> + <Selectable>false</Selectable> + <LibraryPrefix>QtDesigner</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>designer</proVarQT> + <IncludePath>$(QTDIR)\include\QtDesigner</IncludePath> + <Defines>QDESIGNER_EXPORT_WIDGETS</Defines> + <Defines>QT_DESIGNER_LIB</Defines> + </Module> + <Module Id="15"> + <Name>Test</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtTest</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>testlib</proVarQT> + <IncludePath>$(QTDIR)\include\QtTest</IncludePath> + <Defines>QT_TESTLIB_LIB</Defines> + </Module> + <Module Id="16"> + <Name>Script</Name> + <Selectable>false</Selectable> + <LibraryPrefix>QtScript</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>script</proVarQT> + <IncludePath>$(QTDIR)\include\QtScript</IncludePath> + <Defines>QT_SCRIPT_LIB</Defines> + </Module> + <Module Id="17"> + <Name>Help</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtHelp</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>help</proVarQT> + <IncludePath>$(QTDIR)\include\QtHelp</IncludePath> + <Defines>QT_HELP_LIB</Defines> + </Module> + <Module Id="18"> + <Name>WebKit</Name> + <Selectable>false</Selectable> + <LibraryPrefix>QtWebKit</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>webkit</proVarQT> + <IncludePath>$(QTDIR)\include\QtWebKit</IncludePath> + </Module> + <Module Id="19"> + <Name>XmlPatterns</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtXmlPatterns</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>xmlpatterns</proVarQT> + <IncludePath>$(QTDIR)\include\QtXmlPatterns</IncludePath> + <Defines>QT_XMLPATTERNS_LIB</Defines> + </Module> + <Module Id="20"> + <Name>Enginio</Name> + <Selectable>false</Selectable> + <LibraryPrefix>Enginio</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>enginio</proVarQT> + <IncludePath>$(QTDIR)\include\Enginio</IncludePath> + <Defines>QT_ENGINIO_LIB</Defines> + </Module> + <Module Id="21"> + <Name>Multimedia</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtMultimedia</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>multimedia</proVarQT> + <IncludePath>$(QTDIR)\include\QtMultimedia</IncludePath> + <Defines>QT_MULTIMEDIA_LIB</Defines> + </Module> + <Module Id="23"> + <Name>ScriptTools</Name> + <Selectable>false</Selectable> + <LibraryPrefix>QtScriptTools</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>scripttools</proVarQT> + <IncludePath>$(QTDIR)\include\QtScriptTools</IncludePath> + <Defines>QT_SCRIPTTOOLS_LIB</Defines> + </Module> + <Module Id="24"> + <Name>UiTools</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtUiTools</LibraryPrefix> + <HasDLL>false</HasDLL> + <proVarQT>uitools</proVarQT> + <IncludePath>$(QTDIR)\include\QtUiTools</IncludePath> + <Defines>QT_UITOOLS_LIB</Defines> + </Module> + <Module Id="25"> + <Name>Widgets</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtWidgets</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>widgets</proVarQT> + <IncludePath>$(QTDIR)\include\QtWidgets</IncludePath> + <Defines>QT_WIDGETS_LIB</Defines> + </Module> + <Module Id="27"> + <Name>Location</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtLocation</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>location</proVarQT> + <IncludePath>$(QTDIR)\include\QtLocation</IncludePath> + <Defines>QT_LOCATION_LIB</Defines> + </Module> + <Module Id="28"> + <Name>Nfc</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtNfc</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>nfc</proVarQT> + <IncludePath>$(QTDIR)\include\QtNfc</IncludePath> + <Defines>QT_NFC_LIB</Defines> + </Module> + <Module Id="29"> + <Name>Qml</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtQml</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>qml</proVarQT> + <IncludePath>$(QTDIR)\include\QtQml</IncludePath> + <Defines>QT_QML_LIB</Defines> + </Module> + <Module Id="30"> + <Name>Bluetooth</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtBluetooth</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>bluetooth</proVarQT> + <IncludePath>$(QTDIR)\include\QtBluetooth</IncludePath> + <Defines>QT_BLUETOOTH_LIB</Defines> + </Module> + <Module Id="31"> + <Name>Positioning</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtPositioning</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>positioning</proVarQT> + <IncludePath>$(QTDIR)\include\QtPositioning</IncludePath> + <Defines>QT_POSITIONING_LIB</Defines> + </Module> + <Module Id="32"> + <Name>SerialPort</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtSerialPort</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>serialport</proVarQT> + <IncludePath>$(QTDIR)\include\QtSerialPort</IncludePath> + <Defines>QT_SERIALPORT_LIB</Defines> + </Module> + <Module Id="33"> + <Name>PrintSupport</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtPrintSupport</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>printsupport</proVarQT> + <IncludePath>$(QTDIR)\include\QtPrintSupport</IncludePath> + <Defines>QT_PRINTSUPPORT_LIB</Defines> + </Module> + <Module Id="34"> + <Name>WebChannel</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtWebChannel</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>webchannel</proVarQT> + <IncludePath>$(QTDIR)\include\QtWebChannel</IncludePath> + <Defines>QT_WEBCHANNEL_LIB</Defines> + </Module> + <Module Id="35"> + <Name>WebSockets</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtWebSockets</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>websockets</proVarQT> + <IncludePath>$(QTDIR)\include\QtWebSockets</IncludePath> + <Defines>QT_WEBSOCKETS_LIB</Defines> + </Module> + <Module Id="36"> + <Name>Sensors</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtSensors</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>sensors</proVarQT> + <IncludePath>$(QTDIR)\include\QtSensors</IncludePath> + <Defines>QT_SENSORS_LIB</Defines> + </Module> + <Module Id="37"> + <Name>WindowsExtras</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtWinExtras</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>winextras</proVarQT> + <IncludePath>$(QTDIR)\include\QtWinExtras</IncludePath> + <Defines>QT_WINEXTRAS_LIB</Defines> + </Module> + <Module Id="38"> + <Name>QuickWidgets</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtQuickWidgets</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>quickwidgets</proVarQT> + <IncludePath>$(QTDIR)\include\QtQuickWidgets</IncludePath> + <Defines>QT_QUICKWIDGETS_LIB</Defines> + </Module> + <Module Id="40"> + <Name>Quick</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtQuick</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>quick</proVarQT> + <IncludePath>$(QTDIR)\include\QtQuick</IncludePath> + <Defines>QT_QUICK_LIB</Defines> + </Module> + <Module Id="50"> + <Name>WebkitWidgets</Name> + <Selectable>false</Selectable> + <LibraryPrefix>QtWebkitWidgets</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>webkitwidgets</proVarQT> + <IncludePath>$(QTDIR)\include\QtWebkitWidgets</IncludePath> + <Defines>QT_WEBKITWIDGETS_LIB</Defines> + </Module> + <Module Id="51"> + <Name>Concurrent</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtConcurrent</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>concurrent</proVarQT> + <IncludePath>$(QTDIR)\include\QtConcurrent</IncludePath> + <Defines>QT_CONCURRENT_LIB</Defines> + </Module> + <Module Id="52"> + <Name>MultimediaWidgets</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtMultimediaWidgets</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>multimediawidgets</proVarQT> + <IncludePath>$(QTDIR)\include\QtMultimediaWidgets</IncludePath> + <Defines>QT_MULTIMEDIAWIDGETS_LIB</Defines> + </Module> + <Module Id="101"> + <Name>3D</Name> + <Selectable>true</Selectable> + <LibraryPrefix>Qt3DCore</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>3dcore 3danimation 3dextras 3dinput 3dlogic 3drender</proVarQT> + <IncludePath>$(QTDIR)\include\Qt3DCore</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DAnimation</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DExtras</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DInput</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DLogic</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DRender</IncludePath> + <Defines>QT_3DCORE_LIB</Defines> + <Defines>QT_3DANIMATION_LIB</Defines> + <Defines>QT_3DEXTRAS_LIB</Defines> + <Defines>QT_3DINPUT_LIB</Defines> + <Defines>QT_3DLOGIC_LIB</Defines> + <Defines>QT_3DRENDER_LIB</Defines> + <AdditionalLibraries>Qt53DCore.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DAnimation.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DExtras.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DInput.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DLogic.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DRender.lib</AdditionalLibraries> + <AdditionalLibrariesDebug>Qt53DCored.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DAnimationd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DExtrasd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DInputd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DLogicd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DRenderd.lib</AdditionalLibrariesDebug> + </Module> + <Module Id="102"> + <Name>3D Quick</Name> + <Selectable>true</Selectable> + <LibraryPrefix>Qt3DQuick</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>3dquick</proVarQT> + <IncludePath>$(QTDIR)\include\Qt3DQuick</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DQuickAnimation</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DQuickExtras</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DQuickInput</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DQuickRender</IncludePath> + <IncludePath>$(QTDIR)\include\Qt3DQuickScene2D</IncludePath> + <Defines>QT_3DQUICK_LIB</Defines> + <Defines>QT_3DQUICKANIMATION_LIB</Defines> + <Defines>QT_3DQUICKEXTRAS_LIB</Defines> + <Defines>QT_3DQUICKINPUT_LIB</Defines> + <Defines>QT_3DQUICKRENDER_LIB</Defines> + <Defines>QT_3DQUICKSCENE2D_LIB</Defines> + <AdditionalLibraries>Qt53DQuick.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DQuickAnimation.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DQuickExtras.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DQuickInput.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DQuickRender.lib</AdditionalLibraries> + <AdditionalLibraries>Qt53DQuickScene2D.lib</AdditionalLibraries> + <AdditionalLibrariesDebug>Qt53DQuickd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DQuickAnimationd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DQuickExtrasd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DQuickInputd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DQuickRenderd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt53DQuickScene2Dd.lib</AdditionalLibrariesDebug> + </Module> + <Module Id="103"> + <Name>DBus</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtDBus</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>dbus</proVarQT> + <IncludePath>$(QTDIR)\include\QtDBus</IncludePath> + <Defines>QT_DBUS_LIB</Defines> + </Module> + <Module Id="104"> + <Name>Gamepad</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtGamepad</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>gamepad</proVarQT> + <IncludePath>$(QTDIR)\include\QtGamepad</IncludePath> + <Defines>QT_GAMEPAD_LIB</Defines> + </Module> + <Module Id="105"> + <Name>OpenGL Extensions</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtOpenGLExtensions</LibraryPrefix> + <HasDLL>false</HasDLL> + <proVarQT>openglextensions</proVarQT> + <IncludePath>$(QTDIR)\include\QtOpenGLExtensions</IncludePath> + <Defines>QT_OPENGLEXTENSIONS_LIB</Defines> + </Module> + <Module Id="106"> + <Name>QuickTest</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtQuickTest</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>qmltest</proVarQT> + <IncludePath>$(QTDIR)\include\QtQuickTest</IncludePath> + <Defines>QT_QMLTEST_LIB</Defines> + </Module> + <Module Id="107"> + <Name>Quick Controls2</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtQuickControls2</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>quickcontrols2</proVarQT> + <IncludePath>$(QTDIR)\include\QtQuickControls2</IncludePath> + <Defines>QT_QUICKCONTROLS2_LIB</Defines> + </Module> + <Module Id="108"> + <Name>Scxml</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtScxml</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>scxml</proVarQT> + <IncludePath>$(QTDIR)\include\QtScxml</IncludePath> + <Defines>QT_SCXML_LIB</Defines> + </Module> + <Module Id="109"> + <Name>Serial Bus</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtSerialBus</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>serialbus</proVarQT> + <IncludePath>$(QTDIR)\include\QtSerialBus</IncludePath> + <Defines>QT_SERIALBUS_LIB</Defines> + </Module> + <Module Id="110"> + <Name>UI Plugin</Name> + <Selectable>false</Selectable> + <LibraryPrefix>QtUiPlugin</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>uiplugin</proVarQT> + <IncludePath>$(QTDIR)\include\QtUiPlugin</IncludePath> + <Defines>QT_UIPLUGIN_LIB</Defines> + </Module> + <Module Id="111"> + <Name>WebEngine</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtWebEngine</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>webengine</proVarQT> + <IncludePath>$(QTDIR)\include\QtWebEngine</IncludePath> + <IncludePath>$(QTDIR)\include\QtWebEngineCore</IncludePath> + <Defines>QT_WEBENGINE_LIB</Defines> + <Defines>QT_WEBENGINECORE_LIB</Defines> + <AdditionalLibraries>Qt5WebEngine.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5WebEngineCore.lib</AdditionalLibraries> + <AdditionalLibrariesDebug>Qt5WebEngined.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt5WebEngineCored.lib</AdditionalLibrariesDebug> + </Module> + <Module Id="113"> + <Name>WebEngine Widgets</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtWebEngineWidgets</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>webenginewidgets</proVarQT> + <IncludePath>$(QTDIR)\include\QtWebEngineWidgets</IncludePath> + <IncludePath>$(QTDIR)\include\QtWebEngineCore</IncludePath> + <Defines>QT_WEBENGINEWIDGETS_LIB</Defines> + <Defines>QT_WEBENGINECORE_LIB</Defines> + <AdditionalLibraries>Qt5WebEngineWidgets.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5WebEngineCore.lib</AdditionalLibraries> + <AdditionalLibrariesDebug>Qt5WebEngineWidgetsd.lib</AdditionalLibrariesDebug> + <AdditionalLibrariesDebug>Qt5WebEngineCored.lib</AdditionalLibrariesDebug> + </Module> + <Module Id="114"> + <Name>Charts</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtCharts</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>charts</proVarQT> + <IncludePath>$(QTDIR)\include\QtCharts</IncludePath> + <Defines>QT_CHARTS_LIB</Defines> + </Module> + <Module Id="115"> + <Name>Data Visualization</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtDataVisualization</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>datavisualization</proVarQT> + <IncludePath>$(QTDIR)\include\QtDataVisualization</IncludePath> + <Defines>QT_DATAVISUALIZATION_LIB</Defines> + </Module> + <Module Id="116"> + <Name>Network Authorization</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtNetworkAuth</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>networkauth</proVarQT> + <IncludePath>$(QTDIR)\include\QtNetworkAuth</IncludePath> + <Defines>QT_NETWORKAUTH_LIB</Defines> + </Module> + <Module Id="117"> + <Name>Speech</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtTextToSpeech</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>texttospeech</proVarQT> + <IncludePath>$(QTDIR)\include\QtTextToSpeech</IncludePath> + <Defines>QT_TEXTTOSPEECH_LIB</Defines> + </Module> + <Module Id="118"> + <Name>Remote Objects</Name> + <Selectable>true</Selectable> + <LibraryPrefix>QtRemoteObjects</LibraryPrefix> + <HasDLL>true</HasDLL> + <proVarQT>remoteobjects</proVarQT> + <IncludePath>$(QTDIR)\include\QtRemoteObjects</IncludePath> + <Defines>QT_REMOTEOBJECTS_LIB</Defines> + </Module> + <Module Id="119"> + <Name>WebView</Name> + <proVarQT>webview</proVarQT> + <Selectable>true</Selectable> + <LibraryPrefix>QtWebView</LibraryPrefix> + </Module> + <Module Id="120"> + <Name>Purchasing</Name> + <proVarQT>purchasing</proVarQT> + <Selectable>true</Selectable> + <LibraryPrefix>QtPurchasing</LibraryPrefix> + </Module> + <Module Id="121"> + <Name>Virtual Keyboard</Name> + <proVarQT>virtualkeyboard</proVarQT> + <Selectable>true</Selectable> + <LibraryPrefix>QtVirtualKeyboard</LibraryPrefix> + </Module> + <Module Id="122"> + <Name>X11 Extras</Name> + <proVarQT>x11extras</proVarQT> + <Selectable>true</Selectable> + <LibraryPrefix>QtX11Extras</LibraryPrefix> + </Module> + <Module Id="123"> + <Name>Wayland Compositor</Name> + <proVarQT>waylandcompositor</proVarQT> + <Selectable>true</Selectable> + <LibraryPrefix>QtWaylandCompositor</LibraryPrefix> + </Module> + <Module Id="124"> + <Name>Qt Automotive Suite</Name> + <proVarQT>ivicore geniviextras</proVarQT> + <Selectable>true</Selectable> + <LibraryPrefix>QtIviCore</LibraryPrefix> + <AdditionalLibraries>Qt5IviCore.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5IviCored.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5GeniviExtras.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5GeniviExtrasd.lib</AdditionalLibraries> + </Module> + <Module Id="125"> + <Name>Qt for Automation</Name> + <proVarQT>coap knx mqtt opcua</proVarQT> + <Selectable>true</Selectable> + <LibraryPrefix>QtCoap</LibraryPrefix> + <AdditionalLibraries>Qt5Coap.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5Coapd.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5Knx.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5Knxd.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5Mqtt.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5Mqttd.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5OpcUa.lib</AdditionalLibraries> + <AdditionalLibraries>Qt5OpcUad.lib</AdditionalLibraries> + </Module> +</QtVsTools> diff --git a/QtVsTools.Package/source.extension.vsixmanifest_TT b/QtVsTools.Package/source.extension.vsixmanifest_TT new file mode 100644 index 0000000..7321b70 --- /dev/null +++ b/QtVsTools.Package/source.extension.vsixmanifest_TT @@ -0,0 +1,200 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vsixmanifest" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +<# + string PackageId = ""; + string[] VsProducts = new string[0]; + string VersionRange = ""; + switch (VS_NAME) { + case "2022": + PackageId = "QtVsTools.8e827d74-6fc4-40a6-a3aa-faf19652b3b8"; + VsProducts = new[] + { + "Microsoft.VisualStudio.Pro", + "Microsoft.VisualStudio.Premium", + "Microsoft.VisualStudio.Ultimate", + "Microsoft.VisualStudio.Community", + "Microsoft.VisualStudio.Enterprise" + }; + VersionRange = "[17.0, 18.0)"; + break; + case "2019": + PackageId = "QtVsTools.bf3c71c0-ab41-4427-ada9-9b3813d89ff5"; + VsProducts = new[] + { + "Microsoft.VisualStudio.Pro", + "Microsoft.VisualStudio.Premium", + "Microsoft.VisualStudio.Ultimate", + "Microsoft.VisualStudio.Community", + "Microsoft.VisualStudio.Enterprise" + }; + VersionRange = "[16.0, 17.0)"; + break; + case "2017": + PackageId = "QtVsTools.13121978-cd02-4fd0-89bd-e36f85abe16a"; + VsProducts = new[] + { + "Microsoft.VisualStudio.Pro", + "Microsoft.VisualStudio.Premium", + "Microsoft.VisualStudio.Ultimate", + "Microsoft.VisualStudio.Community", + "Microsoft.VisualStudio.Enterprise" + }; + VersionRange = "[15.0.28010.0,16.0)"; + break; + } +#> +--> + +<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" + xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011"> + <Metadata> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Identity + Id="<#=PackageId#>" + Version="<#=QT_VS_TOOLS_VERSION_MANIFEST#>" + Language="en-US" + Publisher="The Qt Company Ltd." /> + <DisplayName>Qt Visual Studio Tools</DisplayName> + <Description xml:space="preserve">The Qt VS Tools for Visual Studio <#=VS_NAME#> allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.</Description> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <MoreInfo>http://www.qt.io</MoreInfo> + <License>LICENSE.GPL3-EXCEPT</License> + <ReleaseNotes>Changelog</ReleaseNotes> + <Icon>qt.ico</Icon> + <PreviewImage>preview.png</PreviewImage> + </Metadata> + <Installation> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <#foreach (var VsProduct in VsProducts) {#> + <InstallationTarget Id="<#=VsProduct#>" Version="<#=VersionRange#>"> + <#if (VS_NAME == "2022") {#> + <ProductArchitecture>amd64</ProductArchitecture> + <#}#> + </InstallationTarget> + <#}#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Installation> + <Dependencies> + <Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" /> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <#if (VS_NAME == "2022") {#> + <Dependency Id="Microsoft.VisualStudio.MPF.16.0" DisplayName="Visual Studio MPF 16.0" d:Source="Installed" Version="[16.0,17.0)" /> + <#}#> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Dependencies> + <Prerequisites> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" + Version="<#=VersionRange#>" + DisplayName="Visual Studio core editor" /> + <Prerequisite Id="Microsoft.VisualStudio.Component.VC.CoreIde" + Version="<#=VersionRange#>" + DisplayName="Visual Studio C++ core features" /> + <Prerequisite Id="Microsoft.VisualStudio.Component.VC.Tools.x86.x64" + Version="<#=VersionRange#>" + DisplayName="MSVC C++ x64/x86 build tools" /> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + </Prerequisites> + <Assets> + <Asset + Type="Microsoft.VisualStudio.VsPackage" d:Source="Project" d:ProjectName="%CurrentProject%" + Path="|%CurrentProject%;PkgdefProjectOutputGroup|" /> + <Asset + Type="Microsoft.VisualStudio.MefComponent" d:Source="Project" d:ProjectName="%CurrentProject%" + Path="|%CurrentProject%|" /> + <Asset + Type="Microsoft.VisualStudio.Assembly" d:Source="Project" d:ProjectName="QtVsTools.Wizards" + Path="|QtVsTools.Wizards|" AssemblyName="|QtVsTools.Wizards;AssemblyName|" /> + <Asset + Type="Microsoft.VisualStudio.ProjectTemplate" d:Source="Project" d:ProjectName="Console" + d:TargetPath="|QtTemplate.Project.Console;TemplateProjectOutputGroup|" Path="ProjectTemplates" + d:VsixSubPath="ProjectTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ProjectTemplate" d:Source="Project" d:ProjectName="Gui" + d:TargetPath="|QtTemplate.Project.Gui;TemplateProjectOutputGroup|" Path="ProjectTemplates" + d:VsixSubPath="ProjectTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ProjectTemplate" d:Source="Project" d:ProjectName="Empty" + d:TargetPath="|QtTemplate.Project.Empty;TemplateProjectOutputGroup|" Path="ProjectTemplates" + d:VsixSubPath="ProjectTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ProjectTemplate" d:Source="Project" d:ProjectName="Quick" + d:TargetPath="|QtTemplate.Project.Quick;TemplateProjectOutputGroup|" Path="ProjectTemplates" + d:VsixSubPath="ProjectTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ProjectTemplate" d:Source="Project" d:ProjectName="Lib" + d:TargetPath="|QtTemplate.Project.Lib;TemplateProjectOutputGroup|" Path="ProjectTemplates" + d:VsixSubPath="ProjectTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ProjectTemplate" d:Source="Project" d:ProjectName="Server" + d:TargetPath="|QtTemplate.Project.Server;TemplateProjectOutputGroup|" Path="ProjectTemplates" + d:VsixSubPath="ProjectTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ProjectTemplate" d:Source="Project" d:ProjectName="Designer" + d:TargetPath="|QtTemplate.Project.Designer;TemplateProjectOutputGroup|" Path="ProjectTemplates" + d:VsixSubPath="ProjectTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ItemTemplate" d:Source="Project" d:ProjectName="Resource" + d:TargetPath="|QtTemplate.Item.Resource;TemplateProjectOutputGroup|" Path="ItemTemplates" + d:VsixSubPath="ItemTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ItemTemplate" d:Source="Project" d:ProjectName="QMLFile" + d:TargetPath="|QtTemplate.Item.QMLFile;TemplateProjectOutputGroup|" Path="ItemTemplates" + d:VsixSubPath="ItemTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ItemTemplate" d:Source="Project" d:ProjectName="QMLDir" + d:TargetPath="|QtTemplate.Item.QMLDir;TemplateProjectOutputGroup|" Path="ItemTemplates" + d:VsixSubPath="ItemTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ItemTemplate" d:Source="Project" + d:ProjectName="DialogButtonBottom" + d:TargetPath="|QtTemplate.Item.DialogButtonBottom;TemplateProjectOutputGroup|" Path="ItemTemplates" + d:VsixSubPath="ItemTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ItemTemplate" d:Source="Project" + d:ProjectName="DialogButtonRight" + d:TargetPath="|QtTemplate.Item.DialogButtonRight;TemplateProjectOutputGroup|" Path="ItemTemplates" + d:VsixSubPath="ItemTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ItemTemplate" d:Source="Project" d:ProjectName="MainWindow" + d:TargetPath="|QtTemplate.Item.MainWindow;TemplateProjectOutputGroup|" Path="ItemTemplates" + d:VsixSubPath="ItemTemplates" /> + <Asset + Type="Microsoft.VisualStudio.ItemTemplate" d:Source="Project" d:ProjectName="Widget" + d:TargetPath="|QtTemplate.Item.Widget;TemplateProjectOutputGroup|" Path="ItemTemplates" + d:VsixSubPath="ItemTemplates" /> + <Asset + Type="Microsoft.VisualStudio.VsPackage" d:Source="File" Path="QtVsTools.Qml.Debug.pkgdef" /> + </Assets> +</PackageManifest> diff --git a/QtVsTools.RegExpr/Properties/AssemblyInfo.cs b/QtVsTools.RegExpr/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..87d3c5e --- /dev/null +++ b/QtVsTools.RegExpr/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("QtVsTools.RegExpr")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("QtVsTools.RegExpr")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0bdf77d1-4705-402c-8e58-f0d4d2679c08")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/QtVsTools.RegExpr/QtVsTools.RegExpr.csproj b/QtVsTools.RegExpr/QtVsTools.RegExpr.csproj new file mode 100644 index 0000000..92577c4 --- /dev/null +++ b/QtVsTools.RegExpr/QtVsTools.RegExpr.csproj @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <RootNamespace>QtVsTools.RegExpr</RootNamespace> + <AssemblyName>QtVsTools.RegExpr</AssemblyName> + <ProjectGuid>{A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}</ProjectGuid> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Xml.Linq" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <!-- // Visual Studio 2022 --> + <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'"> + </ItemGroup> + <!-- // Visual Studio 2019 --> + <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'"> + </ItemGroup> + <!-- // Visual Studio 2017 --> + <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'"> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <Compile Include="expression\CharClassLiteral.cs" /> + <Compile Include="expression\CharClassRange.cs" /> + <Compile Include="expression\CharClassSet.cs" /> + <Compile Include="expression\RegExpr.cs" /> + <Compile Include="expression\RegExprAssert.cs" /> + <Compile Include="expression\RegExprChoice.cs" /> + <Compile Include="expression\RegExprLiteral.cs" /> + <Compile Include="expression\RegExprRepeat.cs" /> + <Compile Include="expression\RegExprSequence.cs" /> + <Compile Include="expression\RegExprToken.cs" /> + <Compile Include="expression\Renderer.cs" /> + <Compile Include="parser\Parser.cs" /> + <Compile Include="parser\ParseTree.cs" /> + <Compile Include="production\Production.cs" /> + <Compile Include="production\ProductionRule.cs" /> + <Compile Include="production\ProductionRuleAction.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="utils\Consts.cs" /> + <Compile Include="utils\Utils.cs" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> \ No newline at end of file diff --git a/QtVsTools.RegExpr/README b/QtVsTools.RegExpr/README new file mode 100644 index 0000000..b548738 --- /dev/null +++ b/QtVsTools.RegExpr/README @@ -0,0 +1,317 @@ + +==== RegExpr: (not just) a C# wrapper for System.Text.RegularExpressions.Regex + +The purpose of this module is to allow: + + 1. Defining regular expressions as C# code instead of plain-old-strings; + + 2. Marking elements of regular expressions as tokens, allowing captured text to be accessed + and manipulated through token IDs; + + 3. Creating token production rules that specify how to process the captured tokens. + + +== 0. "TL;DR" + + * Regular expressions can be written as C# statements without any additional pre-processing. + + * A token definition within a regular expression allows matched text to be captured. + + * Tokens can include production rules that calculate an output object when matching the token. + + * Only one rule from the list of available rules in a token will be selected during parsing. + + * A rule can define a list of actions to be executed in sequence when that rule is selected. + + * Parser output will include all objects created by production rule actions. + + +== 1. Regular expressions as C# statements + +The classes in this module can be instantiated using C# statements to specify regular expressions +that are checked at compile-time, unlike plain-old-strings. Specifying reg-ex'es directly in C# +will potentially also make them more readable and maintainable. + +The following class hierarchy provides abstract representations of regular expressions: + + abstract RegExpr . . . . . . . . Base class of the regular expression abstraction + ^ + | + +--+ abstract CharClass . . . . Match one character of a class of characters + | ^ + | | + | +--+ CharClassLiteral . . . Match one character of a list of characters + | | + | +--+ CharClassRange . . . . Match one character of a range of characters + | | + | +--+ CharClassSet . . . . . Match one character of a set of character classes + | + +--+ RegExprLiteral . . . . . . Match a sequence of characters + | + +--+ RegExprRepeat . . . . . . . Match the same pattern repeatedly + | + +--+ RegExprSequence . . . . . . Match several patterns in sequence + | + +--+ RegExprChoice . . . . . . . Match one of several alternative patterns + | + +--+ RegExprAssert . . . . . . . Assert a pattern but do not consume any characters + | + +--+ Token . . . . . . . . . . . Capture and process text matched by a pattern + +The following syntax can be used to specify regular expressions in C# using RegExpr classes +(the notation _T_ represents an instance of type T): + + Expression Type Description +---------------------------------------------------------------------------------------------------- + CharWord CharClassLiteral Word character (\w) + + CharCr CharClassLiteral Carriage return character (\r) + + CharLf CharClassLiteral Line feed character (\n) + + CharSpace CharClassLiteral Space character (\s) + + CharNonSpace CharClassLiteral Non-space character (\S = [^\s]) + + CharVertSpace CharClassSet Vertical space ([\r\n]) + + CharHorizSpace CharClassSet Horizontal space ([^\S\r\n]) + + Char [ _char_ ] CharClassLiteral Literal character class + + Char [ _string_ ] CharClassLiteral Literal character class + + Char [ _char_ , _char_ ] CharClassRange Range character class + + ~ _CharClass_ CharClassSet Negated character class + + CharSet [ _CharClass_ + _CharClass_ ] CharClassSet Combined character class + + CharSet [ _CharClass_ - _CharClass_ ] CharClassSet Character class subtraction + + CharSetRaw [ _string_ ] CharClassLiteral Raw (unescaped) character class + + AnyChar RegExprLiteral Match any character (.) + + StartOfLine RegExprLiteral Anchor for start of line (^) + + EndOfFile RegExprLiteral Anchor for end of input string ($) + + LineBreak RegExprSequence Match a line break (\r?\n) + + RegX ( _string_ ) RegExprLiteral Literal character sequence + + _RegExpr_ .Optional() RegExprRepeat Optional match + + _RegExpr_ .Repeat() RegExprRepeat Match zero or more times + + _RegExpr_ .Repeat ( atLeast: _int_ ) RegExprRepeat Match at least N times + + _RegExpr_ .Repeat ( atMost: _int_ ) RegExprRepeat Match at most N times + + _RegExpr_ .Repeat ( _int_ ) RegExprRepeat Match exactly N times + + _RegExpr_ .Repeat ( _int_ , _int_ ) RegExprRepeat Match between N and M times + + _RegExpr_ & _RegExpr_ RegExprSequence Sequential composition + + _RegExpr_ | _RegExpr_ RegExprChoice Alternating composition + + Assert [ _RegExpr_ ] RegExprAssert Positive assertion (look ahead) + + !Assert [ _RegExpr_ ] RegExprAssert Negative assertion (look ahead) + + Assert [ _RegExpr_ ] > _RegExpr_ RegExprSequence Assert look ahead before match + + _RegExpr_ > Assert [ _RegExpr_ ] RegExprSequence Assert look ahead after match + + Assert [ _RegExpr_ ] < _RegExpr_ RegExprSequence Assert look behind before match + + _RegExpr_ < Assert [ _RegExpr_ ] RegExprSequence Assert look behind after match + + !Assert [ _RegExpr_ ] > _RegExpr_ RegExprSequence Negative look ahead before match + + _RegExpr_ > !Assert [ _RegExpr_ ] RegExprSequence Negative look ahead after match + + !Assert [ _RegExpr_ ] < _RegExpr_ RegExprSequence Negative look behind before match + + _RegExpr_ < !Assert [ _RegExpr_ ] RegExprSequence Negative look behind after match + + RegXRaw ( _string_ ) RegExprLiteral Raw (unescaped) Regex string + +Examples of regular expressions as C# statements: + + RegExpr (C#) Regular Expression +---------------------------------------------------------------------- + Char["abc"] [abc] + + Char['a', 'z'] [a-z] + + ~Char["abc"] [^abc] + + CharSet[Char["abc"] + Char['x', 'z']] [abcx-z] + + CharSet[Char['a', 'z'] - Char["aeiou"]] [a-z-[aeiou]] + + CharSetRaw["az-[aeiou]"] [a-z-[aeiou]] + + RegX("abc") abc + + RegX(@"\a\b\c") \\a\\b\\c + + RegXRaw(@"\S\r\n") \S\r\n + + RegX("a").Optional() a? + + RegX("a").Repeat() a* + + RegX("a").Repeat(atLeast: 1) a+ + + RegX("a").Repeat(atLeast: 2) a{2,} + + RegX("a").Repeat(atMost: 3) a{,3} + + RegX("a").Repeat(4) a{4} + + RegX("a").Repeat(5, 6) a{5,6} + + RegX("a") & "xyz" axyz + + RegX("a") | "xyz" (?:a)|(?:xyz) + + Assert["abc"] (?=abc) + + Assert[RegX("abc")] > CharWord.Repeat() (?=abc)\w* + + !Assert[RegX("abc") > CharWord.Repeat() (?!abc)\w* + + RegX("abc") > Assert[RegX("xyz")] abc(?=xyz) + + Assert[RegX("abc")] < RegX("xyz") (?<=abc)xyz + + CharWord.Repeat() < Assert[RegX("xyz")] \w*(?<=xyz) + + CharWord.Repeat() < !Assert[RegX("xyz")] \w*(?<!xyz) + + +== 2. Tokens + +The following statement creates a token based on a RegExpr: + + new Token ( _string_ , _RegExpr_ ) + +The string param will be used as an ID to reference the text captured by the RegExpr. +The whitespace immediately before a token can be automatically skipped. A RegExpr to match the +whitespace can be provided as a default for all tokens, or given specifically to each token: + + new Token ( _string_ , _RegExpr_ , _RegExpr_ ) + +The first RegExpr param specifies the pattern of leading whitespace to be skipped for this token. +Whitespace skipping can be disabled for specific tokens: + + new Token ( _string_ , SkipWs_Disable , _RegExpr_ ) + +The following are examples of token definitions: + + new Token("NUM", Char['0', '9'].Repeat()) + + new Token("WORD", Space.Repeat(), CharWord.Repeat()) + + new Token("STRING", SkipWs_Disable, ~Char['\"'].Repeat()) + + +== 3. Production rules + +By default, a token will output the string that was captured by the specified RegExpr. It is +possible to define production rules for a token, indicating how to instantiate an arbitrary output +object based on content captured by that token or other tokens. This uses the following syntax: +(the notation (_T1_, _T2_, ...) => _T_ represents a callback delegate with param types T1, T2, etc., +and return type T; in case of void callback, return type is given as _void_ ) + + new Token ( _string_ , _RegExpr_) + { + new Rule < T > ( + priority: _int_ , + select: (_Token_) => _bool_ , + pre: (_Token_) => _bool_ ) + { + _RuleAction_, + ... + }, + ... + } + +'T' stands for the output type when the token is matched. In this case, use of the Rule<T> class +means that the token will produce an object of type T based only on the captured string. More +complex rules can be specified, which will allow the analysis of syntaxes with recursively delimited +expressions (e.g. expressions with nested parentheses), and infix, prefix or postfix operators: + + PrefixRule < T , TOperand > . . . . . . . . . . . . Prefix operator + + PostfixRule < TOperand , T > . . . . . . . . . . . Postfix operator + + InfixRule < TLeftOperand , T , TRightOperand > . . Infix operator + + LeftDelimiterRule < T > . . . . . . . . . . . . . . Left delimiter (e.g. open parenthesis) + + RightDelimiterRule< TLeftDelim , TExpr , T > . . . Left delimiter (e.g. close parenthesis) + +When capturing text, only one of the rules in the token definition will be applied. The conditions +for token rule selection are specified by rule selector predicates given in the "select:" param. +If a selector predicate fails, the parser will try to select another rule for the token. + +A rule pre-condition can also be specified. This will be tested after the rule was selected and +just before it is executed. If the pre-condition fails, this will generate a parse error. + +To obtain the actual production, i.e. instantiation of an output object, a production rule needs +to provide one or more actions that describe how to create or manipulate the production object. +There are 4 types of possible actions that can be specified inside a production rule: + + Capture < T > ( _(string)_ => _T_ ) New production from token capture + + Create < T , ... > ( _(...)_ => _T_ ) New production from operand productions + + Transform < T , ... > ( _(T,...)_ => _T_ ) New production from current value and operands + + Update < T , ... > ( _(T,...)_ => _void_ ) Update current production value + +Actions may also specify a condition predicate for its params; if the predicate fails, the action +will not be taken and rule execution will continue with the next action in the list. + +A non producing action 'Error' can also be specified within a rule to provide additional syntax +verification. When an Error predicate is verified, this action will produce a string corresponding +to a parse error message; parsing stops at this point. + + +== 4. Examples + +The following token will match a decimal constant and output its int value: + + new Token("NUM", Char['0', '9'].Repeat()) + { + new Rule<int> + { + Capture(value => int.Parse(value)) + } + } + +The following token will match the operator '+' in the context of an int expression: + + new Token("PLUS", "+") + { + new PrefixRule<int, int>( + priority: PRIORITY_PREFIX, + select: t => (t.IsFirst || t.LookBehind().First().Is("LEFT_PAR")) + && t.LookAhead().First().Is("NUM", "LEFT_PAR")) + { + Create((int x) => +x) + }, + + new InfixRule<int, int, int>(priority: PRIORITY_INFIX) + { + Create((int x, int y) => x + y) + } + }; + +More detailed examples of the use of the RegExpr module can be found in the provided auto-tests +(project Test_QtVsTools.RegExpr). diff --git a/QtVsTools.RegExpr/expression/CharClassLiteral.cs b/QtVsTools.RegExpr/expression/CharClassLiteral.cs new file mode 100644 index 0000000..828f3e8 --- /dev/null +++ b/QtVsTools.RegExpr/expression/CharClassLiteral.cs @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// CharClassLiteral ( -> CharClassSet.Element -> CharClass -> RegExpr ) + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Elementary character class defined by one or more allowed characters + /// </summary> + /// + public class CharClassLiteral : CharClassSet.Element + { + public string LiteralChars { get; set; } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + if ((parent == null || !(parent is CharClass)) && NeedsGroup(LiteralChars)) + pattern.AppendFormat("[{0}]", LiteralChars); + else + pattern.Append(LiteralChars); + return null; + } + } + + public abstract partial class CharClass : RegExpr + { + public static CharClassLiteral CharLiteral(string s) + { + return new CharClassLiteral + { + LiteralChars = Escape(s) + }; + } + + public static CharClassLiteral CharRawLiteral(string s) + { + return new CharClassLiteral + { + LiteralChars = s + }; + } + + public static CharClassLiteral CharLiteral(char c) + { + return CharLiteral(c.ToString()); + } + + public partial class CharExprBuilder + { + public CharClassLiteral this[string s] { get { return CharLiteral(s); } } + public CharClassLiteral this[char c] { get { return CharLiteral(c); } } + } + } +} diff --git a/QtVsTools.RegExpr/expression/CharClassRange.cs b/QtVsTools.RegExpr/expression/CharClassRange.cs new file mode 100644 index 0000000..fc92d4c --- /dev/null +++ b/QtVsTools.RegExpr/expression/CharClassRange.cs @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// CharClassRange ( -> CharClassSet.Element -> CharClass -> RegExpr ) + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Represents an elementary character class defined by a range of characters + /// </summary> + /// + public partial class CharClassRange : CharClassSet.Element + { + public CharClassLiteral LowerBound { get; set; } + public CharClassLiteral UpperBound { get; set; } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + + if (parent == null || !(parent is CharClass)) + pattern.Append("["); + + return Items(LowerBound, UpperBound); + } + + protected override void OnRenderNext(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderNext(defaultTokenWs, parent, pattern, ref mode, tokenStack); + pattern.Append("-"); + } + + protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode, tokenStack); + + if (parent == null || !(parent is CharClass)) + pattern.Append("]"); + } + } + + public abstract partial class CharClass : RegExpr + { + public static CharClassRange CharRange(string lBound, string uBound) + { + return CharRange(CharLiteral(lBound), CharLiteral(uBound)); + } + + public static CharClassRange CharRange(CharClassLiteral lBound, CharClassLiteral uBound) + { + return new CharClassRange + { + LowerBound = lBound, + UpperBound = uBound + }; + } + + public partial class CharExprBuilder + { + public CharClassRange this[char lBound, char uBound] + { get { return CharRange(lBound.ToString(), uBound.ToString()); } } + } + } +} diff --git a/QtVsTools.RegExpr/expression/CharClassSet.cs b/QtVsTools.RegExpr/expression/CharClassSet.cs new file mode 100644 index 0000000..36e1fa6 --- /dev/null +++ b/QtVsTools.RegExpr/expression/CharClassSet.cs @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + using static CharClassSet; + using static CharClass.CharSetExprBuilder; + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// CharClassSet ( -> CharClass -> RegExpr ) + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Represents a complex class defined by a combination of elementary character classes. + /// </summary> + /// + public partial class CharClassSet : CharClass, IEnumerable<IEnumerable<Element>> + { + public IEnumerable<Element> Positives { get; set; } + public IEnumerable<Element> Negatives { get; set; } + + bool IsSubSet { get; set; } + bool HasPositive { get { return Positives != null && Positives.Any(); } } + bool HasNegative { get { return Negatives != null && Negatives.Any(); } } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + + if (!HasPositive && !HasNegative) + return null; + + if (!IsSubSet) { + if (HasPositive) + pattern.Append("["); + else + pattern.Append("[^"); + } + + IEnumerable<RegExpr> children = null; + if (HasPositive && HasNegative) { + children = Items( + new CharClassSet(positives: Positives) { IsSubSet = true }, + new CharClassSet(negatives: Negatives) { IsSubSet = true }); + } else { + if (HasPositive) + children = Positives; + else if (HasNegative) + children = Negatives; + } + + return children; + } + + protected override void OnRenderNext(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderNext(defaultTokenWs, parent, pattern, ref mode, tokenStack); + if (!IsSubSet && HasPositive && HasNegative) + pattern.Append("-["); + } + + protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode, tokenStack); + if (!IsSubSet) { + if (HasPositive && HasNegative) + pattern.Append("]]"); + else + pattern.Append("]"); + } + } + + public const bool Invert = true; + public const bool Positive = true; + + public CharClassSet( + IEnumerable<Element> positives = null, + IEnumerable<Element> negatives = null) + { + Positives = positives != null ? positives : Empty<Element>(); + Negatives = negatives != null ? negatives : Empty<Element>(); + } + + public CharClassSet(Element element, bool negative = false) : this() + { + if (negative) + Negatives = Items(element); + else + Positives = Items(element); + } + + public CharClassSet(CharClassSet set, bool invert = false) : this() + { + Add(set, invert); + } + + public void Add(Element element, bool negative = false) + { + if (negative) + Negatives = Negatives.Concat(Items(element)); + else + Positives = Positives.Concat(Items(element)); + } + + public void Add(CharClassSet set, bool invert = false) + { + Positives = Positives.Concat(!invert ? set.Positives : set.Negatives); + Negatives = Negatives.Concat(!invert ? set.Negatives : set.Positives); + } + + public IEnumerator<IEnumerable<Element>> GetEnumerator() + { + return (new[] { Positives, Negatives }).AsEnumerable().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public abstract class Element : CharClass + { + public static CharClassSet operator ~(Element x) + { return new CharClassSet(x, negative: true); } + + public static PositiveSet operator +(Element x, Element y) + { return new PositiveSet(Op.Plus, x, y); } + + public static PositiveSet operator +(Element x, PositiveSet y) + { return new PositiveSet(Op.Plus, x, y); } + + public static PositiveSet operator +(PositiveSet x, Element y) + { return new PositiveSet(Op.Plus, x, y); } + + public static Expr operator -(Element x, Element y) + { return new Expr(Op.Minus, x, y); } + + public static Expr operator -(Element x, PositiveSet y) + { return new Expr(Op.Minus, x, y); } + + public static Expr operator -(PositiveSet x, Element y) + { return new Expr(Op.Minus, x, y); } + } + + public static CharClassSet operator ~(CharClassSet x) + { return new CharClassSet(x, invert: true); } + + public static Expr operator +(Element x, CharClassSet y) + { return new Expr(Op.Plus, x, y); } + + public static Expr operator +(CharClassSet x, Element y) + { return new Expr(Op.Plus, x, y); } + + public static Expr operator +(PositiveSet x, CharClassSet y) + { return new Expr(Op.Plus, x, y); } + + public static Expr operator +(CharClassSet x, PositiveSet y) + { return new Expr(Op.Plus, x, y); } + + public static Expr operator +(CharClassSet x, CharClassSet y) + { return new Expr(Op.Plus, x, y); } + + public static Expr operator -(CharClassSet x, Element y) + { return new Expr(Op.Minus, x, y); } + + public static Expr operator -(CharClassSet x, PositiveSet y) + { return new Expr(Op.Minus, x, y); } + } + + public abstract partial class CharClass : RegExpr + { + public partial class CharSetExprBuilder + { + public enum Op { Term, Tilde, Plus, Minus } + + public class Expr + { + public Op Operator { get; private set; } + + public List<Expr> Factors { get; private set; } + public Expr(Op op, List<Expr> factors) { Operator = op; Factors = factors; } + public Expr(Op op, params Expr[] factors) : this(op, factors.ToList()) { } + + public CharClass Term { get; private set; } + public Expr(CharClass c) { Operator = Op.Term; Term = c; } + public static implicit operator Expr(CharClass c) { return new Expr(c); } + public static implicit operator Expr(string s) { return Char[s]; } + public static implicit operator Expr(char c) { return Char[c]; } + public static Expr operator ~(Expr x) + { return new Expr(Op.Tilde, x); } + } + + public class PositiveSet : Expr + { + public PositiveSet(Op op, params Expr[] factors) : base(op, factors) { } + + public static PositiveSet operator +(PositiveSet x, PositiveSet y) + { return new PositiveSet(Op.Plus, x, y); } + + public static Expr operator -(PositiveSet x, PositiveSet y) + { return new Expr(Op.Minus, x, y); } + } + + public CharClassSet this[params Expr[] exprs] + { + get + { + return this[new PositiveSet(Op.Plus, exprs)]; + } + } + + public CharClassSet this[Expr expr] + { + get + { + var stack = new Stack<StackFrame>(); + stack.Push(expr); + CharClassSet classSet = null; + + while (stack.Any()) { + var context = stack.Pop(); + expr = context.Expr; + if (context == null || expr == null) { + continue; + } else if (context.Children == null) { + context.Children = new Queue<Expr>(); + if (expr.Factors != null && expr.Factors.Any()) + expr.Factors.ForEach(x => context.Children.Enqueue(x)); + stack.Push(context); + continue; + } else if (context.Children.Any()) { + expr = context.Children.Dequeue(); + stack.Push(context); + stack.Push(expr); + continue; + } + + classSet = null; + if (expr.Operator == Op.Term) { + if (expr.Term is CharClassSet) + classSet = expr.Term as CharClassSet; + else + classSet = new CharClassSet(expr.Term as Element); + } else if (context.SubSets != null && context.SubSets.Any()) { + switch (expr.Operator) { + case Op.Tilde: + classSet = new CharClassSet + { + { context.SubSets.First(), Invert } + }; + break; + case Op.Plus: + classSet = new CharClassSet + { + { context.SubSets.First() }, + { context.SubSets.Last() } + }; + break; + case Op.Minus: + classSet = new CharClassSet + { + { context.SubSets.First() }, + { context.SubSets.Last(), Invert } + }; + break; + } + } + + var parentContext = stack.Any() ? stack.Peek() : null; + if (classSet != null && parentContext != null) + parentContext.SubSets.Add(classSet); + } + + if (classSet == null) + throw new CharClassEvalException(); + + return classSet; + } + } + + public CharClassSet this[IEnumerable<char> chars] + { + get + { + return this[chars.Select(c => (Expr)c).ToArray()]; + } + } + + class StackFrame + { + public Expr Expr { get; set; } + public Queue<Expr> Children { get; set; } + public List<CharClassSet> SubSets { get; set; } + public StackFrame() + { + Expr = null; + Children = null; + SubSets = new List<CharClassSet>(); + } + public static implicit operator StackFrame(Expr e) + { + return new StackFrame { Expr = e }; + } + } + } + + public partial class CharSetRawExprBuilder + { + public CharClassLiteral this[string s] { get { return CharRawLiteral(s); } } + } + + public class CharClassEvalException : RegExprException + { + public CharClassEvalException(string message = null) : base(message) { } + } + } +} diff --git a/QtVsTools.RegExpr/expression/RegExpr.cs b/QtVsTools.RegExpr/expression/RegExpr.cs new file mode 100644 index 0000000..2b77846 --- /dev/null +++ b/QtVsTools.RegExpr/expression/RegExpr.cs @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// RegExpr + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Abstract representation of a regular expression. + /// </summary> + /// <remarks> + /// RegExpr objects can be created and combined using C# expressions, and then rendered into a + /// regular expression pattern and parser. + /// </remarks> + public abstract partial class RegExpr + { + /// <summary> + /// Render the RegExpr into a corresponding pattern and parser. + /// </summary> + /// <param name="defaultTokenWs">Default token whitespace</param> + /// <returns> + /// <see cref="Parser"/> object that can process strings according to the pattern rendered. + /// </returns> + public Parser Render(RegExpr defaultTokenWs = null) + { + return new Parser(this, defaultTokenWs); + } + + [Flags] + protected enum RenderMode { Default = 0, Assert = 1 } + + /// <summary> + /// Event triggered when starting the rendering process for this RegExpr. + /// </summary> + /// <param name="defaultTokenWs">Default token whitespace</param> + /// <param name="parent">Parent expression</param> + /// <param name="pattern">Rendered pattern</param> + /// <param name="mode">Rendering mode</param> + /// + /// <returns>Sub-expressions to add to the rendering process.</returns> + /// <remarks> + /// RegExpr sub-classes will re-implement OnRenderBegin, OnRenderNext and OnRenderEnd to + /// define their specific rendering process. Regular expression strings are appended to + /// <paramref name="pattern"/>. When rendering a named capture group, a mapping to a + /// production object can be defined and added to <paramref name="prodMap"/>. The production + /// object will be responsible for translating the captured values into instances of + /// external, application specific classes. + /// </remarks> + protected virtual IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { return null; } + + /// <summary> + /// Event triggered when rendering the next sub-expression. + /// </summary> + /// <param name="defaultTokenWs">Default token whitespace</param> + /// <param name="parent">Parent expression</param> + /// <param name="pattern">Rendered pattern</param> + /// <param name="mode">Rendering mode</param> + /// + protected virtual void OnRenderNext(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { } + + /// <summary> + /// Event triggered after all sub-expressions have been rendered. + /// </summary> + /// <param name="defaultTokenWs">Default token whitespace</param> + /// <param name="parent">Parent expression</param> + /// <param name="pattern">Rendered pattern</param> + /// <param name="mode">Rendering mode</param> + /// + protected virtual void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { } + + public class RegExprException : System.Exception + { + public RegExprException(string message = null) : base(message) { } + } + } +} diff --git a/QtVsTools.RegExpr/expression/RegExprAssert.cs b/QtVsTools.RegExpr/expression/RegExprAssert.cs new file mode 100644 index 0000000..09172c0 --- /dev/null +++ b/QtVsTools.RegExpr/expression/RegExprAssert.cs @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + using static RegExprAssert; + + //////////////////////////////////////////////////////////////////////////////////////////////// + /// + /// RegExprAssert ( -> RegExpr) + /// + //////////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Asserts a pattern on the input string without consuming chars. + /// </summary> + /// + public partial class RegExprAssert : RegExpr + { + public enum AssertLook { Ahead, Behind } + + public AssertLook Context { get; set; } + public bool Negative { get; set; } + public RegExpr Expr { get; set; } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + + if (mode.HasFlag(RenderMode.Assert)) + throw new NestedAssertException(); + + switch (Context) { + case AssertLook.Ahead: + if (Negative) + pattern.Append("(?!"); + else + pattern.Append("(?="); + break; + case AssertLook.Behind: + if (Negative) + pattern.Append("(?<!"); + else + pattern.Append("(?<="); + break; + } + + mode |= RenderMode.Assert; + return Items(Expr); + } + + protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode, tokenStack); + pattern.Append(")"); + mode &= ~RenderMode.Assert; + } + } + + public abstract partial class RegExpr + { + RegExprAssert AsAssert() + { + if (this is RegExprAssert) + return this as RegExprAssert; + + return new RegExprAssert + { + Context = AssertLook.Ahead, + Negative = false, + Expr = this + }; + } + + public static RegExprAssert AssertLookAhead(RegExpr expr) + { + var assert = expr.AsAssert(); + return new RegExprAssert + { + Context = AssertLook.Ahead, + Negative = assert.Negative, + Expr = assert.Expr + }; + } + + public static RegExprAssert AssertLookBehind(RegExpr expr) + { + var assert = expr.AsAssert(); + return new RegExprAssert + { + Context = AssertLook.Behind, + Negative = assert.Negative, + Expr = assert.Expr + }; + } + + public static RegExprAssert AssertNegated(RegExpr expr) + { + var assert = expr.AsAssert(); + return new RegExprAssert + { + Context = assert.Context, + Negative = !assert.Negative, + Expr = assert.Expr + }; + } + + public delegate RegExprAssert AssertTemplate(RegExpr expr); + + public class AssertExprBuilder + { + AssertTemplate Template { get; set; } + + public AssertExprBuilder(AssertTemplate template) + { + Template = template; + } + + public class Expr + { + public RegExprAssert Assert { get; set; } + public Expr(RegExprAssert assert) { Assert = assert; } + + public static implicit operator RegExpr(Expr e) + { + return e.Assert; + } + + public static RegExpr operator &(RegExpr rx1, Expr rx2) + { + return Concat(rx1, rx2); + } + + public static RegExpr operator |(RegExpr rx1, Expr rx2) + { + return Choice(rx1, rx2); + } + } + + public class NegateableExpr : Expr + { + public NegateableExpr(RegExprAssert assert) : base(assert) { } + + public static Expr operator !(NegateableExpr x) + { + return new Expr(AssertNegated(x.Assert)); + } + } + + public NegateableExpr this[RegExpr expr] + { + get { return new NegateableExpr(Template(expr)); } + } + } + + public class NestedAssertException : RegExprException + { + public NestedAssertException(string message = null) : base(message) { } + } + } +} diff --git a/QtVsTools.RegExpr/expression/RegExprChoice.cs b/QtVsTools.RegExpr/expression/RegExprChoice.cs new file mode 100644 index 0000000..83afe0e --- /dev/null +++ b/QtVsTools.RegExpr/expression/RegExprChoice.cs @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + /// <summary> + /// Alternating composition + /// </summary> + public partial class RegExprChoice : RegExpr + { + public IEnumerable<RegExpr> Exprs { get; set; } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + if (!(parent is Token)) + pattern.Append("(?:"); + pattern.Append("(?:"); + return Exprs; + } + + protected override void OnRenderNext(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderNext(defaultTokenWs, parent, pattern, ref mode, tokenStack); + pattern.Append(")|(?:"); + } + + protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode, tokenStack); + pattern.Append(")"); + if (!(parent is Token)) + pattern.Append(")"); + } + } + + public abstract partial class RegExpr + { + public static RegExpr Choice(params RegExpr[] rxs) + { + return new RegExprChoice + { + Exprs = rxs.SelectMany(rx => rx is RegExprChoice + ? ((RegExprChoice)rx).Exprs + : Items(rx)) + }; + + } + + public static RegExpr operator |(RegExpr rx1, RegExpr rx2) + { + return Choice(rx1, rx2); + } + } +} diff --git a/QtVsTools.RegExpr/expression/RegExprLiteral.cs b/QtVsTools.RegExpr/expression/RegExprLiteral.cs new file mode 100644 index 0000000..aa8612e --- /dev/null +++ b/QtVsTools.RegExpr/expression/RegExprLiteral.cs @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + /// <summary> + /// Literal character sequence + /// </summary> + public class RegExprLiteral : RegExpr + { + public string LiteralExpr { get; set; } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + pattern.Append(LiteralExpr); + return null; + } + } + + public abstract partial class RegExpr + { + public static RegExprLiteral RegX(string s) + { + return new RegExprLiteral() { LiteralExpr = Escape(s) }; + } + + public static RegExprLiteral RegXRaw(string s) + { + return new RegExprLiteral() { LiteralExpr = s }; + } + + public static implicit operator RegExpr(string s) + { + return RegX(s); + } + + public static implicit operator RegExpr(char c) + { + return RegX(c.ToString()); + } + } +} diff --git a/QtVsTools.RegExpr/expression/RegExprRepeat.cs b/QtVsTools.RegExpr/expression/RegExprRepeat.cs new file mode 100644 index 0000000..a6ee1fa --- /dev/null +++ b/QtVsTools.RegExpr/expression/RegExprRepeat.cs @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + /// <summary> + /// Regular expression quantifier. + /// </summary> + public class RegExprRepeat : RegExpr + { + public int AtLeast { get; set; } + public int AtMost { get; set; } + public RegExpr Expr { get; set; } + + bool ExprNeedsGroup + { + get + { + return (Expr is RegExprSequence) + || (Expr is RegExprLiteral + && NeedsGroup(Expr.As<RegExprLiteral>().LiteralExpr)); + } + } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + + if (ExprNeedsGroup) + pattern.Append("(?:"); + + return Items(Expr); + } + + protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode, tokenStack); + + if (ExprNeedsGroup) + pattern.Append(")"); + + if (AtLeast == 0 && AtMost == 1) + pattern.Append("?"); + else if (AtLeast == 0 && AtMost == int.MaxValue) + pattern.Append("*"); + else if (AtLeast == 1 && AtMost == int.MaxValue) + pattern.Append("+"); + else if (AtLeast == AtMost) + pattern.AppendFormat("{{{0}}}", AtLeast); + else if (AtMost == int.MaxValue) + pattern.AppendFormat("{{{0},}}", AtLeast); + else if (AtLeast == 0) + pattern.AppendFormat("{{,{0}}}", AtMost); + else + pattern.AppendFormat("{{{0},{1}}}", AtLeast, AtMost); + } + } + + public abstract partial class RegExpr + { + public RegExpr Optional() + { + return Repeat(0, 1); + } + + public RegExpr Repeat(int count) + { + return Repeat(count, count); + } + + public RegExpr Repeat(int atLeast = 0, int atMost = int.MaxValue) + { + if (this is RegExprRepeat) + throw new NestedRepeatException(); + + return new RegExprRepeat + { + AtLeast = atLeast, + AtMost = atMost, + Expr = this + }; + } + + public class NestedRepeatException : RegExprException + { + public NestedRepeatException(string message = null) : base(message) { } + } + } +} diff --git a/QtVsTools.RegExpr/expression/RegExprSequence.cs b/QtVsTools.RegExpr/expression/RegExprSequence.cs new file mode 100644 index 0000000..80528fa --- /dev/null +++ b/QtVsTools.RegExpr/expression/RegExprSequence.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + /// <summary> + /// Sequential composition + /// </summary> + public partial class RegExprSequence : RegExpr + { + public IEnumerable<RegExpr> Exprs { get; set; } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + return Exprs; + } + } + + public abstract partial class RegExpr + { + public static RegExprSequence Concat(params RegExpr[] rxs) + { + return new RegExprSequence + { + Exprs = rxs.SelectMany(rx => rx is RegExprSequence + ? ((RegExprSequence)rx).Exprs + : Items(rx)) + }; + } + + public static RegExprSequence operator &(RegExpr rx1, RegExpr rx2) + { + return Concat(rx1, rx2); + } + } +} diff --git a/QtVsTools.RegExpr/expression/RegExprToken.cs b/QtVsTools.RegExpr/expression/RegExprToken.cs new file mode 100644 index 0000000..08152b1 --- /dev/null +++ b/QtVsTools.RegExpr/expression/RegExprToken.cs @@ -0,0 +1,388 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + public enum SkipWhitespace { Disable, Enable } + + //////////////////////////////////////////////////////////////////////////////////////////// + /// + /// RegExpr.Token + /// + //////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// A token of the syntax under analysis. + /// </summary> + /// <remarks> + /// Token objects contain an encapsulated <see cref="RegExpr"/> that defines the pattern + /// of the token's syntax, rendered as a named capture group. The name of the capture group + /// corresponds to the identifier of the token. A Token may also include production rules + /// defining the actions to take when content is matched by the associated capture group. + /// </remarks> + public partial class Token : RegExpr, IEnumerable<IProductionRule> + { + public string Id { get; set; } + + public bool SkipLeadingWhitespace { get; set; } + public RegExpr LeadingWhitespace { get; set; } + + public RegExpr Expr { get; set; } + + public HashSet<Token> Children { get; set; } + public Dictionary<string, Token> Parents { get; set; } + public IEnumerable<string> CaptureIds => Parents.Keys; + + public Token(string id, RegExpr skipWs, RegExpr expr) + { + Id = id; + SkipLeadingWhitespace = true; + LeadingWhitespace = skipWs; + Expr = expr; + Rules = new TokenRules(); + Children = new HashSet<Token>(); + Parents = new Dictionary<string, Token>(); + } + + public Token(string id, SkipWhitespace skipWs, RegExpr expr) + { + Id = id; + SkipLeadingWhitespace = (skipWs == SkipWhitespace.Enable); + Expr = expr; + Rules = new TokenRules(); + Children = new HashSet<Token>(); + Parents = new Dictionary<string, Token>(); + } + + public Token(Enum id, RegExpr expr) + : this(id.ToString(), SkipWhitespace.Enable, expr) + { } + + public Token(string id, RegExpr expr) + : this(id, SkipWhitespace.Enable, expr) + { } + + public Token(Enum id, SkipWhitespace skipWs, RegExpr expr) + : this(id.ToString(), skipWs, expr) + { } + + public Token(Enum id, RegExpr skipWs, RegExpr expr) + : this(id.ToString(), skipWs, expr) + { } + + public Token(RegExpr skipWs, RegExpr expr) + : this(string.Empty, skipWs, expr) + { } + + public Token(RegExpr expr) + : this(string.Empty, SkipWhitespace.Enable, expr) + { } + + public Token() + : this(string.Empty, SkipWhitespace.Enable, null) + { } + + public static Token CreateRoot() + { + var rootToken = new Token(); + rootToken.Parents[ParseTree.KeyRoot] = rootToken; + return rootToken; + } + + protected override IEnumerable<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); + + var tokenWs = GetTokenWhitespace(defaultTokenWs); + if (tokenWs != null) + pattern.Append("(?:"); + if (NeedsWhitespaceGroup(tokenWs, mode)) + pattern.Append("(?:"); + return Items(tokenWs, Expr); + } + + protected override void OnRenderNext(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderNext(defaultTokenWs, parent, pattern, ref mode, tokenStack); + var tokenWs = GetTokenWhitespace(defaultTokenWs); + if (NeedsWhitespaceGroup(tokenWs, mode)) + pattern.Append(")"); + if (Expr != null) { + if (!mode.HasFlag(RenderMode.Assert) && !string.IsNullOrEmpty(Id)) { + string captureId = GenerateCaptureId(Id); + Parents.Add(captureId, tokenStack.Peek()); + tokenStack.Peek().Children.Add(this); + pattern.AppendFormat("(?<{0}>", captureId); + } else { + pattern.Append("(?:"); + } + } + tokenStack.Push(this); + } + + protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, + StringBuilder pattern, ref RenderMode mode, Stack<Token> tokenStack) + { + base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode, tokenStack); + if (Expr != null) + pattern.Append(")"); + var tokenWs = GetTokenWhitespace(defaultTokenWs); + if (tokenWs != null) + pattern.Append(")"); + tokenStack.Pop(); + } + + /// <summary> + /// Set of rules that can be applied to the token. + /// </summary> + TokenRules Rules { get; set; } + + public void Add(IProductionRule rule) + { + Rules.Add(rule); + rule.Token = this; + } + + public IProductionRule SelectRule(ITokenCapture tokenCapture) + { + return Rules.Select(tokenCapture); + } + + const string TokenUniqueIdTemplate = "TOKEN_{0}_{1}"; + + protected static string GenerateCaptureId(string Id) + { + return string.Concat(string.Format(TokenUniqueIdTemplate, + Path.GetRandomFileName().Replace(".", ""), Id) + .Take(32)); + } + + RegExpr GetTokenWhitespace(RegExpr defaultTokenWs) + { + if (!SkipLeadingWhitespace) + return null; + var tokenWs = LeadingWhitespace; + if (tokenWs == null) + tokenWs = defaultTokenWs; + return tokenWs; + } + + bool NeedsWhitespaceGroup(RegExpr tokenWs, RenderMode mode) + { + return tokenWs != null && !mode.HasFlag(RenderMode.Assert) + && (tokenWs is RegExprLiteral || tokenWs is RegExprSequence); + } + + public IEnumerator<IProductionRule> GetEnumerator() + { + return ((IEnumerable<IProductionRule>)Rules).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<IProductionRule>)Rules).GetEnumerator(); + } + } + + public class TokenGroup : IEnumerable<string> + { + HashSet<string> TokenIds { get; set; } + + public TokenGroup(params string[] tokenIds) + { + TokenIds = new HashSet<string>(tokenIds); + } + + public void Add(string tokenId) + { + TokenIds.Add(tokenId); + } + + public void Add(IEnumerable<string> tokenIds) + { + TokenIds.UnionWith(tokenIds); + } + + public void Add(Token token) + { + Add(token.Id); + } + + public static implicit operator TokenGroup(string tokenId) + { + return new TokenGroup(tokenId); + } + + public IEnumerator<string> GetEnumerator() + { + return TokenIds.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return TokenIds.GetEnumerator(); + } + } + + public class TokenRules : IEnumerable<IProductionRule> + { + Dictionary<RuleCallback.Selector, IProductionRule> Rules { get; set; } + IProductionRule DefaultRule { get; set; } + + public TokenRules() + { + Rules = new Dictionary<RuleCallback.Selector, IProductionRule>(); + } + + public void Add(IProductionRule item) + { + if (item.Selector == Default) + DefaultRule = item; + else + Rules[item.Selector] = item; + } + + bool TestSelector( + KeyValuePair<RuleCallback.Selector, IProductionRule> pairSelectorRule, + ITokenCapture tokenCapture) + { + var selector = pairSelectorRule.Key; + var rule = pairSelectorRule.Value; + if (rule == null) + return false; + if (selector != null && !selector(tokenCapture)) + return false; + return true; + } + + public IProductionRule Select(ITokenCapture tokenCapture) + { + var selectedRules = Rules + .Where(rule => TestSelector(rule, tokenCapture)) + .Select(rule => rule.Value); + return selectedRules.Any() ? selectedRules.First() : DefaultRule; + } + + public IEnumerator<IProductionRule> GetEnumerator() + { + return Rules.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Rules.Values.GetEnumerator(); + } + } + + public interface ITokenCapture + { + string TokenId { get; } + string Value { get; } + bool IsFirst { get; } + bool IsLast { get; } + IEnumerable<ITokenCapture> LookAhead(params TokenGroup[] tokenIds); + IEnumerable<ITokenCapture> LookBehind(params TokenGroup[] tokenIds); + bool Is(params TokenGroup[] tokenIds); + bool IsNot(params TokenGroup[] tokenIds); + } + + public interface IOperatorCapture : ITokenCapture + { + IOperandCapture Operand { get; } + IOperandCapture LeftOperand { get; } + IOperandCapture RightOperand { get; } + bool HasOperand { get; } + bool HasLeftOperand { get; } + bool HasRightOperand { get; } + } + + public interface IOperandCapture : ITokenCapture + { + object Production { get; } + } + + public class TokenEndOfList : IOperatorCapture, IOperandCapture + { + public string TokenId { get; } + public string Value { get; } + public bool IsFirst { get; } + public bool IsLast { get; } + public IEnumerable<ITokenCapture> LookAhead(params TokenGroup[] tokenIds) + { + return Items(this); + } + + public IEnumerable<ITokenCapture> LookBehind(params TokenGroup[] tokenIds) + { + return Items(this); + } + + public bool Is(params TokenGroup[] tokenIds) + { + return false; + } + + public bool IsNot(params TokenGroup[] tokenIds) + { + return true; + } + + public IOperandCapture Operand + { + get { return this; } + } + + public IOperandCapture LeftOperand + { + get { return this; } + } + + public IOperandCapture RightOperand + { + get { return this; } + } + + public bool HasOperand { get; } + public bool HasLeftOperand { get; } + public bool HasRightOperand { get; } + public object Production { get; } + } + + static TokenEndOfList EndOfList = new TokenEndOfList(); + } +} diff --git a/QtVsTools.RegExpr/expression/Renderer.cs b/QtVsTools.RegExpr/expression/Renderer.cs new file mode 100644 index 0000000..c79da8c --- /dev/null +++ b/QtVsTools.RegExpr/expression/Renderer.cs @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + public class Pattern + { + public RegExpr Expr { get; set; } + public string ExprRender { get; set; } + public Dictionary<string, Token> Tokens { get; set; } + public Token Root { get; set; } + } + + class Renderer + { + //////////////////////////////////////////////////////////////////////////////////////// + /// + /// RegExpr.Renderer.RenderPattern() + /// + //////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Transform the RegExpr representation of a regular expression into a pattern string + /// and a mapping of capture group id's into corresponding token definitions. + /// </summary> + /// <param name="rootExpr">RegExpr to render</param> + /// <param name="wsExpr">Default token white-space</param> + /// <returns>Pattern object containing pattern string and token map</returns> + public Pattern RenderPattern(RegExpr rootExpr, RegExpr wsExpr) + { + var pattern = new StringBuilder(); + var rootToken = Token.CreateRoot(); + var tokenStack = new Stack<Token>(); + tokenStack.Push(rootToken); + var tokens = new HashSet<Token>(); + + var stack = new Stack<StackFrame>(); + var mode = RenderMode.Default; + + stack.Push(rootExpr); + while (stack.Any()) { + var context = stack.Pop(); + + if (context.Expr == null) + continue; + + var expr = context.Expr; + IEnumerable<RegExpr> children = context.Children; + RegExpr parent = stack.Any() ? stack.Peek() : null; + + if (expr is Token token) + tokens.Add(token); + + if (children == null) { + children = expr.OnRender(wsExpr, parent, pattern, ref mode, tokenStack); + if (children != null && children.Any()) { + stack.Push(new StackFrame { Expr = expr, Children = children.Skip(1) }); + stack.Push(children.First()); + } + } else if (children.Any()) { + expr.OnRenderNext(wsExpr, parent, pattern, ref mode, tokenStack); + stack.Push(new StackFrame { Expr = expr, Children = children.Skip(1) }); + stack.Push(children.First()); + } else { + expr.OnRenderEnd(wsExpr, parent, pattern, ref mode, tokenStack); + } + } + + var tokensByCaptureId = tokens + .SelectMany(token => token.CaptureIds + .Select(captureId => new { Id = captureId, Token = token })) + .ToDictionary(idToken => idToken.Id, idToken => idToken.Token); + tokensByCaptureId.Add(ParseTree.KeyRoot, rootToken); + + return new Pattern + { + Expr = rootExpr, + ExprRender = pattern.ToString(), + Tokens = tokensByCaptureId, + Root = rootToken + }; + } + + class StackFrame + { + public RegExpr Expr { get; set; } + public IEnumerable<RegExpr> Children { get; set; } + + public static implicit operator StackFrame(RegExpr expr) + { + return new StackFrame { Expr = expr }; + } + + public static implicit operator RegExpr(StackFrame frame) + { + return (frame != null) ? frame.Expr : null; + } + } + } + } +} diff --git a/QtVsTools.RegExpr/parser/ParseTree.cs b/QtVsTools.RegExpr/parser/ParseTree.cs new file mode 100644 index 0000000..1ee054c --- /dev/null +++ b/QtVsTools.RegExpr/parser/ParseTree.cs @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + //////////////////////////////////////////////////////////////////////////////////////////// + /// + /// RegExpr.ParseTree + /// + //////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Result of processing input text with a pattern rendered from a RegExpr. + /// </summary> + /// <remarks> + /// Nodes in a ParseTree correspond to captured tokens. The parent-child relationship + /// between nodes reflects token embedding. + /// </remarks> + public class ParseTree + { + public Node Root { get; set; } + public const string KeyRoot = "0"; + + public class Node : IOperatorCapture, IOperandCapture + { + public string CaptureId { get; set; } + public string Value { get; set; } + public int Begin { get; set; } + public int End { get; set; } + public int GroupIdx { get; set; } + public int CaptureIdx { get; set; } + + class NodeComparer : IComparer<Node> + { + public int Compare(Node x, Node y) + { + return Comparer<int>.Default.Compare(x.Begin, y.Begin); + } + } + + static NodeComparer _Comparer = new NodeComparer(); + public static IComparer<Node> Comparer { get { return _Comparer; } } + + public Token Token { get; set; } + public string TokenId { get { return Token.Id; } } + + public object Production { get; set; } + + public Node Parent { get; set; } + + SortedList<int, Node> _ChildNodes = new SortedList<int, Node>(); + public SortedList<int, Node> ChildNodes { get { return _ChildNodes; } } + + ProductionObjects _ChildProductions = new ProductionObjects(); + public ProductionObjects ChildProductions { get { return _ChildProductions; } } + + public Queue<Node> TokenStream { get; set; } + public Stack<Node> OperatorStack { get; set; } + public Stack<Node> OperandStack { get; set; } + + IProductionRule _Rule = null; + public IProductionRule Rule + { + get + { + if (_Rule == null) + _Rule = Token.SelectRule(this); + return _Rule; + } + } + + public string Key + { + get + { + if (CaptureId == KeyRoot) + return KeyRoot; + return string.Format("{0}:{1}:{2}", CaptureId, Begin, End); + } + } + + public override string ToString() + { + return string.Format("{0}[{1}]", TokenId, Value); + } + + public static implicit operator ParseTree(Node node) + { + return new ParseTree { Root = node }; + } + + int SiblingIdx + { + get + { + if (Parent == null) + return 0; + return Parent.ChildNodes.IndexOfKey(Begin); + } + } + + int SiblingCount + { + get + { + if (Parent == null) + return 1; + return Parent.ChildNodes.Count; + } + } + + public bool IsFirst { get { return SiblingIdx == 0; } } + + public bool IsLast { get { return SiblingIdx == SiblingCount - 1; } } + + public IEnumerable<ITokenCapture> LookAhead(params TokenGroup[] ids) + { + if (Parent == null) + return Empty<ITokenCapture>(); + var lookAhead = Parent.ChildNodes.Values + .Skip(SiblingIdx + 1); + if (ids.Any()) + lookAhead = lookAhead.Where(x => ids.Any(g => g.Contains(x.TokenId))); + return lookAhead.Cast<ITokenCapture>().Concat(Items(EndOfList)); + } + + public IEnumerable<ITokenCapture> LookBehind(params TokenGroup[] ids) + { + if (Parent == null) + return Empty<ITokenCapture>(); + var lookBehind = Parent.ChildNodes.Values + .Take(SiblingIdx) + .Reverse(); + if (ids.Any()) + lookBehind = lookBehind.Where(x => ids.Any(g => g.Contains(x.TokenId))); + return lookBehind.Cast<ITokenCapture>().Concat(Items(EndOfList)); + } + + public bool Is(params TokenGroup[] tokenIds) + { + return tokenIds.Any(g => g.Contains(TokenId)); + } + + public bool IsNot(params TokenGroup[] tokenIds) + { + return !tokenIds.Any(g => g.Contains(TokenId)); + } + + public IOperandCapture Operand + { + get + { + if (Parent == null) + return EndOfList; + if (Parent.OperandStack == null) + return EndOfList; + if (!Parent.OperandStack.Any()) + return EndOfList; + return Parent.OperandStack.Peek(); + } + } + + public IOperandCapture LeftOperand + { + get + { + if (Parent == null) + return EndOfList; + if (Parent.OperandStack == null) + return EndOfList; + if (Parent.OperandStack.Count() < 2) + return EndOfList; + return Parent.OperandStack.Skip(1).First(); + } + } + + public IOperandCapture RightOperand + { + get + { + if (Parent == null) + return EndOfList; + if (Parent.OperandStack == null) + return EndOfList; + if (Parent.OperandStack.Count() < 2) + return EndOfList; + return Parent.OperandStack.Peek(); + } + } + + public bool HasOperand + { + get { return Operand != EndOfList; } + } + + public bool HasLeftOperand + { + get { return LeftOperand != EndOfList; } + } + + public bool HasRightOperand + { + get { return RightOperand != EndOfList; } + } + } + } + } +} diff --git a/QtVsTools.RegExpr/parser/Parser.cs b/QtVsTools.RegExpr/parser/Parser.cs new file mode 100644 index 0000000..f69cae4 --- /dev/null +++ b/QtVsTools.RegExpr/parser/Parser.cs @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + //////////////////////////////////////////////////////////////////////////////////////////// + /// + /// RegExpr.Parser + /// + //////////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Rendering of <see cref="RegExpr"/> + /// </summary> + public partial class Parser + { + Renderer Renderer { get; set; } + Pattern Pattern { get; set; } + public Regex Regex { get; private set; } + + internal Parser(RegExpr expr, RegExpr defaultTokenWs = null) + { + Renderer = new Renderer(); + Refresh(expr, defaultTokenWs); + } + + /// <summary> + /// Parse input text and return productions. + /// </summary> + /// <remarks> + /// The parsing procedure will first calculate the parse tree corresponding to the input + /// text, given the token data captured. The parse tree is then used to generate all + /// productions, according to the production rules defined for each token. + /// (see also <see cref="GetProductions(ParseTreeNode)"/>) + /// </remarks> + /// <param name="text">Text to be parsed.</param> + /// <returns>Productions by token id</returns> + public ProductionObjects Parse(string text) + { + var parseTree = GetParseTree(text); + return GetProductionObjects(parseTree); + } + + public void Refresh(RegExpr expr, RegExpr defaultTokenWs = null) + { + // Render Regex string + Pattern = Renderer.RenderPattern(expr, defaultTokenWs); + + // Compile Regex + Regex = new Regex(Pattern.ExprRender, RegexOptions.Multiline); + } + + /// <summary> + /// Parse input text using Regex and generate corresponding parse tree. + /// </summary> + /// <param name="text">Text to be parsed</param> + /// <returns>Parse tree</returns> + ParseTree GetParseTree(string text) + { + // Match regex pattern + var match = Regex.Match(text); + if (!match.Success || match.Length == 0) + throw new ParseErrorException(); + + // Flat list of parse-tree nodes, from Regex captures + var nodes = match.Groups.Cast<Group>() + .SelectMany((group, groupIdx) => group.Captures.Cast<Capture>() + .Where(capture => !string.IsNullOrEmpty(capture.Value)) + .Select((capture, captureIdx) => new ParseTree.Node + { + CaptureId = Regex.GroupNameFromNumber(groupIdx), + Token = Pattern.Tokens[Regex.GroupNameFromNumber(groupIdx)], + Value = capture.Value, + Begin = capture.Index, + End = capture.Index + capture.Length, + GroupIdx = groupIdx, + CaptureIdx = captureIdx, + })) + .OrderBy(c => c.Begin) + .ToList(); + + // Node list partitioned by token + var nodesByToken = nodes + .GroupBy(node => node.Token) + .ToDictionary(g => g.Key, g => g.ToArray()); + + foreach (var node in nodes.Where(n => n.Token != Pattern.Root)) { + // Get nodes captured by parent token + Token parentToken; + if (!node.Token.Parents.TryGetValue(node.CaptureId, out parentToken)) + throw new ParseErrorException("Unknown capture ID"); + ParseTree.Node[] parentNodes; + if (!nodesByToken.TryGetValue(parentToken, out parentNodes)) + throw new ParseErrorException("Missing parent nodes"); + // Find parent node + int idx = Array.BinarySearch(parentNodes, node, ParseTree.Node.Comparer); + if (idx < 0) { + idx = (~idx) - 1; + if (idx < 0) + throw new ParseErrorException("Parent node not found"); + } + // Attach to parent node + (node.Parent = parentNodes[idx]).ChildNodes.Add(node.Begin, node); + } + + // Return parse tree root + return nodesByToken[Pattern.Root].FirstOrDefault(); + } + } + + public class ParseErrorException : RegExprException + { + public ParseErrorException(string message = null) : base(message) { } + } + } +} diff --git a/QtVsTools.RegExpr/production/Production.cs b/QtVsTools.RegExpr/production/Production.cs new file mode 100644 index 0000000..8dc30cc --- /dev/null +++ b/QtVsTools.RegExpr/production/Production.cs @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + public partial class Parser + { + //////////////////////////////////////////////////////////////////////////////////////// + /// + /// RegExpr.Parser.GetProductionObjects() + /// + //////////////////////////////////////////////////////////////////////////////////////// + /// <summary> + /// Extract productions from a parse tree. + /// </summary> + /// <param name="root"> + /// Root node of parse tree, obtained from parsing an input text with a RegExpr + /// </param> + /// <returns>Productions by token id</returns> + ProductionObjects GetProductionObjects(ParseTree parseTree) + { + var outputProductions = new ProductionObjects(); + + var stack = new Stack<ParseTree.Node>(); + stack.Push(parseTree.Root); + while (stack.Any()) { + var node = stack.Pop(); + + // Depth-first traversal + if (node.TokenStream == null) { + node.TokenStream = new Queue<ParseTree.Node>(node.ChildNodes.Values); + node.OperandStack = new Stack<ParseTree.Node>(); + node.OperatorStack = new Stack<ParseTree.Node>(); + stack.Push(node); + continue; + } else if (node.TokenStream.Any()) { + var nextNode = node.TokenStream.Dequeue(); + stack.Push(node); + stack.Push(nextNode); + continue; + } + + if (node.Parent == null) + continue; + + var operatorStack = node.Parent.OperatorStack; + var operandStack = node.Parent.OperandStack; + var rule = node.Rule; + if (rule == null) { + // Default token (without rule definitions) + // just use captured value as production + node.Production = node.Value; + operandStack.Push(node); + } else if (rule.Delimiters != Delimiter.None) { + // Delimiter token + if (rule.Delimiters == Delimiter.Left) { + // if left delim, push to operator stack + operatorStack.Push(node); + } else { + // if right delim, unwind operator stack until left delim + UnwindOperatorStack(HaltUnwind.AtLeftDelimiter, + operatorStack, operandStack); + // set left delim as left operand, delimited expr as right operand + operandStack.ReverseTop(); + // execute delimiter rule + node.Production = rule.Execute(node); + operandStack.Push(node); + } + } else if (rule.Operands != Operand.None) { + // Operator token + // unwind operator stack until lower priority operator or empty + UnwindOperatorStack(HaltUnwind.AtLowerPriority, + operatorStack, operandStack, rule.Priority); + + // if operator needs left operand but none is available, error out + if (rule.Operands.HasFlag(Operand.Left) && !operandStack.Any()) + throw new ParseErrorException(); + + if (rule.Operands.HasFlag(Operand.Right)) { + // if needs right operand, push to operator stack + operatorStack.Push(node); + } else { + // if left operand only, execute rule immediately + node.Production = rule.Execute(node); + operandStack.Push(node); + } + } else { + // Captured value or embedded captures ("nullary operator") + // execute rule immediately + node.Production = rule.Execute(node); + operandStack.Push(node); + } + + if (node.IsLast) { + // Last token + // unwind operator stack until empty + UnwindOperatorStack(HaltUnwind.WhenEmpty, + operatorStack, operandStack); + + // get output from operand stack + foreach (var operand in operandStack.Reverse()) { + + // check if it's a dangling left delimiter + if (operand.Rule != null && operand.Rule.Delimiters == Delimiter.Left) + throw new ParseErrorException(); + + // add production to parent context + node.Parent.ChildProductions.Add(operand.TokenId, operand.Production); + + // add production to output list + outputProductions.Add(operand.TokenId, operand.Production); + } + operandStack.Clear(); + } + } + + return outputProductions; + } + + enum HaltUnwind + { + WhenEmpty, + AtLeftDelimiter, + AtLowerPriority + } + + void UnwindOperatorStack( + HaltUnwind haltingCondition, + Stack<ParseTree.Node> operatorStack, + Stack<ParseTree.Node> operandStack, + int priority = int.MinValue) + { + while (operatorStack.Any()) { + var node = operatorStack.Pop(); + Debug.Assert(node != null); + + var rule = node.Rule; + Debug.Assert(rule != null); + + if (haltingCondition == HaltUnwind.AtLeftDelimiter + && rule.Delimiters == Delimiter.Left + ) { + // Halting stack unwind: left delimiter found + // check if an operand (i.e. delimited expression) is available + if (!operandStack.Any()) + throw new ParseErrorException(); + + // execute left delimiter rule + node.Production = rule.Execute(node); + + // add to operands (will be picked up by right delimiter rule) + operandStack.Push(node); + return; + } + + if (haltingCondition == HaltUnwind.AtLowerPriority + && rule.Priority < priority + ) { + // Halting stack unwind: lower priority operator found + // push operator back into stack + operatorStack.Push(node); + return; + } + + // still haven't found what we're looking for; continue stack unwind + node.Production = rule.Execute(node); + operandStack.Push(node); + } + + // error-out if didn't find left delimiter + if (haltingCondition == HaltUnwind.AtLeftDelimiter) + throw new ParseErrorException(); + } + } + + /// <summary> + /// Collection of production objects, grouped by token ID + /// </summary> + public partial class ProductionObjects : IEnumerable<KeyValuePair<string, object>> + { + List<KeyValuePair<string, object>> Productions { get; set; } + Dictionary<string, List<object>> ProductionsByTokenId { get; set; } + + public ProductionObjects() + { + Productions = new List<KeyValuePair<string, object>>(); + ProductionsByTokenId = new Dictionary<string, List<object>>(); + } + + public void Add(string tokenId, object prodObj) + { + Productions.Add(new KeyValuePair<string, object>(tokenId, prodObj)); + List<object> prodObjs; + if (!ProductionsByTokenId.TryGetValue(tokenId, out prodObjs)) + ProductionsByTokenId.Add(tokenId, prodObjs = new List<object>()); + prodObjs.Add(prodObj); + } + + public IEnumerable<T> GetValues<T>(string tokenId) + { + if (string.IsNullOrEmpty(tokenId)) + return Empty<T>(); + + List<object> tokenProds; + if (!ProductionsByTokenId.TryGetValue(tokenId, out tokenProds)) + return Empty<T>(); + + return tokenProds + .Where(x => x != null && x is T) + .Select(x => (T)x); + } + + public IEnumerable<T> GetValues<T>(Enum tokenId) + { + return GetValues<T>(tokenId.ToString()); + } + + public IEnumerable<T> GetValues<T>(Token token) + { + return GetValues<T>(token.Id); + } + + public IEnumerable<T> GetValues<T>(ProductionRule<T> production) + { + return GetValues<T>(production.Token.Id); + } + + public IEnumerable<object> GetValues(string tokenId) + { + return GetValues<object>(tokenId); + } + + public IEnumerable<object> GetValues(Enum tokenId) + { + return GetValues<object>(tokenId.ToString()); + } + + public IEnumerable<object> GetValues(Token token) + { + return GetValues<object>(token.Id); + } + + public IEnumerable<object> GetValues(IProductionRule production) + { + return GetValues<object>(production.Token.Id); + } + + public object this[string tokenId, int index = 0] + { + get + { + return GetValues(tokenId).ElementAtOrDefault(index); + } + } + + public object this[Enum tokenId, int index = 0] + { + get + { + return this[tokenId.ToString(), index]; + } + } + + public IEnumerator<KeyValuePair<string, object>> GetEnumerator() + { + return ((IEnumerable<KeyValuePair<string, object>>)Productions).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<KeyValuePair<string, object>>)Productions).GetEnumerator(); + } + } + } +} diff --git a/QtVsTools.RegExpr/production/ProductionRule.cs b/QtVsTools.RegExpr/production/ProductionRule.cs new file mode 100644 index 0000000..8884aae --- /dev/null +++ b/QtVsTools.RegExpr/production/ProductionRule.cs @@ -0,0 +1,409 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + [Flags] + public enum Delimiter { None, Left, Right } + + [Flags] + public enum Operand { None, Left, Right } + + public interface IProductionRule + { + int Priority { get; } + RuleCallback.Selector Selector { get; } + RuleCallback.PreCondition PreCondition { get; } + Token Token { get; set; } + Delimiter Delimiters { get; } + Operand Operands { get; } + object Execute(ParseTree.Node node); + } + + public abstract class ProductionRule<T> : IProductionRule, IEnumerable + { + public int Priority { get; protected set; } + public RuleCallback.Selector Selector { get; set; } + public RuleCallback.PreCondition PreCondition { get; set; } + + public Token Token { get; set; } + public virtual Delimiter Delimiters { get { return Delimiter.None; } } + public virtual Operand Operands { get { return Operand.None; } } + + protected List<IRuleAction<T>> Actions = new List<IRuleAction<T>>(); + + protected void Init( + int priority, RuleCallback.Selector select, RuleCallback.PreCondition pre) + { + Priority = priority; + Selector = select; + PreCondition = pre; + } + + public void Add(IRuleAction<T> action) + { + Actions.Add(action); + } + + protected abstract object[] FetchOperands(Stack<ParseTree.Node> operandStack); + + protected virtual T DefaultProduction( + string capturedValue, + Stack<ParseTree.Node> operandStack, + ProductionObjects productions) + { + return CreateInstance(); + } + + protected virtual bool TestPreCondition( + ParseTree.Node node, + string capturedValue, + Stack<ParseTree.Node> operandStack, + ProductionObjects productions) + { + if (PreCondition == null) + return true; + return PreCondition(node); + } + + public object Execute(ParseTree.Node node) + { + if (PreCondition != null && !PreCondition(node)) + throw new ParseErrorException(); + + if (node.Parent == null) + return null; + + var operandStack = node.Parent.OperandStack; + var capturedValue = node.Value; + var productions = node.ChildProductions; + + // pop-out rule operands from operand stack + object[] ruleOperands = FetchOperands(operandStack); + + // calculate order of actions taking child productions into account + var actionScheduling = ScheduleActions(productions); + + // run actions in the calculated order + T production = default(T); + foreach (var actionSchedule in actionScheduling) { + var a = actionSchedule.Action; + var childProduction = actionSchedule.Production; + + // if production is null and action is update, init with default production + if (production == null && a.ActionInfo.ReturnType == typeof(void)) + production = DefaultProduction(capturedValue, operandStack, productions); + + // if action uses child production, use it as input instead of the rule operands + if (childProduction != null) + a.Execute(ref production, capturedValue, childProduction); + else + a.Execute(ref production, capturedValue, ruleOperands); + } + + // if no production was created by rule actions, create default production + if (production == null) + production = DefaultProduction(capturedValue, operandStack, productions); + + return production; + } + + class ActionSchedule + { + public IRuleAction<T> Action { get; set; } + public int ActionIndex { get; set; } + public object Production { get; set; } + public int ProductionIndex { get; set; } + } + + IEnumerable<ActionSchedule> ScheduleActions(ProductionObjects childProductions) + { + // actions with order of definition + var actionsOrder = Actions + .Select((a, aIdx) => new + { + Self = a, + Index = aIdx, + a.SourceTokenId + }); + var dependentActions = actionsOrder + .Where(a => !string.IsNullOrEmpty(a.SourceTokenId)); + var independentActions = actionsOrder + .Where(a => string.IsNullOrEmpty(a.SourceTokenId)); + + // child productions with order of creation + var productionsOrder = childProductions + .Select((p, pIdx) => new + { + Self = p.Value, + Index = pIdx, + TokenId = p.Key + }); + + // schedule actions that depend on child production: + // * actions x productions + // * sorted by production creation order (inverted) + // * and then by action definition order (inverted) + var dependentActionSchedules = dependentActions + .Join(productionsOrder, + a => a.SourceTokenId, p => p.TokenId, + (a, p) => new ActionSchedule + { + Action = a.Self, + ActionIndex = a.Index, + Production = p.Self, + ProductionIndex = p.Index, + }) + .OrderByDescending(ap => ap.ProductionIndex) + .ThenByDescending(ap => ap.ActionIndex); + + // insert independent actions in the right order + var scheduled = new Stack<ActionSchedule>(); + IEnumerable<ActionSchedule> toSchedule = dependentActionSchedules; + foreach (var a in independentActions.OrderByDescending(a => a.Index)) { + var scheduleAfter = toSchedule + .TakeWhile(ap => ap.ActionIndex > a.Index); + foreach (var ap in scheduleAfter) + scheduled.Push(ap); + scheduled.Push(new ActionSchedule { Action = a.Self }); + + toSchedule = toSchedule.Skip(scheduleAfter.Count()); + } + if (toSchedule.Any()) { + foreach (var ap in toSchedule) + scheduled.Push(ap); + } + + return scheduled; + } + + public IEnumerator GetEnumerator() + { + return Actions.GetEnumerator(); + } + + protected T CreateInstance() + { + var type = typeof(T); + + if (type.IsValueType) + return default(T); + + if (type == typeof(string)) + return (T)(object)string.Empty; + + if (!type.IsClass) + throw new InvalidOperationException("Not a class: " + type.Name); + + if (type.IsAbstract) + throw new InvalidOperationException("Abstract class: " + type.Name); + + if (type.ContainsGenericParameters) + throw new InvalidOperationException("Generic class: " + type.Name); + + var ctorInfo = ((TypeInfo)type).DeclaredConstructors + .Where(x => x.GetParameters().Length == 0) + .FirstOrDefault(); + + if (ctorInfo == null) + throw new InvalidOperationException("No default constructor: " + type.Name); + + return (T)ctorInfo.Invoke(new object[0]); + } + } + + public class Rule<T> : ProductionRule<T> + { + public Rule( + int priority = int.MaxValue, + RuleCallback.Selector select = null, + RuleCallback.PreCondition pre = null) + { + Init(priority, select, pre); + } + + protected override object[] FetchOperands(Stack<ParseTree.Node> operandStack) + { + return new object[] { }; + } + } + + public class PrefixRule<TOperand, T> : ProductionRule<T> + { + public override Operand Operands { get { return Operand.Right; } } + + public PrefixRule( + int priority = 0, + RuleCallback.Selector select = null, + RuleCallback.PreCondition pre = null) + { + Init(priority, select, pre); + } + + protected override object[] FetchOperands(Stack<ParseTree.Node> operandStack) + { + if (operandStack.Count < 1) + throw new ParseErrorException(); + + var operand = operandStack.Pop(); + if (!(operand.Production is TOperand)) + throw new ParseErrorException(); + + return new object[] { operand.Production }; + } + } + + public class PostfixRule<TOperand, T> : ProductionRule<T> + { + public override Operand Operands { get { return Operand.Left; } } + + public PostfixRule( + int priority = 0, + RuleCallback.Selector select = null, + RuleCallback.PreCondition pre = null) + { + Init(priority, select, pre); + } + + protected override object[] FetchOperands(Stack<ParseTree.Node> operandStack) + { + if (operandStack.Count < 1) + throw new ParseErrorException(); + + var operand = operandStack.Pop(); + if (!(operand.Production is TOperand)) + throw new ParseErrorException(); + + return new object[] { operand.Production }; + } + } + + public class InfixRule<TLeftOperand, TRightOperand, T> : ProductionRule<T> + { + public override Operand Operands { get { return Operand.Left | Operand.Right; } } + + public InfixRule( + int priority = 0, + RuleCallback.Selector select = null, + RuleCallback.PreCondition pre = null) + { + Init(priority, select, pre); + } + + protected override object[] FetchOperands(Stack<ParseTree.Node> operandStack) + { + if (operandStack.Count < 2) + throw new ParseErrorException(); + + var rightOperand = operandStack.Pop(); + if (!(rightOperand.Production is TRightOperand)) + throw new ParseErrorException(); + + var leftOperand = operandStack.Pop(); + if (!(leftOperand.Production is TLeftOperand)) + throw new ParseErrorException(); + + return new object[] { leftOperand.Production, rightOperand.Production }; + } + } + + public class LeftDelimiterRule<T> : ProductionRule<T> + { + public override Delimiter Delimiters { get { return Delimiter.Left; } } + + public LeftDelimiterRule( + int priority = int.MinValue, + RuleCallback.Selector select = null, + RuleCallback.PreCondition pre = null) + { + Init(priority, select, pre); + } + + protected override object[] FetchOperands(Stack<ParseTree.Node> operandStack) + { + return new object[] { }; + } + } + + public class RightDelimiterRule<TLeftDelim, TExpr, T> : ProductionRule<T> + { + public override Delimiter Delimiters { get { return Delimiter.Right; } } + + public RightDelimiterRule( + int priority = int.MaxValue, + RuleCallback.Selector select = null, + RuleCallback.PreCondition pre = null) + { + Init(priority, select, pre); + } + + protected override T DefaultProduction( + string capturedValue, + Stack<ParseTree.Node> operandStack, + ProductionObjects productions) + { + throw new ParseErrorException(); + } + + protected override object[] FetchOperands(Stack<ParseTree.Node> operandStack) + { + if (operandStack.Count < 2) + throw new ParseErrorException(); + + var delimitedExpr = operandStack.Pop(); + if (!(delimitedExpr.Production is TExpr)) + throw new ParseErrorException(); + + var leftDelimiter = operandStack.Pop(); + if (!(leftDelimiter.Production is TLeftDelim)) + throw new ParseErrorException(); + + return new object[] { leftDelimiter.Production, delimitedExpr.Production }; + } + } + + public static class RuleCallback + { + public delegate bool Selector(ITokenCapture token); + public delegate bool PreCondition(IOperatorCapture capture); + } + + public const RuleCallback.Selector Default = null; + } +} diff --git a/QtVsTools.RegExpr/production/ProductionRuleAction.cs b/QtVsTools.RegExpr/production/ProductionRuleAction.cs new file mode 100644 index 0000000..71662d8 --- /dev/null +++ b/QtVsTools.RegExpr/production/ProductionRuleAction.cs @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Reflection; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + public interface IRuleAction<T> + { + int NumOperands { get; } + string SourceTokenId { get; set; } + bool Execute(ref T productionObj, string capturedValue, params object[] operandObjs); + MethodInfo AssertInfo { get; } + MethodInfo ActionInfo { get; } + } + + public class RuleAction<T, T1, T2> : IRuleAction<T> + { + public Delegate Assert { get; set; } + public Delegate Action { get; set; } + + public MethodInfo AssertInfo { get { return Assert != null ? Assert.Method : null; } } + public MethodInfo ActionInfo { get { return Action != null ? Action.Method : null; } } + + public string SourceTokenId { get; set; } + + static readonly int _NumOperands + = (typeof(T1) != typeof(Void) ? 1 : 0) + + (typeof(T2) != typeof(Void) ? 1 : 0); + + public int NumOperands { get { return _NumOperands; } } + + bool TestAssert(T prod, string value, T1 x, T2 y) + { + if (Assert == null) + return true; + + else if (Assert is CaptureCallback.Predicate) + return ((CaptureCallback.Predicate)Assert)(value); + + else if (Assert is UnaryCallback.Predicate<T1>) + return ((UnaryCallback.Predicate<T1>)Assert)(x); + + else if (Assert is UnaryCallback.Predicate<T, T1>) + return ((UnaryCallback.Predicate<T, T1>)Assert)(prod, x); + + else if (Assert is BinaryCallback.Predicate<T1, T2>) + return ((BinaryCallback.Predicate<T1, T2>)Assert)(x, y); + + else if (Assert is BinaryCallback.Predicate<T, T1, T2>) + return ((BinaryCallback.Predicate<T, T1, T2>)Assert)(prod, x, y); + + else + throw new InvalidOperationException("Incompatible assert callback."); + } + + void RunAction(ref T prod, string value, T1 x, T2 y) + { + if (Action == null) + throw new InvalidOperationException("Missing action callback."); + + else if (Action is CaptureCallback.Create<T>) + prod = ((CaptureCallback.Create<T>)Action)(value); + + else if (Action is UnaryCallback.Create<T, T1>) + prod = ((UnaryCallback.Create<T, T1>)Action)(x); + + else if (Action is BinaryCallback.Create<T, T1, T2>) + prod = ((BinaryCallback.Create<T, T1, T2>)Action)(x, y); + + else if (Action is UnaryCallback.Transform<T, T1>) + prod = ((UnaryCallback.Transform<T, T1>)Action)(prod, x); + + else if (Action is BinaryCallback.Transform<T, T1, T2>) + prod = ((BinaryCallback.Transform<T, T1, T2>)Action)(prod, x, y); + + else if (Action is UnaryCallback.Update<T, T1>) + ((UnaryCallback.Update<T, T1>)Action)(prod, x); + + else if (Action is BinaryCallback.Update<T, T1, T2>) + ((BinaryCallback.Update<T, T1, T2>)Action)(prod, x, y); + + else if (Action is UnaryCallback.Error<T, T1>) + throw new ErrorException(((UnaryCallback.Error<T, T1>)Action)(prod, x)); + + else if (Action is BinaryCallback.Error<T, T1, T2>) + throw new ErrorException(((BinaryCallback.Error<T, T1, T2>)Action)(prod, x, y)); + + else + throw new InvalidOperationException("Incompatible action callback."); + } + + bool GetOperand<TOperand>(out TOperand x, object[] operands, ref int idx) + { + x = default(TOperand); + if (typeof(TOperand) == typeof(Void)) + return true; + if (operands.Length <= idx) + return false; + object operandObj = operands[idx++]; + if (!(operandObj is TOperand)) + return false; + x = (TOperand)operandObj; + return true; + } + + public bool Execute(ref T prod, string value, params object[] operands) + { + int idx = 0; + T1 x; + T2 y; + if (!GetOperand(out x, operands, ref idx)) + return false; + if (!GetOperand(out y, operands, ref idx)) + return false; + + if (!TestAssert(prod, value, x, y)) + return false; + + RunAction(ref prod, value, x, y); + return true; + } + } + + public class RuleAction<T, T1> : RuleAction<T, T1, Void> { } + + public class RuleAction<T> : RuleAction<T, Void, Void> { } + + /////////////////////////////////////////////////////////////////////////////////////////// + #region Capture + + public static RuleAction<T> Capture<T>( + CaptureCallback.Predicate p, CaptureCallback.Create<T> a) + { return new RuleAction<T> { Assert = p, Action = a }; } + + public static RuleAction<T> Capture<T>( + CaptureCallback.Create<T> a) + { return new RuleAction<T> { Assert = null, Action = a }; } + + #endregion Capture + + /////////////////////////////////////////////////////////////////////////////////////////// + #region Create + + public static RuleAction<T, T1> Create<T, T1>( + string sid, UnaryCallback.Predicate<T1> p, UnaryCallback.Create<T, T1> a) + { return new RuleAction<T, T1> { SourceTokenId = sid, Assert = p, Action = a }; } + + public static RuleAction<T, T1> Create<T, T1>( + Enum sid, UnaryCallback.Predicate<T1> p, UnaryCallback.Create<T, T1> a) + { return Create(sid.ToString(), p, a); } + + public static RuleAction<T, T1> Create<T, T1>( + string sid, UnaryCallback.Create<T, T1> a) + { return Create(sid, null, a); } + + public static RuleAction<T, T1> Create<T, T1>( + Enum sid, UnaryCallback.Create<T, T1> a) + { return Create(sid, null, a); } + + public static RuleAction<T, T1> Create<T, T1>( + UnaryCallback.Predicate<T1> p, UnaryCallback.Create<T, T1> a) + { return Create(string.Empty, p, a); } + + public static RuleAction<T, T1> Create<T, T1>( + UnaryCallback.Create<T, T1> a) + { return Create(string.Empty, null, a); } + + public static RuleAction<T, T1, T2> Create<T, T1, T2>( + BinaryCallback.Predicate<T1, T2> p, BinaryCallback.Create<T, T1, T2> a) + { return new RuleAction<T, T1, T2> { Assert = p, Action = a }; } + + public static RuleAction<T, T1, T2> Create<T, T1, T2>( + BinaryCallback.Create<T, T1, T2> a) + { return Create(null, a); } + + #endregion Create + + /////////////////////////////////////////////////////////////////////////////////////////// + #region Transform + + public static RuleAction<T, T1> Transform<T, T1>( + string sid, UnaryCallback.Predicate<T, T1> p, UnaryCallback.Transform<T, T1> a) + { return new RuleAction<T, T1> { SourceTokenId = sid, Assert = p, Action = a }; } + + public static RuleAction<T, T1> Transform<T, T1>( + Enum sid, UnaryCallback.Predicate<T, T1> p, UnaryCallback.Transform<T, T1> a) + { return Transform(sid.ToString(), p, a); } + + public static RuleAction<T, T1> Transform<T, T1>( + string sid, UnaryCallback.Transform<T, T1> a) + { return Transform(sid, null, a); } + + public static RuleAction<T, T1> Transform<T, T1>( + Enum sid, UnaryCallback.Transform<T, T1> a) + { return Transform(sid, null, a); } + + public static RuleAction<T, T1> Transform<T, T1>( + UnaryCallback.Transform<T, T1> a) + { return Transform(string.Empty, null, a); } + + public static RuleAction<T, T1, T2> Transform<T, T1, T2>( + BinaryCallback.Predicate<T, T1, T2> p, BinaryCallback.Transform<T, T1, T2> a) + { return new RuleAction<T, T1, T2> { Assert = p, Action = a }; } + + public static RuleAction<T, T1, T2> Transform<T, T1, T2>( + BinaryCallback.Transform<T, T1, T2> a) + { return Transform(null, a); } + + #endregion Transform + + /////////////////////////////////////////////////////////////////////////////////////////// + #region Update + + public static RuleAction<T, T1> Update<T, T1>( + string sid, UnaryCallback.Predicate<T, T1> p, UnaryCallback.Update<T, T1> a) + { return new RuleAction<T, T1> { SourceTokenId = sid, Assert = p, Action = a }; } + + public static RuleAction<T, T1> Update<T, T1>( + Enum sid, UnaryCallback.Predicate<T, T1> p, UnaryCallback.Update<T, T1> a) + { return Update(sid.ToString(), p, a); } + + public static RuleAction<T, T1> Update<T, T1>( + string sid, UnaryCallback.Update<T, T1> a) + { return Update(sid, null, a); } + + public static RuleAction<T, T1> Update<T, T1>( + Enum sid, UnaryCallback.Update<T, T1> a) + { return Update(sid, null, a); } + + public static RuleAction<T, T1> Update<T, T1>( + UnaryCallback.Update<T, T1> a) + { return Update(string.Empty, null, a); } + + public static RuleAction<T, T1, T2> Update<T, T1, T2>( + BinaryCallback.Predicate<T, T1, T2> p, BinaryCallback.Update<T, T1, T2> a) + { return new RuleAction<T, T1, T2> { Assert = p, Action = a }; } + + public static RuleAction<T, T1, T2> Update<T, T1, T2>( + BinaryCallback.Update<T, T1, T2> a) + { return Update(null, a); } + + #endregion Update + + /////////////////////////////////////////////////////////////////////////////////////////// + #region Error + + public static RuleAction<T, T1> Error<T, T1>( + string sid, UnaryCallback.Predicate<T, T1> p, UnaryCallback.Error<T, T1> a) + { return new RuleAction<T, T1> { SourceTokenId = sid, Assert = p, Action = a }; } + + public static RuleAction<T, T1> Error<T, T1>( + Enum sid, UnaryCallback.Predicate<T, T1> p, UnaryCallback.Error<T, T1> a) + { return Error(sid.ToString(), p, a); } + + public static RuleAction<T, T1> Error<T, T1>( + string sid, UnaryCallback.Error<T, T1> a) + { return Error(sid, null, a); } + + public static RuleAction<T, T1> Error<T, T1>( + Enum sid, + UnaryCallback.Error<T, T1> a) + { return Error(sid, null, a); } + + public static RuleAction<T, T1> Error<T, T1>( + UnaryCallback.Predicate<T, T1> p, UnaryCallback.Error<T, T1> a) + { return Error(string.Empty, p, a); } + + public static RuleAction<T, T1> Error<T, T1>( + UnaryCallback.Error<T, T1> a) + { return Error(string.Empty, null, a); } + + public static RuleAction<T, T1, T2> Error<T, T1, T2>( + BinaryCallback.Predicate<T, T1, T2> p, BinaryCallback.Error<T, T1, T2> a) + { return new RuleAction<T, T1, T2> { Assert = p, Action = a }; } + + public static RuleAction<T, T1, T2> Error<T, T1, T2>( + BinaryCallback.Error<T, T1, T2> a) + { return Error(null, a); } + + public class ErrorException : RegExprException + { public ErrorException(string message = null) : base(message) { } } + + #endregion Error + + /////////////////////////////////////////////////////////////////////////////////////////// + #region Callbacks + + public static class CaptureCallback + { + public delegate bool Predicate(string capture); + public delegate T Create<T>(string capture); + } + + public static class UnaryCallback + { + public delegate bool Predicate<T1>(T1 operand1); + public delegate bool Predicate<T, T1>(T obj, T1 operand1); + public delegate T Create<T, T1>(T1 operand1); + public delegate T Transform<T, T1>(T obj, T1 operand1); + public delegate void Update<T, T1>(T obj, T1 operand1); + public delegate string Error<T, T1>(T obj, T1 operand1); + } + + public static class BinaryCallback + { + public delegate bool Predicate<T1, T2>(T1 operand1, T2 operand2); + public delegate bool Predicate<T, T1, T2>(T obj, T1 operand1, T2 operand2); + public delegate T Create<T, T1, T2>(T1 operand1, T2 operand2); + public delegate T Transform<T, T1, T2>(T obj, T1 operand1, T2 operand2); + public delegate void Update<T, T1, T2>(T obj, T1 operand1, T2 operand2); + public delegate string Error<T, T1, T2>(T obj, T1 operand1, T2 operand2); + } + + #endregion Callbacks + } +} diff --git a/QtVsTools.RegExpr/utils/Consts.cs b/QtVsTools.RegExpr/utils/Consts.cs new file mode 100644 index 0000000..4835958 --- /dev/null +++ b/QtVsTools.RegExpr/utils/Consts.cs @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QtVsTools.SyntaxAnalysis +{ + using static CharClass; + + public abstract partial class RegExpr + { + /// <summary><![CDATA[ + /// Equivalent to: [\w] + /// ]]></summary> + public static CharClassLiteral CharWord + { get { return new CharClassLiteral { LiteralChars = @"\w" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: [\w]* + /// ]]></summary> + public static RegExpr Word + { get { return CharWord.Repeat(); } } + + /// <summary><![CDATA[ + /// Equivalent to: [\d] + /// ]]></summary> + public static CharClassLiteral CharDigit + { get { return new CharClassLiteral { LiteralChars = @"\d" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: [\d]* + /// ]]></summary> + public static RegExpr Number + { get { return CharDigit.Repeat(); } } + + /// <summary><![CDATA[ + /// Equivalent to: [\r] + /// ]]></summary> + public static CharClassLiteral CharCr + { get { return new CharClassLiteral { LiteralChars = @"\r" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: [\n] + /// ]]></summary> + public static CharClassLiteral CharLf + { get { return new CharClassLiteral { LiteralChars = @"\n" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: [\s] + /// ]]></summary> + public static CharClassLiteral CharSpace + { get { return new CharClassLiteral { LiteralChars = @"\s" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: [\S] + /// ]]></summary> + public static CharClassLiteral CharNonSpace + { get { return new CharClassLiteral { LiteralChars = @"\S" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: [\r\n] + /// ]]></summary> + public static CharClassSet CharVertSpace + { get { return CharSet[CharCr + CharLf]; } } + + /// <summary><![CDATA[ + /// Equivalent to: [^\S\r\n] + /// ]]></summary> + public static CharClassSet CharHorizSpace + { get { return CharSet[~(CharNonSpace + CharVertSpace)]; } } + + /// <summary><![CDATA[ + /// Equivalent to: . + /// ]]></summary> + public static RegExprLiteral AnyChar + { get { return new RegExprLiteral { LiteralExpr = "." }; } } + + /// <summary><![CDATA[ + /// Equivalent to: ^ + /// ]]></summary> + public static RegExprLiteral StartOfLine + { get { return new RegExprLiteral { LiteralExpr = "^" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: $ + /// ]]></summary> + public static RegExprLiteral EndOfLine + { get { return new RegExprLiteral { LiteralExpr = "$" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: \A + /// ]]></summary> + public static RegExprLiteral StartOfFile + { get { return new RegExprLiteral { LiteralExpr = @"\A" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: \z + /// ]]></summary> + public static RegExprLiteral EndOfFile + { get { return new RegExprLiteral { LiteralExpr = @"\z" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: \r?\n + /// ]]></summary> + public static RegExprSequence LineBreak + { get { return CharCr.Optional() & CharLf; } } + + /// <summary><![CDATA[ + /// Equivalent to: [\s]* + /// ]]></summary> + public static RegExpr Space + { get { return CharSpace.Repeat(); } } + + /// <summary><![CDATA[ + /// Equivalent to: [\S]* + /// ]]></summary> + public static RegExpr NonSpace + { get { return CharNonSpace.Repeat(); } } + + /// <summary><![CDATA[ + /// Equivalent to: [\r\n]* + /// ]]></summary> + public static RegExpr VertSpace + { get { return CharVertSpace.Repeat(); } } + + /// <summary><![CDATA[ + /// Equivalent to: [^\S\r\n]* + /// ]]></summary> + public static RegExpr HorizSpace + { get { return CharHorizSpace.Repeat(); } } + + /// <summary><![CDATA[ + /// Equivalent to: [^\r\n]* + /// ]]></summary> + public static RegExpr Line + { get { return CharSet[~CharVertSpace].Repeat(); } } + + /// <summary><![CDATA[ + /// Equivalent to: (?i) + /// ]]></summary> + public static RegExprLiteral IgnoreCase + { get { return new RegExprLiteral { LiteralExpr = @"(?i)" }; } } + + /// <summary><![CDATA[ + /// Equivalent to: (?-i) + /// ]]></summary> + public static RegExprLiteral SenseCase + { get { return new RegExprLiteral { LiteralExpr = @"(?-i)" }; } } + + /// <summary> + /// Applies the same whitespace skipping rules as tokens, but does not any capture text. + /// </summary> + public static RegExpr SkipWs + { get { return new Token(); } } + + static CharExprBuilder _Char = new CharExprBuilder(); + public static CharExprBuilder Char { get { return _Char; } } + public static CharExprBuilder Chars { get { return _Char; } } + + static CharSetExprBuilder _CharSet = new CharSetExprBuilder(); + public static CharSetExprBuilder CharSet { get { return _CharSet; } } + + static CharSetRawExprBuilder _CharSetRaw = new CharSetRawExprBuilder(); + public static CharSetRawExprBuilder CharSetRaw { get { return _CharSetRaw; } } + + static AssertExprBuilder _LookAhead = new AssertExprBuilder(AssertLookAhead); + public static AssertExprBuilder LookAhead { get { return _LookAhead; } } + + static AssertExprBuilder _LookBehind = new AssertExprBuilder(AssertLookBehind); + public static AssertExprBuilder LookBehind { get { return _LookBehind; } } + + public const SkipWhitespace SkipWs_Disable = SkipWhitespace.Disable; + } +} diff --git a/QtVsTools.RegExpr/utils/Utils.cs b/QtVsTools.RegExpr/utils/Utils.cs new file mode 100644 index 0000000..f4af1c2 --- /dev/null +++ b/QtVsTools.RegExpr/utils/Utils.cs @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace QtVsTools.SyntaxAnalysis +{ + public abstract partial class RegExpr + { + const string MetaChars = @"[]\/#^$.|?*+(){}-"; + + protected static string Escape(string s) + { + return new string(s.SelectMany(c => + MetaChars.Contains(c) ? Items('\\', c) : + (c == ' ') ? "\\x20".Cast<char>() : + (c == '\t') ? "\\t".Cast<char>() : + (c == '\r') ? "\\r".Cast<char>() : + (c == '\n') ? "\\n".Cast<char>() : + Items(c)).ToArray()); + } + + public static bool NeedsGroup(string literal) + { + if (literal.Length == 1) + return false; + if (literal.Length == 2 && literal.StartsWith(@"\")) + return false; + if (literal.Length == 3 && literal.StartsWith(@"\c")) + return false; + if (literal.Length == 4 && literal.StartsWith(@"\x")) + return false; + if (literal.Length == 6 && literal.StartsWith(@"\u")) + return false; + return true; + } + + internal T As<T>() + where T : RegExpr + { + if (this is T) + return this as T; + else + throw new InvalidCastException(); + } + + internal static IEnumerable<T> Empty<T>() + { + return new T[] { }; + } + + internal static IEnumerable<T> Items<T>(params T[] items) + { + return items; + } + + public sealed class Void { } + } + + public static class RegExprExtensions + { + public static void ReverseTop<T>(this Stack<T> stack, int count = 2) + { + if (count < 2 || stack.Count < count) + return; + var items = Enumerable.Repeat(stack, count).Select(s => s.Pop()).ToList(); + items.ForEach(item => stack.Push(item)); + } + } +} diff --git a/QtVsTools.Wizards/Properties/AssemblyInfo.cs b/QtVsTools.Wizards/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6a77927 --- /dev/null +++ b/QtVsTools.Wizards/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Qt Project Wizard")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6ac58c89-d2cc-4b96-9906-e6c66fe2eb54")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/QtVsTools.Wizards/QtVsTools.Wizards.csproj b/QtVsTools.Wizards/QtVsTools.Wizards.csproj new file mode 100644 index 0000000..0b333de --- /dev/null +++ b/QtVsTools.Wizards/QtVsTools.Wizards.csproj @@ -0,0 +1,250 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <SchemaVersion>2.0</SchemaVersion> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <RootNamespace>QtVsTools.Wizards</RootNamespace> + <AssemblyName>QtVsTools.Wizards</AssemblyName> + <ProjectGuid>{8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}</ProjectGuid> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>Resources\QtProjectWizard.ico</ApplicationIcon> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + <Reference Include="System" /> + <Reference Include="System.Drawing" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="PresentationCore" /> + <Reference Include="PresentationFramework" /> + <Reference Include="WindowsBase" /> + <Reference Include="Microsoft.VisualStudio.VCCodeModel" /> + <Reference Include="Microsoft.VisualStudio.ExtensionsExplorer.UI, Version=$(VisualStudioVersion).0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>$(DevEnvDir)PrivateAssemblies\Microsoft.VisualStudio.ExtensionsExplorer.UI.dll</HintPath> + </Reference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="Microsoft.VisualStudio.SDK" + Version="$(Version_Microsoft_VisualStudio_SDK)" ExcludeAssets="runtime" /> + <PackageReference Include="Microsoft.VSSDK.BuildTools" + Version="$(Version_Microsoft_VSSDK_BuildTools)" /> + <PackageReference Include="Newtonsoft.Json" + Version="$(Version_Newtonsoft_Json)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='17.0'"> + <PackageReference Include="$(Name_Microsoft_VisualStudio_VCProjectEngine)" + Version="$(Version_Microsoft_VisualStudio_VCProjectEngine)" /> + <PackageReference Include="$(Name_Microsoft_VisualStudio_TemplateWizardInterface)" + Version="$(Version_Microsoft_VisualStudio_TemplateWizardInterface)" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='16.0'"> + <Reference Include="Microsoft.VisualStudio.TemplateWizardInterface" /> + </ItemGroup> + <ItemGroup Condition="'$(VisualStudioVersion)'=='15.0'"> + <Reference Include="Microsoft.VisualStudio.VCProjectEngine" /> + <Reference Include="Microsoft.VisualStudio.TemplateWizardInterface" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="..\QtVsTools.Core\QtVsTools.Core.csproj"> + <Project>{2621ad55-c4e9-4884-81e9-da0d00b4c6e5}</Project> + <Name>QtVsTools.Core</Name> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <Content Include="Resources\QtProjectWizard.ico" /> + <Resource Include="Resources\medium.png"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Resource> + <Resource Include="Resources\Qt-logo-small.png"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Resource> + <Resource Include="Resources\small.png"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Resource> + </ItemGroup> + <ItemGroup> + <Compile Include="Wizards\ClassWizard\AddClassPage.xaml.cs"> + <DependentUpon>AddClassPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ClassWizard\AddClassWizard.cs" /> + <Compile Include="Wizards\ClassWizard\Class.cs" /> + <Compile Include="Wizards\ClassWizard\ClassKind.cs" /> + <Compile Include="Wizards\ProjectWizard\ConfigPage.xaml.cs"> + <DependentUpon>ConfigPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ProjectWizard\ProjectTemplateWizard.cs" /> + <Compile Include="Wizards\ProjectWizard\Quick\QuickWizard.cs" /> + <Compile Include="Wizards\Util\ClassNameValidationRule.cs" /> + <Compile Include="Wizards\ProjectWizard\Console\ConsoleWizard.cs" /> + <Compile Include="Wizards\ClassWizard\Core\CoreClassPage.xaml.cs"> + <DependentUpon>CoreClassPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ClassWizard\Core\CoreClassWizard.cs" /> + <Compile Include="Wizards\ProjectWizard\Designer\DesignerPage.xaml.cs"> + <DependentUpon>DesignerPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ProjectWizard\Designer\DesignerWizard.cs" /> + <Compile Include="Wizards\Util\FileExistsInFilterValidationRule.cs" /> + <Compile Include="Wizards\Util\FileNameValidationRule.cs" /> + <Compile Include="Wizards\ClassWizard\Gui\GuiClassPage.xaml.cs"> + <DependentUpon>GuiClassPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ClassWizard\Gui\GuiClassWizard.cs" /> + <Compile Include="Wizards\ProjectWizard\Gui\GuiPage.xaml.cs"> + <DependentUpon>GuiPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ProjectWizard\Gui\GuiWizard.cs" /> + <Compile Include="Wizards\ProjectWizard\Empty\EmptyWizard.cs" /> + <Compile Include="Wizards\ClassWizard\IClassWizard.cs" /> + <Compile Include="Wizards\WizardIntroPage.xaml.cs"> + <DependentUpon>WizardIntroPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ProjectWizard\Library\LibraryClassPage.xaml.cs"> + <DependentUpon>LibraryClassPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ProjectWizard\Library\LibraryWizard.cs" /> + <Compile Include="Wizards\Util\NativeMethods.cs" /> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + <Compile Include="Wizards\ProjectWizard\Server\ServerPage.xaml.cs"> + <DependentUpon>ServerPage.xaml</DependentUpon> + </Compile> + <Compile Include="Wizards\ProjectWizard\Server\ServerWizard.cs" /> + <Compile Include="Wizards\Util\SortComboBoxItem.cs" /> + <Compile Include="Wizards\ClassWizard\UiClassInclusion.cs" /> + <Compile Include="Wizards\Util\UnsafeNativeMethods.cs" /> + <Compile Include="Wizards\Util\VCLanguageManagerValidationRule.cs" /> + <Compile Include="Wizards\WizardData.cs" /> + <Compile Include="Wizards\WizardPage.cs" /> + <Compile Include="Wizards\WizardResult.cs" /> + <Compile Include="Wizards\WizardWindow.xaml.cs"> + <DependentUpon>WizardWindow.xaml</DependentUpon> + </Compile> + <Page Include="Wizards\ClassWizard\AddClassPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\ClassWizard\Core\CoreClassPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\ProjectWizard\Designer\DesignerPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\ClassWizard\Gui\GuiClassPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\ProjectWizard\Gui\GuiPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\ProjectWizard\ConfigPage.xaml"> + <Generator>MSBuild:Compile</Generator> + <SubType>Designer</SubType> + </Page> + <Page Include="Wizards\WizardIntroPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\ProjectWizard\Library\LibraryClassPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Resources\ExpanderStyle.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\ProjectWizard\Server\ServerPage.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + <Page Include="Wizards\WizardWindow.xaml"> + <SubType>Designer</SubType> + <Generator>MSBuild:Compile</Generator> + </Page> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> \ No newline at end of file diff --git a/QtVsTools.Wizards/Resources/ExpanderStyle.xaml b/QtVsTools.Wizards/Resources/ExpanderStyle.xaml new file mode 100644 index 0000000..c0cdd63 --- /dev/null +++ b/QtVsTools.Wizards/Resources/ExpanderStyle.xaml @@ -0,0 +1,173 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:ee="clr-namespace:Microsoft.VisualStudio.ExtensionsExplorer.UI;assembly=Microsoft.VisualStudio.ExtensionsExplorer.UI"> + + <SolidColorBrush x:Key="Expander.Static.Arrow.GlyphBrush" + Color="#444" /> + <Style x:Key="ExpanderHeaderFocusVisual"> + <Setter Property="Control.Template"> + <Setter.Value> + <ControlTemplate> + <Border> + <Rectangle Margin="0" + SnapsToDevicePixels="true" + Stroke="Black" + StrokeThickness="1" + StrokeDashArray="1 2" /> + </Border> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + + <Style x:Key="ExpanderDownHeaderStyle" + TargetType="{x:Type ToggleButton}"> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type ToggleButton}"> + <Border Padding="{TemplateBinding Padding}"> + <Grid Background="Transparent" + SnapsToDevicePixels="False"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="19" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Path x:Name="ExpandPath" + HorizontalAlignment="Center" + VerticalAlignment="Center" + SnapsToDevicePixels="false" + Stroke="{StaticResource Expander.Static.Arrow.GlyphBrush}" + Data="M4,0L8,4 4,8z" /> + <ContentPresenter Grid.Column="1" + HorizontalAlignment="Left" + Margin="4,0,0,0" + RecognizesAccessKey="True" + SnapsToDevicePixels="True" + VerticalAlignment="Center" /> + </Grid> + </Border> + <ControlTemplate.Triggers> + <Trigger Property="IsChecked" + Value="true"> + <Setter Property="Data" + TargetName="ExpandPath" + Value="M8,2.3L8,8 2.3,8z" /> + <Setter Property="Fill" + TargetName="ExpandPath" + Value="{StaticResource Expander.Static.Arrow.GlyphBrush}" /> + </Trigger> + </ControlTemplate.Triggers> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + <Style x:Key="ExpanderStyle" + TargetType="{x:Type Expander}"> + <Setter Property="Foreground" + Value="{DynamicResource {x:Static ee:ColorResources.WonderbarTextBrushKey}}" /> + <Setter Property="Background" + Value="Transparent" /> + <Setter Property="HorizontalContentAlignment" + Value="Stretch" /> + <Setter Property="VerticalContentAlignment" + Value="Stretch" /> + <Setter Property="BorderBrush" + Value="Transparent" /> + <Setter Property="BorderThickness" + Value="1" /> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="{x:Type Expander}"> + <Border BorderBrush="{TemplateBinding BorderBrush}" + BorderThickness="{TemplateBinding BorderThickness}" + Background="{TemplateBinding Background}" + CornerRadius="3" + SnapsToDevicePixels="true"> + <DockPanel> + <ToggleButton x:Name="HeaderSite" + ContentTemplate="{TemplateBinding HeaderTemplate}" + ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" + Content="{TemplateBinding Header}" + DockPanel.Dock="Top" + Foreground="{TemplateBinding Foreground}" + FontWeight="{TemplateBinding FontWeight}" + FocusVisualStyle="{StaticResource ExpanderHeaderFocusVisual}" + FontStyle="{TemplateBinding FontStyle}" + FontStretch="{TemplateBinding FontStretch}" + FontSize="{TemplateBinding FontSize}" + FontFamily="{TemplateBinding FontFamily}" + HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" + IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" + Margin="1" + MinWidth="0" + MinHeight="0" + Padding="{TemplateBinding Padding}" + Style="{StaticResource ExpanderDownHeaderStyle}" + VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" /> + <ContentPresenter x:Name="ExpandSite" + DockPanel.Dock="Bottom" + Focusable="false" + HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" + Margin="{TemplateBinding Padding}" + Visibility="Collapsed" + VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> + </DockPanel> + </Border> + <ControlTemplate.Triggers> + <Trigger Property="IsExpanded" + Value="true"> + <Setter Property="Visibility" + TargetName="ExpandSite" + Value="Visible" /> + </Trigger> + <Trigger Property="ExpandDirection" + Value="Left"> + <Setter Property="DockPanel.Dock" + TargetName="ExpandSite" + Value="Left" /> + <Setter Property="DockPanel.Dock" + TargetName="HeaderSite" + Value="Right" /> + </Trigger> + <Trigger Property="IsEnabled" + Value="false"> + <Setter Property="Foreground" + Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" /> + </Trigger> + </ControlTemplate.Triggers> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> + +</ResourceDictionary> diff --git a/QtVsTools.Wizards/Resources/Qt-logo-small.png b/QtVsTools.Wizards/Resources/Qt-logo-small.png new file mode 100644 index 0000000..ce0adfb --- /dev/null +++ b/QtVsTools.Wizards/Resources/Qt-logo-small.png Binary files differ diff --git a/QtVsTools.Wizards/Resources/QtProjectWizard.ico b/QtVsTools.Wizards/Resources/QtProjectWizard.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/QtVsTools.Wizards/Resources/QtProjectWizard.ico Binary files differ diff --git a/QtVsTools.Wizards/Resources/medium.png b/QtVsTools.Wizards/Resources/medium.png new file mode 100644 index 0000000..2aaf7ff --- /dev/null +++ b/QtVsTools.Wizards/Resources/medium.png Binary files differ diff --git a/QtVsTools.Wizards/Resources/small.png b/QtVsTools.Wizards/Resources/small.png new file mode 100644 index 0000000..c3ee062 --- /dev/null +++ b/QtVsTools.Wizards/Resources/small.png Binary files differ diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml b/QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml new file mode 100644 index 0000000..8b261f3 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml @@ -0,0 +1,356 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.ClassWizard.AddClassPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ee="clr-namespace:Microsoft.VisualStudio.ExtensionsExplorer.UI;assembly=Microsoft.VisualStudio.ExtensionsExplorer.UI" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="660" + d:DesignWidth="955"> + <wizards:WizardPage.Resources> + <Style x:Key="ListViewItemEnabledStyle" + TargetType="ListViewItem"> + <Setter Property="IsEnabled" + Value="True" /> + <EventSetter Event="MouseDoubleClick" + Handler="OnListViewItemMouseDoubleClick" /> + </Style> + <Style x:Key="ListViewItemDisabledStyle" + TargetType="ListViewItem"> + <Setter Property="IsEnabled" + Value="False" /> + </Style> + </wizards:WizardPage.Resources> + <Grid Background="{DynamicResource {x:Static ee:ColorResources.BackgroundBrushKey}}"> + <Grid.RowDefinitions> + <RowDefinition /> + <RowDefinition Height="5" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid Grid.Row="0"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="220" /> + <ColumnDefinition Width="4" /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <ResourceDictionary> + <ResourceDictionary.MergedDictionaries> + <ResourceDictionary Source="/QtVsTools.Wizards;component/Resources/ExpanderStyle.xaml" /> + </ResourceDictionary.MergedDictionaries> + </ResourceDictionary> + </Grid.Resources> + <Expander Grid.Column="0" + Header="Installed" + IsExpanded="True" + Margin="10,14,0,0" + Style="{DynamicResource ExpanderStyle}" + TabIndex="0" + Expanded="OnExpanded" + Collapsed="OnCollapsed"> + <TreeView MaxHeight="479" + Padding="6,7,0,7" + BorderThickness="0" + Name="VisualCppView" + Background="{DynamicResource {x:Static ee:ColorResources.WonderbarBrushKey}}" + TabIndex="4"> + <TreeView.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="{DynamicResource {x:Static ee:ColorResources.WonderbarTreeSelectedColorKey}}" /> + <SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" + Color="{DynamicResource {x:Static ee:ColorResources.WonderbarTreeInactiveSelectedColorKey}}" /> + </TreeView.Resources> + <TreeViewItem IsExpanded="True" + Header="Visual C++" + Foreground="{DynamicResource {x:Static ee:ColorResources.WonderbarTextBrushKey}}"> + <TreeViewItem IsExpanded="True" + Header="Qt " + IsSelected="True" + Foreground="{DynamicResource {x:Static ee:ColorResources.WonderbarTextBrushKey}}" /> + </TreeViewItem> + </TreeView> + </Expander> + <Grid Grid.Column="2"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition /> + <ColumnDefinition Width="1" /> + <ColumnDefinition Width="245" /> + </Grid.ColumnDefinitions> + <Grid Grid.Column="0" + Grid.Row="0" + Grid.ColumnSpan="2" + Margin="0,10,0,5"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + </Grid.ColumnDefinitions> + <TextBlock Grid.Column="0" + Text="Sort by:" + VerticalAlignment="Center" + Foreground="{DynamicResource {x:Static ee:ColorResources.BackgroundLowerRegionTextBrushKey}}" /> + <ComboBox Grid.Column="1" + Margin="10,0,0,0" + IsReadOnly="True" + SelectedIndex="0" + Loaded="OnComboBoxLoaded" + IsSynchronizedWithCurrentItem="True" + SelectionChanged="OnComboBoxSelectionChanged" + TabIndex="1" /> + <ee:ThemedToggleButton Grid.Column="2" + Name="SmallIcons" + Height="22" + Width="22" + Margin="5,0,0,0" + IsThreeState="False" + IsChecked="False" + ToolTip="Small Icons" + Checked="OnSmallIconsChecked" + TabIndex="2"> + <Image Source="/QtVsTools.Wizards;component/Resources/small.png" + Height="16" + Width="16" + RenderOptions.BitmapScalingMode="NearestNeighbor" + RenderOptions.EdgeMode="Aliased" /> + </ee:ThemedToggleButton> + <ee:ThemedToggleButton Grid.Column="3" + Name="MediumIcons" + Height="22" + Width="22" + Margin="5,0,0,0" + IsThreeState="False" + IsChecked="True" + ToolTip="Medium Icons" + Checked="OnMediumIconsChecked" + TabIndex="3"> + <Image Source="/QtVsTools.Wizards;component/Resources/medium.png" + Height="16" + Width="16" + RenderOptions.BitmapScalingMode="NearestNeighbor" + RenderOptions.EdgeMode="Aliased" /> + </ee:ThemedToggleButton> + </Grid> + <ListView Grid.Column="0" + Grid.Row="1" + TabIndex="5" + Margin="0,0,0,5" + Name="ClassView" + BorderThickness="0" + SelectionMode="Single" + ItemsSource="{Binding Classes}" + HorizontalContentAlignment="Stretch" + SelectionChanged="OnListViewSelectionChanged" + ItemContainerStyle="{StaticResource ListViewItemEnabledStyle}" + Background="{DynamicResource {x:Static ee:ColorResources.ContentBrushKey}}"> + <ListView.Resources> + <DataTemplate x:Key="EmptyTemplate"> + <TextBlock Text="No items found." + HorizontalAlignment="Center" + VerticalAlignment="Top" + Foreground="{DynamicResource {x:Static ee:ColorResources.ContentTextBrushKey}}" /> + </DataTemplate> + <DataTemplate x:Key="SmallTemplate"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + Height="16" + Width="16" + Margin="4,4,4,4" + Source="{Binding ImageSource}" + RenderOptions.EdgeMode="Aliased" + RenderOptions.BitmapScalingMode="HighQuality" /> + <TextBlock Grid.Column="1" + Margin="10,0,0,0" + MinHeight="10" + MinWidth="80" + Text="{Binding ClassName}" + TextTrimming="CharacterEllipsis" + VerticalAlignment="Center" + Foreground="{DynamicResource {x:Static ee:ColorResources.ContentTextBrushKey}}" /> + </Grid> + </DataTemplate> + <DataTemplate x:Key="MediumTemplate"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="38" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="Auto" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + Height="32" + Width="32" + Margin="0,2,4,2" + Source="{Binding ImageSource}" + RenderOptions.EdgeMode="Aliased" + RenderOptions.BitmapScalingMode="HighQuality" /> + <TextBlock Grid.Column="1" + Margin="8,0,0,0" + MinWidth="80" + Text="{Binding ClassName}" + TextTrimming="CharacterEllipsis" + VerticalAlignment="Center" + Foreground="{DynamicResource {x:Static ee:ColorResources.ContentTextBrushKey}}" /> + <TextBlock Grid.Column="2" + MinWidth="60" + MaxWidth="140" + Text="{Binding Type}" + VerticalAlignment="Center" + HorizontalAlignment="Left" + TextTrimming="CharacterEllipsis" + Foreground="{DynamicResource {x:Static ee:ColorResources.ContentTextBrushKey}}" /> + </Grid> + </DataTemplate> + </ListView.Resources> + </ListView> + <Border Grid.Column="2" + Grid.Row="1" + Margin="0,0,10,5" + Background="{DynamicResource {x:Static ee:ColorResources.DetailsBrushKey}}"> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" + MaxWidth="75" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock Grid.Column="0" + Grid.Row="0" + Text="Type:" + FontWeight="Bold" + Margin="10,10,0,10" + TextTrimming="CharacterEllipsis" + Foreground="{DynamicResource {x:Static ee:ColorResources.DetailsTextBrushKey}}" /> + <TextBlock x:Name="Type" + Grid.Column="1" + Grid.Row="0" + Text="Dummy" + Margin="10,10,10,10" + TextTrimming="CharacterEllipsis" + Foreground="{DynamicResource {x:Static ee:ColorResources.DetailsTextBrushKey}}" /> + <TextBlock x:Name="Description" + Grid.Column="0" + Grid.Row="1" + Text="Dummy" + TextWrapping="Wrap" + Grid.ColumnSpan="2" + Margin="10,0,10,10" + TextTrimming="CharacterEllipsis" + Foreground="{DynamicResource {x:Static ee:ColorResources.DetailsTextBrushKey}}" /> + </Grid> + </Border> + </Grid> + </Grid> + <Grid Grid.Row="2" + Background="{DynamicResource {x:Static ee:ColorResources.BackgroundLowerRegionBrushKey}}"> + <Grid.RowDefinitions> + <RowDefinition Height="5" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + </Grid.ColumnDefinitions> + <Button x:Name="BrowseButton" + Content="_Browse..." + MinWidth="75" + Grid.Row="2" + Grid.Column="2" + Margin="10,0,0,10" + TabIndex="8" + Click="OnBrowseButtonClick" /> + <TextBlock TextWrapping="Wrap" + Text="Name:" + Grid.Row="1" + Margin="10,0,0,0" + Height="18" + VerticalAlignment="Center" + Foreground="{DynamicResource {x:Static ee:ColorResources.BackgroundLowerRegionTextBrushKey}}" /> + <TextBlock TextWrapping="Wrap" + Text="Location:" + Grid.Row="2" + Margin="10,0,0,10" + VerticalAlignment="Center" + Foreground="{DynamicResource {x:Static ee:ColorResources.BackgroundLowerRegionTextBrushKey}}" /> + <TextBox x:Name="DefaultNameTextBox" + TextWrapping="Wrap" + Grid.Column="1" + Grid.Row="1" + Margin="10,0,0,5" + MinHeight="22" + TabIndex="6" + TextChanged="OnDefaultNameTextChanged" /> + <ComboBox x:Name="LocationComboBox" + IsEditable="True" + StaysOpenOnEdit="True" + Grid.Row="2" + Grid.Column="1" + Margin="10,0,0,10" + TabIndex="7" + Text="{Binding Path=Location}" + IsEnabled="False" /> + <StackPanel Grid.Row="3" + Grid.Column="3" + Orientation="Horizontal"> + <Button x:Name="AddButton" + Content="_Add" + IsDefault="True" + MinWidth="75" + Margin="10,0,0,10" + TabIndex="9" + Click="OnFinishButtonClick" /> + <Button Content="Cancel" + IsCancel="True" + MinWidth="75" + Margin="10,0,10,10" + TabIndex="10" /> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml.cs b/QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml.cs new file mode 100644 index 0000000..1bccf49 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/AddClassPage.xaml.cs @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using QtVsTools.VisualStudio; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Navigation; + +namespace QtVsTools.Wizards.ClassWizard +{ + using Util; + + public partial class AddClassPage : WizardPage + { + public AddClassPage() + { + InitializeComponent(); + DataContext = this; + + Classes = new List<Class> { + new Class { + ClassName = @"Qt Class", + DefaultName = @"QtClass", + Type = @"Visual C++", + Description = @"Creates a C++ header and source file for a new class that you " + + @"can add to a Qt Project.", + ImageSource = @"/QtVsTools.Wizards;component/Resources/Qt-logo-small.png", + Kind = ClassKind.Core + }, + new Class { + ClassName = @"Qt Widgets Class", + DefaultName = @"QtWidgetsClass", + Type = @"Visual C++", + Description = @"Creates a new empty Qt Designer form along with a matching " + + @"C++ header and source file for implementation purposes. You can add " + + @"the form and class to an existing Qt Project.", + ImageSource = @"/QtVsTools.Wizards;component/Resources/Qt-logo-small.png", + Kind = ClassKind.Gui + } + }; + ClassView.SelectedIndex = 0; + ClassView.ItemTemplate = ClassView.FindResource("MediumTemplate") as DataTemplate; + VisualCppView.Focus(); + } + + public Class Class + { + get; private set; + } + + public string Location + { + get; set; + } + + public List<Class> Classes + { + get; private set; + } + + private void OnExpanded(object sender, RoutedEventArgs e) + { + if (ClassView == null) + return; + ClassView.ItemsSource = Classes; + ClassView.ItemTemplate = ClassView.FindResource(MediumIcons.IsChecked + .GetValueOrDefault() ? "MediumTemplate" : "SmallTemplate") as DataTemplate; + ClassView.ItemContainerStyle = FindResource("ListViewItemEnabledStyle") as Style; + ClassView.SelectedIndex = 0; + + AddButton.IsEnabled = true; + BrowseButton.IsEnabled = true; + LocationComboBox.IsEnabled = true; + DefaultNameTextBox.IsEnabled = true; + } + + private void OnCollapsed(object sender, RoutedEventArgs e) + { + if (ClassView == null) + return; + + ClassView.ItemsSource = new List<Class> { + new Class() + }; + ClassView.SelectedIndex = 0; + ClassView.ItemTemplate = ClassView.FindResource("EmptyTemplate") as DataTemplate; + ClassView.ItemContainerStyle = FindResource("ListViewItemDisabledStyle") as Style; + ClassView.SelectedIndex = -1; + + AddButton.IsEnabled = false; + BrowseButton.IsEnabled = false; + LocationComboBox.IsEnabled = false; + DefaultNameTextBox.IsEnabled = false; + } + + private void OnComboBoxLoaded(object sender, RoutedEventArgs e) + { + var comboBox = sender as ComboBox; + if (comboBox == null) + return; + + comboBox.ItemsSource = new List<SortComboBoxItem> { + new SortComboBoxItem { + Content = "Default", + SortDirection = null + }, + new SortComboBoxItem { + Content = "Name Ascending", + SortDirection = ListSortDirection.Ascending + }, + new SortComboBoxItem { + Content = "Name Descending", + SortDirection = ListSortDirection.Descending + } + }; + comboBox.SelectedIndex = 0; + } + + private void OnBrowseButtonClick(object sender, RoutedEventArgs e) + { + var block = System.IntPtr.Zero; + try { + var dte = VsServiceProvider.GetService<DTE>(); + var serviceProvider = new ServiceProvider(dte as IServiceProvider); + var iVsUIShell = VsServiceProvider.GetService<SVsUIShell, IVsUIShell>(); + + System.IntPtr owner; + iVsUIShell.GetDialogOwnerHwnd(out owner); + + var browseInfo = new VSBROWSEINFOW[1]; + browseInfo[0].lStructSize = (uint)Marshal.SizeOf(typeof(VSBROWSEINFOW)); + browseInfo[0].pwzInitialDir = Location; + browseInfo[0].pwzDlgTitle = @"Location"; + browseInfo[0].hwndOwner = owner; + browseInfo[0].nMaxDirName = 260; + block = Marshal.AllocCoTaskMem(520); + browseInfo[0].pwzDirName = block; + + var result = iVsUIShell.GetDirectoryViaBrowseDlg(browseInfo); + if (result == Microsoft.VisualStudio.VSConstants.S_OK) { + Location = Marshal.PtrToStringAuto(browseInfo[0].pwzDirName); + LocationComboBox.Text = Marshal.PtrToStringAuto(browseInfo[0].pwzDirName); + } + } finally { + if (block != System.IntPtr.Zero) + Marshal.FreeCoTaskMem(block); + } + } + + private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) + { + var comboBox = sender as ComboBox; + if (comboBox == null) + return; + + var item = comboBox.SelectedItem as SortComboBoxItem; + if (item == null || ClassView == null) + return; + + var view = CollectionViewSource.GetDefaultView(ClassView.ItemsSource) as CollectionView; + view.SortDescriptions.Clear(); + if (item.SortDirection == null) + return; + view.SortDescriptions.Add(new SortDescription(@"ClassName", item.SortDirection + .GetValueOrDefault())); + } + + private void OnSmallIconsChecked(object sender, RoutedEventArgs e) + { + MediumIcons.IsChecked = false; + if (ClassView != null) + ClassView.ItemTemplate = ClassView.FindResource("SmallTemplate") as DataTemplate; + } + + private void OnMediumIconsChecked(object sender, RoutedEventArgs e) + { + SmallIcons.IsChecked = false; + if (ClassView != null) + ClassView.ItemTemplate = ClassView.FindResource("MediumTemplate") as DataTemplate; + } + + private void OnListViewSelectionChanged(object sender, SelectionChangedEventArgs e) + { + var listView = sender as ListView; + if (listView == null) + return; + + var item = listView.SelectedItem as Class; + if (item == null) + return; + + Class = item; + Type.Text = item.Type; + Description.Text = item.Description; + DefaultNameTextBox.Text = item.DefaultName; + } + + private void OnDefaultNameTextChanged(object sender, TextChangedEventArgs e) + { + var item = ClassView.SelectedItem as Class; + if (item == null) + return; + item.DefaultName = DefaultNameTextBox.Text; + } + + private void OnListViewItemMouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (e.LeftButton != MouseButtonState.Pressed) + return; + + var item = (sender as ListViewItem).DataContext as Class; + if (item == null) + return; + item.DefaultName = DefaultNameTextBox.Text; + + OnReturnEx(new ReturnEventArgs<WizardResult>(WizardResult.Finished)); + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/AddClassWizard.cs b/QtVsTools.Wizards/Wizards/ClassWizard/AddClassWizard.cs new file mode 100644 index 0000000..958653f --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/AddClassWizard.cs @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.Internal.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using QtVsTools.VisualStudio; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.ClassWizard +{ + using Util; + + public class AddClassWizard : IClassWizard + { + public WizardResult Run(EnvDTE.DTE dte, string name, string location) + { + IVsUIShell iVsUIShell = null; + ServiceProvider serviceProvider = null; + try { + serviceProvider = new ServiceProvider(dte as IServiceProvider); + iVsUIShell = VsServiceProvider.GetService<SVsUIShell, IVsUIShell>(); + } catch { + return WizardResult.Exception; + } + + iVsUIShell.EnableModeless(0); + + try { + System.IntPtr hwnd; + iVsUIShell.GetDialogOwnerHwnd(out hwnd); + + var addClassPage = new AddClassPage { Location = location }; + var wizard = new WizardWindow(new List<WizardPage> { addClassPage }) + { + Width = 955, + Height = 660, + MinWidth = 800, + MinHeight = 450, + + Title = @"Add Class - " + name, + MaxWidth = double.PositiveInfinity, + MaxHeight = double.PositiveInfinity, + ResizeMode = System.Windows.ResizeMode.CanResize + }; + WindowHelper.ShowModal(wizard, hwnd); + if (!wizard.DialogResult.GetValueOrDefault(false)) + return WizardResult.Canceled; + + IClassWizard classWizard = null; + switch (addClassPage.Class.Kind) { + case ClassKind.Gui: + classWizard = new GuiClassWizard(); + break; + case ClassKind.Core: + classWizard = new CoreClassWizard(); + break; + default: + throw new System.Exception("Unexpected class kind."); + } + + var className = addClassPage.Class.DefaultName; + className = Regex.Replace(className, @"[^a-zA-Z0-9_]", string.Empty); + className = Regex.Replace(className, @"^[\d-]*\s*", string.Empty); + var result = new ClassNameValidationRule().Validate(className, null); + if (result != ValidationResult.ValidResult) + className = string.Empty; + + classWizard.Run(dte, className, addClassPage.Location); + + } catch { + return WizardResult.Exception; + } finally { + iVsUIShell.EnableModeless(1); + } + + return WizardResult.Finished; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/Class.cs b/QtVsTools.Wizards/Wizards/ClassWizard/Class.cs new file mode 100644 index 0000000..6f4d070 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/Class.cs @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Wizards.ClassWizard +{ + public class Class + { + public string ClassName + { + get; set; + } + + public string DefaultName + { + get; set; + } + + public string Type + { + get; set; + } + + public string Description + { + get; set; + } + + public string ImageSource + { + get; set; + } + + public ClassKind Kind + { + get; set; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/ClassKind.cs b/QtVsTools.Wizards/Wizards/ClassWizard/ClassKind.cs new file mode 100644 index 0000000..db0b359 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/ClassKind.cs @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Wizards.ClassWizard +{ + public enum ClassKind + { + Gui, + Core + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml b/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml new file mode 100644 index 0000000..a72873f --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml @@ -0,0 +1,298 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.ClassWizard.CoreClassPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:util="clr-namespace:QtVsTools.Wizards.Util" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="445" + d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" + VerticalAlignment="Top" + Margin="0,25,0,0" + RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" + ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" + Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" + Grid.Row="0"> + <Run FontWeight="Bold" + Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + <LineBreak /> + </TextBlock> + <Grid Grid.Row="1"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <Style TargetType="{x:Type TextBox}"> + <Style.Triggers> + <Trigger Property="Validation.HasError" + Value="true"> + <Setter Property="ToolTip" + Value="{Binding RelativeSource={RelativeSource Self}, + Path=(Validation.Errors)[0].ErrorContent}" /> + </Trigger> + </Style.Triggers> + </Style> + </Grid.Resources> + <TextBlock Grid.Row="0" + Text="Class Name:" + Margin="0,0,10,0" /> + <TextBox Grid.Row="1" + Margin="0,0,10,10" + Name="ClassName" + TextChanged="OnClassNameChanged" + TabIndex="0"> + <TextBox.Text> + <Binding Path="Data.ClassName" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule SupportNamespaces="True" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock TextWrapping="Wrap" + Text="Constructor signature:" + Grid.Row="2" + Margin="0,0,10,5" /> + <ComboBox Grid.Row="3" + VerticalAlignment="Top" + TabIndex="2" + IsSynchronizedWithCurrentItem="True" + Margin="0,0,10,30" + SelectedIndex="0" + Text="{Binding Path=Data.ConstructorSignature}"> + <ComboBoxItem Content="QObject *parent" + IsSelected="True" /> + <ComboBoxItem Content="QWidget *parent" /> + <ComboBoxItem Content="" /> + <ComboBox.Style> + <Style TargetType="{x:Type ComboBox}"> + <Style.Triggers> + <DataTrigger Binding="{Binding ElementName=BaseClass, Path=Text}" + Value=""> + <Setter Property="IsEnabled" + Value="false" /> + </DataTrigger> + </Style.Triggers> + </Style> + </ComboBox.Style> + </ComboBox> + <TextBlock TextWrapping="Wrap" + Text="Base class:" + Margin="0,0,10,0" + Grid.Column="1" /> + <TextBox Margin="0,0,0,10" + TabIndex="1" + Grid.Column="1" + Grid.Row="1" + Name="BaseClass"> + <TextBox.Text> + <Binding Path="Data.BaseClass" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule AllowEmptyIdentifier="True"/> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <Grid Grid.Row="4" + Grid.ColumnSpan="2"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock Grid.Row="0" + Margin="0,0,10,5" + Text="Header (.h) file:" /> + <TextBox Grid.Row="1" + Margin="0,0,10,0" + Name="ClassHeaderFile" + TabIndex="4"> + <TextBox.Text> + <Binding Path="Data.ClassHeaderFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".h" /> + <util:FileExistsinFilterValidationRule Filter="FL_HFiles" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Row="0" + Grid.Column="1" + Margin="0,0,0,5" + Text="Source (.cpp) file:" /> + <TextBox Grid.Row="1" + Grid.Column="1" + Margin="0,0,0,00" + Name="ClassSourceFile" + TabIndex="5"> + <TextBox.Text> + <Binding Path="Data.ClassSourceFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".cpp" /> + <util:FileExistsinFilterValidationRule Filter="FL_CppFiles" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + </Grid> + <CheckBox Grid.Row="5" + Content="Lower case file names" + Name="LowerCaseFileNames" + Click="OnLowerCaseFileNamesClick" + Margin="0,20,10,5" + TabIndex="6" /> + <CheckBox Content="Insert Q__OBJECT macro" + Grid.Column="1" + Grid.Row="3" + VerticalAlignment="Center" + Margin="0,0,0,30" + TabIndex="3" + IsChecked="True"> + <CheckBox.Style> + <Style TargetType="{x:Type CheckBox}"> + <Style.Triggers> + <DataTrigger Binding="{Binding ElementName=BaseClass, Path=Text}" + Value=""> + <Setter Property="IsEnabled" + Value="false" /> + </DataTrigger> + </Style.Triggers> + </Style> + </CheckBox.Style> + </CheckBox> + </Grid> + <StackPanel HorizontalAlignment="Right" + Orientation="Horizontal" + Grid.Row="2" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" + Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" + MinWidth="75">< _Previous</Button> + <Button MinWidth="75" + Name="NextButton" + Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" + Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" + Click="OnFinishButtonClick" + Margin="10,0,0,0" + IsDefault="True" + Name="FinishButton" + Content="_Finish" + VerticalAlignment="Bottom"> + <Button.Style> + <Style TargetType="{x:Type Button}"> + <Setter Property="IsEnabled" + Value="false" /> + <Style.Triggers> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding Path=FinishButtonEnabled}" + Value="true" /> + <Condition Binding="{Binding ElementName=ClassName, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=BaseClass, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassHeaderFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassSourceFile, + Path=(Validation.HasError)}" + Value="false" /> + </MultiDataTrigger.Conditions> + <Setter Property="IsEnabled" + Value="true" /> + </MultiDataTrigger> + </Style.Triggers> + </Style> + </Button.Style> + </Button> + <Button Click="OnCancelButtonClick" + MinWidth="75" + Margin="10,0,0,0" + Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" + IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml.cs b/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml.cs new file mode 100644 index 0000000..1f8221c --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassPage.xaml.cs @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Windows; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.ClassWizard +{ + public partial class CoreClassPage : WizardPage + { + public CoreClassPage() + { + InitializeComponent(); + DataContext = this; + } + + private void OnClassNameChanged(object sender, TextChangedEventArgs e) + { + UpdateFileNames(); + } + + private void OnLowerCaseFileNamesClick(object sender, RoutedEventArgs e) + { + UpdateFileNames(); + } + + private void UpdateFileNames() + { + var filename = ClassName.Text; + if (LowerCaseFileNames.IsChecked.GetValueOrDefault()) + filename = filename.ToLower(); + + var index = filename.LastIndexOf(@":", System.StringComparison.Ordinal); + if (index >= 0) + filename = filename.Substring(index + 1); + + ClassHeaderFile.Text = filename + @".h"; + ClassSourceFile.Text = filename + @".cpp"; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassWizard.cs b/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassWizard.cs new file mode 100644 index 0000000..ef2cd23 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/Core/CoreClassWizard.cs @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.Internal.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TemplateWizard; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace QtVsTools.Wizards.ClassWizard +{ + public class CoreClassWizard : IClassWizard + { + public WizardResult Run(EnvDTE.DTE dte, string name, string location) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + var serviceProvider = new ServiceProvider(dte as IServiceProvider); + var iVsUIShell = VsServiceProvider.GetService<SVsUIShell, IVsUIShell>(); + + iVsUIShell.EnableModeless(0); + + try { + System.IntPtr hwnd; + iVsUIShell.GetDialogOwnerHwnd(out hwnd); + + try { + if (string.IsNullOrEmpty(name)) + name = @"QtClass"; + + data.ClassName = name; + data.BaseClass = @"QObject"; + data.ConstructorSignature = "QObject *parent"; + data.InsertQObjectMacro = true; + data.ClassHeaderFile = name + @".h"; + data.ClassSourceFile = name + @".cpp"; + + var wizard = new WizardWindow(new List<WizardPage> { + new WizardIntroPage { + Data = data, + Header = @"Welcome to the Qt Class Wizard", + Message = @"This wizard will add a new Qt class to your project. The " + + @"wizard creates a .h and .cpp file." + System.Environment.NewLine + + System.Environment.NewLine + "To continue, click Next.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new CoreClassPage { + Data = data, + Header = @"Welcome to the Qt Class Wizard", + Message = @"This wizard will add a new Qt class to your project. The " + + @"wizard creates a .h and .cpp file.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true + } + }) + { + Title = @"Qt Class Wizard" + }; + WindowHelper.ShowModal(wizard, hwnd); + if (!wizard.DialogResult.HasValue || !wizard.DialogResult.Value) + throw new System.Exception("Unexpected wizard return value."); + + var array = data.ClassName.Split(new[] { "::" }, System + .StringSplitOptions.RemoveEmptyEntries); + data.ClassName = array.LastOrDefault(); + + string nsBegin = string.Empty, nsEnd = string.Empty; + for (var i = 0; i < array.Length - 1; ++i) { + nsBegin += "namespace " + array[i] + " {\r\n"; + nsEnd = "} // namespace " + array[i] + "\r\n" + nsEnd; + } + + var pro = HelperFunctions.GetSelectedQtProject(dte); + if (pro == null) + throw new QtVSException("Can't find a selected project"); + + var qtProject = QtProject.Create(pro); + + var hppFile = AddProjectItemHpp(location); + ReplaceNamespaceToken(hppFile, nsBegin, nsEnd); + + qtProject.AdjustWhitespace(hppFile); + qtProject.AddFileToProject(hppFile, Filters.HeaderFiles()); + VsShellUtilities.OpenDocument(serviceProvider, hppFile); + + var pch = string.Empty; + if (qtProject.UsesPrecompiledHeaders()) + pch = qtProject.GetPrecompiledHeaderThrough(); + + var cppFile = AddProjectItemCpp(location, pch); + ReplaceNamespaceToken(cppFile, nsBegin, nsEnd); + + qtProject.AdjustWhitespace(cppFile); + qtProject.AddFileToProject(cppFile, Filters.SourceFiles()); + VsShellUtilities.OpenDocument(serviceProvider, cppFile); + + } catch (QtVSException exception) { + Messages.DisplayErrorMessage(exception.Message); + throw; // re-throw, but keep the original exception stack intact + } + } catch { + throw new WizardBackoutException(); + } finally { + iVsUIShell.EnableModeless(1); + } + + return WizardResult.Finished; + } + + private readonly WizardData data = new WizardData + { + DefaultModules = new List<string> { + @"QtCore" + } + }; + + private static void ReplaceNamespaceToken(string file, string nsBegin, string nsEnd) + { + if (!string.IsNullOrEmpty(nsBegin)) + nsBegin += System.Environment.NewLine; + QtProject.ReplaceTokenInFile(file, "%NAMESPACE_BEGIN%", nsBegin); + + if (!string.IsNullOrEmpty(nsEnd)) + nsEnd = System.Environment.NewLine + nsEnd; + QtProject.ReplaceTokenInFile(file, "%NAMESPACE_END%", nsEnd); + } + + private string AddProjectItemHpp(string location) + { + var hppFile = Path.GetTempFileName(); + using (var tmp = new StreamWriter(hppFile)) { + tmp.Write("#pragma once\r\n\r\n" + + "%BASECLASSINCLUDE%%NAMESPACE_BEGIN%class %CLASS%%BASEDECL%\r\n" + + "{%Q_OBJECT%\r\n" + + "public:\r\n" + + " %CLASS%(%CTORSIG%);\r\n" + + " ~%CLASS%();\r\n" + + "};\r\n" + + "%NAMESPACE_END%"); + } + hppFile = QtProject.CopyFileToFolder(hppFile, location, data.ClassHeaderFile); + + QtProject.ReplaceTokenInFile(hppFile, "%CLASS%", data.ClassName); + QtProject.ReplaceTokenInFile(hppFile, "%BASECLASS%", data.BaseClass); + + if (string.IsNullOrEmpty(data.BaseClass)) { + QtProject.ReplaceTokenInFile(hppFile, "%BASEDECL%", string.Empty); + QtProject.ReplaceTokenInFile(hppFile, "%BASECLASSINCLUDE%", string.Empty); + QtProject.ReplaceTokenInFile(hppFile, "%Q_OBJECT%", string.Empty); + QtProject.ReplaceTokenInFile(hppFile, "%CTORSIG%", string.Empty); + } else { + QtProject.ReplaceTokenInFile(hppFile, "%BASEDECL%", " : public " + + data.BaseClass); + QtProject.ReplaceTokenInFile(hppFile, "%BASECLASSINCLUDE%", "#include <" + + data.BaseClass + ">\r\n\r\n"); + if (data.InsertQObjectMacro) + QtProject.ReplaceTokenInFile(hppFile, "%Q_OBJECT%", "\r\n\tQ_OBJECT\r\n"); + QtProject.ReplaceTokenInFile(hppFile, "%CTORSIG%", data.ConstructorSignature); + } + + return hppFile; + } + + private string AddProjectItemCpp(string location, string pch) + { + var cppFile = Path.GetTempFileName(); + using (var tmp = new StreamWriter(cppFile)) { + tmp.Write("#include \"%INCLUDE%\"\r\n\r\n" + + "%NAMESPACE_BEGIN%%CLASS%::%CLASS%(%CTORSIG%)%BASECLASS%\r\n" + + "{\r\n}\r\n\r\n" + + "%CLASS%::~%CLASS%()\r\n" + + "{\r\n}\r\n" + + "%NAMESPACE_END%"); + } + cppFile = QtProject.CopyFileToFolder(cppFile, location, data.ClassSourceFile); + + if (!string.IsNullOrEmpty(pch)) + QtProject.ReplaceTokenInFile(cppFile, "%INCLUDE%", pch + "\"\r\n#include \"%INCLUDE%"); + + QtProject.ReplaceTokenInFile(cppFile, "%INCLUDE%", data.ClassHeaderFile); + QtProject.ReplaceTokenInFile(cppFile, "%CLASS%", data.ClassName); + + QtProject.ReplaceTokenInFile(cppFile, "%BASECLASS%", string.IsNullOrEmpty(data + .ConstructorSignature) ? "" : "\r\n : " + data.BaseClass + "(parent)"); + QtProject.ReplaceTokenInFile(cppFile, "%CTORSIG%", data.ConstructorSignature); + + return cppFile; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml b/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml new file mode 100644 index 0000000..f65cf86 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml @@ -0,0 +1,301 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.ClassWizard.GuiClassPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:util="clr-namespace:QtVsTools.Wizards.Util" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="445" + d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" + VerticalAlignment="Top" + Margin="0,25,0,0" + RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" + ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" + Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" + Grid.Row="0"> + <Run FontWeight="Bold" + Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + <LineBreak /> + </TextBlock> + <Grid Grid.Row="1"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <Style TargetType="{x:Type TextBox}"> + <Style.Triggers> + <Trigger Property="Validation.HasError" + Value="true"> + <Setter Property="ToolTip" + Value="{Binding RelativeSource={RelativeSource Self}, + Path=(Validation.Errors)[0].ErrorContent}" /> + </Trigger> + </Style.Triggers> + </Style> + </Grid.Resources> + <TextBlock Grid.Row="0" + Text="Class Name:" + Margin="0,0,10,0" /> + <TextBox Grid.Row="1" + Margin="0,0,10,10" + Name="ClassName" + TextChanged="OnClassNameChanged" + TabIndex="0"> + <TextBox.Text> + <Binding Path="Data.ClassName" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule SupportNamespaces="True" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock TextWrapping="Wrap" + Text="User Interface (.ui) file:" + Grid.Row="2" + Margin="0,0,10,5" /> + <TextBlock TextWrapping="Wrap" + Text="Base class:" + Margin="0,0,10,0" + Grid.Column="1" /> + <TextBox x:Name="BaseClass" + Margin="0,0,0,10" + TabIndex="1" + Grid.Column="1" + Grid.Row="1"> + <TextBox.Text> + <Binding Path="Data.BaseClass" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <Grid Grid.Row="4" + Grid.ColumnSpan="2"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock Grid.Row="0" + Margin="0,0,10,5" + Text="Header (.h) file:" /> + <TextBox Grid.Row="1" + Margin="0,0,10,0" + Name="ClassHeaderFile" + TabIndex="6"> + <TextBox.Text> + <Binding Path="Data.ClassHeaderFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".h" /> + <util:FileExistsinFilterValidationRule Filter="FL_HFiles" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Row="0" + Grid.Column="1" + Margin="0,0,0,5" + Text="Source (.cpp) file:" /> + <TextBox Grid.Row="1" + Grid.Column="1" + Margin="0,0,0,00" + Name="ClassSourceFile" + TabIndex="7"> + <TextBox.Text> + <Binding Path="Data.ClassSourceFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".cpp" /> + <util:FileExistsinFilterValidationRule Filter="FL_CppFiles" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + </Grid> + <CheckBox Grid.Row="5" + Content="Lower case file names" + Name="LowerCaseFileNames" + Click="OnLowerCaseFileNamesClick" + Margin="0,20,10,5" + TabIndex="8" /> + <TextBlock Grid.Column="1" + Grid.Row="2" + TextWrapping="Wrap" + Text="Ui Class Inclusion as:" + Margin="0" + VerticalAlignment="Top" /> + <StackPanel Grid.Column="1" + Grid.Row="3" + Margin="10,0,0,20"> + <RadioButton x:Name="Member" + Content="Member" + Margin="0,0,0,5" + IsChecked="True" + Checked="OnRadioButtonChecked" + TabIndex="3" /> + <RadioButton x:Name="MemberPointer" + Content="Member Pointer" + Margin="0,0,0,5" + Checked="OnRadioButtonChecked" + TabIndex="4" /> + <RadioButton x:Name="MultipleInheritance" + Content="Multiple Inheritance" + Checked="OnRadioButtonChecked" + TabIndex="5" /> + </StackPanel> + <TextBox x:Name="UiFile" + Grid.Row="3" + TextWrapping="Wrap" + VerticalAlignment="Top" + Margin="0,0,10,0" + TabIndex="2"> + <TextBox.Text> + <Binding Path="Data.UiFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".ui" /> + <util:FileExistsinFilterValidationRule Filter="FL_UiFiles" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + </Grid> + <StackPanel HorizontalAlignment="Right" + Orientation="Horizontal" + Grid.Row="2" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" + Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" + MinWidth="75">< _Previous</Button> + <Button MinWidth="75" + Name="NextButton" + Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" + Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" + Click="OnFinishButtonClick" + Margin="10,0,0,0" + IsDefault="True" + Name="FinishButton" + Content="_Finish" + VerticalAlignment="Bottom"> + <Button.Style> + <Style TargetType="{x:Type Button}"> + <Setter Property="IsEnabled" + Value="false" /> + <Style.Triggers> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding Path=FinishButtonEnabled}" + Value="true" /> + <Condition Binding="{Binding ElementName=ClassName, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=BaseClass, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=UiFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassHeaderFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassSourceFile, + Path=(Validation.HasError)}" + Value="false" /> + </MultiDataTrigger.Conditions> + <Setter Property="IsEnabled" + Value="true" /> + </MultiDataTrigger> + </Style.Triggers> + </Style> + </Button.Style> + </Button> + <Button Click="OnCancelButtonClick" + MinWidth="75" + Margin="10,0,0,0" + Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" + IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml.cs b/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml.cs new file mode 100644 index 0000000..8b7cb96 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassPage.xaml.cs @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Windows; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.ClassWizard +{ + using Util; + + public partial class GuiClassPage : WizardPage + { + public GuiClassPage() + { + InitializeComponent(); + DataContext = this; + } + + private void OnClassNameChanged(object sender, TextChangedEventArgs e) + { + UpdateFileNames(); + } + + private void OnLowerCaseFileNamesClick(object sender, RoutedEventArgs e) + { + UpdateFileNames(); + } + + private void UpdateFileNames() + { + var filename = ClassName.Text; + if (LowerCaseFileNames.IsChecked.GetValueOrDefault()) + filename = filename.ToLower(); + + var index = filename.LastIndexOf(@":", System.StringComparison.Ordinal); + if (index >= 0) + filename = filename.Substring(index + 1); + + ClassHeaderFile.Text = filename + @".h"; + ClassSourceFile.Text = filename + @".cpp"; + UiFile.Text = filename + @".ui"; + } + + private void OnRadioButtonChecked(object sender, RoutedEventArgs e) + { + if (Data == null) + return; + + if (Member.IsChecked.GetValueOrDefault()) + Data.UiClassInclusion = UiClassInclusion.Member; + if (MemberPointer.IsChecked.GetValueOrDefault()) + Data.UiClassInclusion = UiClassInclusion.MemberPointer; + if (MultipleInheritance.IsChecked.GetValueOrDefault()) + Data.UiClassInclusion = UiClassInclusion.MultipleInheritance; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassWizard.cs b/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassWizard.cs new file mode 100644 index 0000000..a694266 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/Gui/GuiClassWizard.cs @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.Internal.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TemplateWizard; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace QtVsTools.Wizards.ClassWizard +{ + public class GuiClassWizard : IClassWizard + { + public WizardResult Run(EnvDTE.DTE dte, string name, string location) + { + var serviceProvider = new ServiceProvider(dte as IServiceProvider); + var iVsUIShell = VsServiceProvider.GetService<SVsUIShell, IVsUIShell>(); + + iVsUIShell.EnableModeless(0); + + try { + System.IntPtr hwnd; + iVsUIShell.GetDialogOwnerHwnd(out hwnd); + + try { + if (string.IsNullOrEmpty(name)) + name = @"QtWidgetsClass"; + + data.ClassName = name; + data.BaseClass = @"QWidget"; + data.ClassHeaderFile = name + @".h"; + data.ClassSourceFile = name + @".cpp"; + data.UiFile = data.ClassName + @".ui"; + + var wizard = new WizardWindow(new List<WizardPage> { + new WizardIntroPage { + Data = data, + Header = @"Welcome to the Qt Widgets Class Wizard", + Message = @"This wizard will add a new Qt Widgets class to your project. " + + @"The wizard creates a .h and .cpp file. It also creates a new " + + @"empty form." + System.Environment.NewLine + + System.Environment.NewLine + "To continue, click Next.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new GuiClassPage { + Data = data, + Header = @"Welcome to the Qt Widgets Class Wizard", + Message = @"This wizard will add a new Qt Widgets class to your project. " + + @"The wizard creates a .h and .cpp file. It also creates a new " + + @"empty form.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true + } + }) + { + Title = @"Qt Widgets Class Wizard" + }; + WindowHelper.ShowModal(wizard, hwnd); + if (!wizard.DialogResult.HasValue || !wizard.DialogResult.Value) + throw new System.Exception("Unexpected wizard return value."); + + var array = data.ClassName.Split(new[] { "::" }, System + .StringSplitOptions.RemoveEmptyEntries); + data.ClassName = array.LastOrDefault(); + + string nsBegin = string.Empty, nsEnd = string.Empty; + for (var i = 0; i < array.Length - 1; ++i) { + nsBegin += "namespace " + array[i] + " {\r\n"; + nsEnd = "} // namespace " + array[i] + "\r\n" + nsEnd; + } + + var pro = HelperFunctions.GetSelectedQtProject(dte); + if (pro == null) + throw new QtVSException("Can't find a selected project"); + + var qtProject = QtProject.Create(pro); + + var hppFile = AddProjectItemHpp(location); + ReplaceNamespaceToken(hppFile, nsBegin, nsEnd); + + qtProject.AdjustWhitespace(hppFile); + qtProject.AddFileToProject(hppFile, Filters.HeaderFiles()); + VsShellUtilities.OpenDocument(serviceProvider, hppFile); + + var pch = string.Empty; + if (qtProject.UsesPrecompiledHeaders()) + pch = qtProject.GetPrecompiledHeaderThrough(); + + var cppFile = AddProjectItemCpp(location, pch); + ReplaceNamespaceToken(cppFile, nsBegin, nsEnd); + + qtProject.AdjustWhitespace(cppFile); + qtProject.AddFileToProject(cppFile, Filters.SourceFiles()); + VsShellUtilities.OpenDocument(serviceProvider, cppFile); + + var uiFile = AddProjectItemUi(location); + qtProject.AdjustWhitespace(uiFile); + qtProject.AddFileToProject(uiFile, Filters.FormFiles()); + + } catch (QtVSException exception) { + Messages.DisplayErrorMessage(exception.Message); + throw; // re-throw, but keep the original exception stack intact + } + } catch { + throw new WizardBackoutException(); + } finally { + iVsUIShell.EnableModeless(1); + } + + return WizardResult.Finished; + } + + private readonly WizardData data = new WizardData + { + DefaultModules = new List<string> { + @"QtCore", @"QtGui", @"QtWidgets" + } + }; + + private const string MemberClassHeader = + "#pragma once\r\n" + + "\r\n" + + "#include <%BASECLASS%>\r\n" + + "#include \"%UI_HDR%\"\r\n" + + "\r\n" + + "%NAMESPACE_BEGIN%class %CLASS% : public %BASECLASS%\r\n" + + "{\r\n" + + " Q_OBJECT\r\n" + + "\r\n" + + "public:\r\n" + + " %CLASS%(QWidget *parent = Q_NULLPTR);\r\n" + + " ~%CLASS%();\r\n" + + "\r\n" + + "private:\r\n" + + " Ui::%CLASS% ui;\r\n" + + "};\r\n" + + "%NAMESPACE_END%"; + + private const string MemberClassSource = + "#include \"%INCLUDE%\"\r\n" + + "\r\n" + + "%NAMESPACE_BEGIN%%CLASS%::%CLASS%(QWidget *parent)\r\n" + + " : %BASECLASS%(parent)\r\n" + + "{\r\n" + + " ui.setupUi(this);\r\n" + + "}\r\n" + + "\r\n" + + "%CLASS%::~%CLASS%()\r\n" + + "{\r\n" + + "}\r\n" + + "%NAMESPACE_END%"; + + private const string MemberPointerClassHeader = + "#pragma once\r\n" + + "\r\n" + + "#include <%BASECLASS%>\r\n" + + "namespace Ui { class %CLASS%; };\r\n" + + "\r\n" + + "%NAMESPACE_BEGIN%class %CLASS% : public %BASECLASS%\r\n" + + "{\r\n" + + " Q_OBJECT\r\n" + + "\r\n" + + "public:\r\n" + + " %CLASS%(QWidget *parent = Q_NULLPTR);\r\n" + + " ~%CLASS%();\r\n" + + "\r\n" + + "private:\r\n" + + " Ui::%CLASS% *ui;\r\n" + + "};\r\n" + + "%NAMESPACE_END%"; + + private const string MemberPointerClassSource = + "#include \"%INCLUDE%\"\r\n" + + "#include \"%UI_HDR%\"\r\n" + + "\r\n" + + "%NAMESPACE_BEGIN%%CLASS%::%CLASS%(QWidget *parent)\r\n" + + " : %BASECLASS%(parent)\r\n" + + "{\r\n" + + " ui = new Ui::%CLASS%();\r\n" + + " ui->setupUi(this);\r\n" + + "}\r\n" + + "\r\n" + + "%CLASS%::~%CLASS%()\r\n" + + "{\r\n" + + " delete ui;\r\n" + + "}\r\n" + + "%NAMESPACE_END%"; + + private const string InheritanceClassHeader = + "#pragma once\r\n" + + "\r\n" + + "#include <%BASECLASS%>\r\n" + + "#include \"%UI_HDR%\"\r\n" + + "\r\n" + + "%NAMESPACE_BEGIN%class %CLASS% : public %BASECLASS%, public Ui::%CLASS%\r\n" + + "{\r\n" + + " Q_OBJECT\r\n" + + "\r\n" + + "public:\r\n" + + " %CLASS%(QWidget *parent = Q_NULLPTR);\r\n" + + " ~%CLASS%();\r\n" + + "};\r\n" + + "%NAMESPACE_END%"; + + private const string InheritanceClassSource = + "#include \"%INCLUDE%\"\r\n" + + "\r\n" + + "%NAMESPACE_BEGIN%%CLASS%::%CLASS%(QWidget *parent)\r\n" + + " : %BASECLASS%(parent)\r\n" + + "{\r\n" + + " setupUi(this);\r\n" + + "}\r\n" + + "\r\n" + + "%CLASS%::~%CLASS%()\r\n" + + "{\r\n" + + "}\r\n" + + "%NAMESPACE_END%"; + + private static void ReplaceNamespaceToken(string file, string nsBegin, string nsEnd) + { + if (!string.IsNullOrEmpty(nsBegin)) + nsBegin += System.Environment.NewLine; + QtProject.ReplaceTokenInFile(file, "%NAMESPACE_BEGIN%", nsBegin); + + if (!string.IsNullOrEmpty(nsEnd)) + nsEnd = System.Environment.NewLine + nsEnd; + QtProject.ReplaceTokenInFile(file, "%NAMESPACE_END%", nsEnd); + } + + private string AddProjectItemHpp(string location) + { + var replaceUiHeader = true; + var hppFile = Path.GetTempFileName(); + using (var tmp = new StreamWriter(hppFile)) { + var content = string.Empty; + switch (data.UiClassInclusion) { + case UiClassInclusion.Member: + content = MemberClassHeader; + break; + case UiClassInclusion.MemberPointer: + replaceUiHeader = false; + content = MemberPointerClassHeader; + break; + case UiClassInclusion.MultipleInheritance: + content = InheritanceClassHeader; + break; + } + tmp.Write(content); + } + hppFile = QtProject.CopyFileToFolder(hppFile, location, data.ClassHeaderFile); + + QtProject.ReplaceTokenInFile(hppFile, "%CLASS%", data.ClassName); + QtProject.ReplaceTokenInFile(hppFile, "%BASECLASS%", data.BaseClass); + if (replaceUiHeader) { + QtProject.ReplaceTokenInFile(hppFile, "%UI_HDR%", "ui_" + Path + .GetFileNameWithoutExtension(data.UiFile) + ".h"); + } + + return hppFile; + } + + private string AddProjectItemCpp(string location, string pch) + { + var replaceUiHeader = false; + var cppFile = Path.GetTempFileName(); + + using (var tmp = new StreamWriter(cppFile)) { + var content = string.Empty; + switch (data.UiClassInclusion) { + case UiClassInclusion.Member: + content = MemberClassSource; + break; + case UiClassInclusion.MemberPointer: + replaceUiHeader = true; + content = MemberPointerClassSource; + break; + case UiClassInclusion.MultipleInheritance: + content = InheritanceClassSource; + break; + } + tmp.Write(content); + } + cppFile = QtProject.CopyFileToFolder(cppFile, location, data.ClassSourceFile); + + if (!string.IsNullOrEmpty(pch)) + QtProject.ReplaceTokenInFile(cppFile, "%INCLUDE%", pch + "\"\r\n#include \"%INCLUDE%"); + + QtProject.ReplaceTokenInFile(cppFile, "%INCLUDE%", data.ClassHeaderFile); + QtProject.ReplaceTokenInFile(cppFile, "%CLASS%", data.ClassName); + QtProject.ReplaceTokenInFile(cppFile, "%BASECLASS%", data.BaseClass); + if (replaceUiHeader) { + QtProject.ReplaceTokenInFile(cppFile, "%UI_HDR%", "ui_" + Path + .GetFileNameWithoutExtension(data.UiFile) + ".h"); + } + + return cppFile; + } + + private string AddProjectItemUi(string location) + { + var uiFile = Path.GetTempFileName(); + using (var tmp = new StreamWriter(uiFile)) { + tmp.Write( + "<UI version=\"4.0\" >\r\n" + + " <class>%CLASS%</class>\r\n" + + " <widget class=\"%BASECLASS%\" name=\"%CLASS%\" >\r\n" + + " <property name=\"objectName\" >\r\n" + + " <string notr=\"true\">%CLASS%</string>\r\n" + + " </property>\r\n" + + " <property name=\"geometry\" >\r\n" + + " <rect>\r\n" + + " <x>0</x>\r\n" + + " <y>0</y>\r\n" + + " <width>400</width>\r\n" + + " <height>300</height>\r\n" + + " </rect>\r\n" + + " </property>\r\n" + + " <property name=\"windowTitle\" >\r\n" + + " <string>%CLASS%</string>\r\n" + + " </property>%CENTRAL_WIDGET%\r\n" + + " </widget>\r\n" + + " <layoutDefault spacing=\"6\" margin=\"11\" />\r\n" + + " <pixmapfunction></pixmapfunction>\r\n" + + " <resources/>\r\n" + + " <connections/>\r\n" + + "</UI>\r\n"); + } + uiFile = QtProject.CopyFileToFolder(uiFile, location, data.UiFile); + + QtProject.ReplaceTokenInFile(uiFile, "%CLASS%", data.ClassName); + QtProject.ReplaceTokenInFile(uiFile, "%BASECLASS%", data.BaseClass); + if (data.BaseClass == "QMainWindow") { + QtProject.ReplaceTokenInFile(uiFile, "%CENTRAL_WIDGET%", + "\r\n <widget class=\"QMenuBar\" name=\"menuBar\" />" + + "\r\n <widget class=\"QToolBar\" name=\"mainToolBar\" />" + + "\r\n <widget class=\"QWidget\" name=\"centralWidget\" />" + + "\r\n <widget class=\"QStatusBar\" name=\"statusBar\" />"); + } else if (data.BaseClass == "QDockWidget") { + QtProject.ReplaceTokenInFile(uiFile, "%CENTRAL_WIDGET%", + "\r\n <widget class=\"QWidget\" name=\"widget\" />"); + } else { + QtProject.ReplaceTokenInFile(uiFile, "%CENTRAL_WIDGET%", string.Empty); + } + + return uiFile; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/IClassWizard.cs b/QtVsTools.Wizards/Wizards/ClassWizard/IClassWizard.cs new file mode 100644 index 0000000..87adaac --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/IClassWizard.cs @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Wizards.ClassWizard +{ + public interface IClassWizard + { + WizardResult Run(EnvDTE.DTE dte, string name, string location); + } +} diff --git a/QtVsTools.Wizards/Wizards/ClassWizard/UiClassInclusion.cs b/QtVsTools.Wizards/Wizards/ClassWizard/UiClassInclusion.cs new file mode 100644 index 0000000..8fd1b01 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ClassWizard/UiClassInclusion.cs @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Wizards.ClassWizard +{ + public enum UiClassInclusion + { + Member, + MemberPointer, + MultipleInheritance + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml b/QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml new file mode 100644 index 0000000..3227854 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml @@ -0,0 +1,298 @@ +<!-- +***************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +***************************************************************************** +--> +<wizards:WizardPage x:Class="QtVsTools.Wizards.ProjectWizard.ConfigPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" KeepAlive="True" + mc:Ignorable="d" d:DesignHeight="445" d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" VerticalAlignment="Top" + Margin="0,25,0,0" RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" Grid.Row="0"> + <Run FontWeight="Bold" Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + <LineBreak /> + </TextBlock> + <Grid Grid.Row="1" Name="ModuleGrid"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <Style TargetType="{x:Type ListBox}"> + <Setter Property="Background" Value="Transparent" /> + </Style> + </Grid.Resources> + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <DataGrid Margin="0,20,0,0" Name="ConfigTable" AutoGenerateColumns="False" + IsReadOnly="True" BorderThickness="1" BorderBrush="LightGray" + GridLinesVisibility="All" HorizontalGridLinesBrush="LightGray" + VerticalGridLinesBrush="LightGray" CanUserReorderColumns="False" + CanUserSortColumns="False" CanUserResizeRows="False" RowHeaderWidth="0" + FrozenColumnCount="1" Grid.Row="0"> + <DataGrid.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" /> + <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" /> + </DataGrid.Resources> + <DataGrid.CellStyle> + <Style TargetType="DataGridCell"> + <Setter Property="BorderThickness" Value="0" /> + <Setter Property="FocusVisualStyle" Value="{x:Null}" /> + </Style> + </DataGrid.CellStyle> + <DataGrid.Columns> + <DataGridTemplateColumn Header="Configuration"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> + <Button Cursor="Hand" ButtonBase.Click="RemoveConfig_Click"> + <Button.Template> + <ControlTemplate TargetType="Button"> + <Grid HorizontalAlignment="Center" VerticalAlignment="Center" + Margin="2,0"> + <Ellipse Fill="Red" Width="13" Height="13" /> + <Rectangle Width="8" Height="2" Fill="White" /> + </Grid> + </ControlTemplate> + </Button.Template> + </Button> + <Button Cursor="Hand" ButtonBase.Click="DuplicateConfig_Click"> + <Button.Template> + <ControlTemplate TargetType="Button"> + <Grid HorizontalAlignment="Center" VerticalAlignment="Center" + Margin="2,0"> + <Ellipse Fill="#FF36B31A" Width="13" Height="13" /> + <Rectangle Width="8" Height="2" Fill="White" /> + <Rectangle Width="2" Height="8" Fill="White" /> + </Grid> + </ControlTemplate> + </Button.Template> + </Button> + <TextBox Text="{Binding Name}" Margin="2,8" BorderThickness="0" + Background="Transparent" TextBoxBase.TextChanged="Name_TextChanged"> + <TextBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + </TextBox.Resources> + </TextBox> + </StackPanel> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Qt Version"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <ComboBox Text="{Binding QtVersion}" IsEditable="True" Height="22" + SelectedIndex="0" BorderThickness="0" Background="Transparent" + FrameworkElement.Loaded="QtVersion_ComboBox_Loaded" + TextBoxBase.TextChanged="QtVersion_TextChanged"> + <ComboBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + </ComboBox.Resources> + </ComboBox> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Target"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <ComboBox Text="{Binding Target}" IsEditable="True" Height="22" + SelectedIndex="0" BorderThickness="0" Background="Transparent" + FrameworkElement.Loaded="Target_ComboBox_Loaded" + TextBoxBase.TextChanged="Target_TextChanged"> + <ComboBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + </ComboBox.Resources> + </ComboBox> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Platform"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <ComboBox Text="{Binding Platform}" IsEditable="True" Height="22" + SelectedIndex="0" BorderThickness="0" Background="Transparent" + FrameworkElement.Loaded="Platform_ComboBox_Loaded" + TextBoxBase.TextChanged="Platform_TextChanged"> + <ComboBox.Resources> + <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" + Color="LimeGreen" /> + </ComboBox.Resources> + </ComboBox> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Debug"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" + IsChecked="{Binding IsDebug}" Click="Debug_Click" /> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + <DataGridTemplateColumn Header="Qt Modules" Width="*"> + <DataGridTemplateColumn.CellTemplate> + <DataTemplate> + <ComboBox Name="Modules"> + <ComboBox.Template> + <ControlTemplate TargetType="ComboBox"> + <Grid> + <ListView Name="SelectedModules" ItemsSource="{Binding SelectedModules}" + ScrollViewer.HorizontalScrollBarVisibility="Disabled" + BorderThickness="0" IsEnabled="False"> + <ListView.ItemsPanel> + <ItemsPanelTemplate> + <WrapPanel /> + </ItemsPanelTemplate> + </ListView.ItemsPanel> + <ListView.ItemTemplate> + <DataTemplate> + <Grid> + <Rectangle Fill="#FF36B31A" RadiusX="4" RadiusY="4" Height="20" /> + <Label Content="{Binding Name}" Foreground="White" /> + </Grid> + </DataTemplate> + </ListView.ItemTemplate> + </ListView> + <ToggleButton Opacity="0" Name="ToggleButton" Focusable="false" + IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" + ClickMode="Press" Cursor="Hand" /> + <Popup Name="Popup" Placement="Bottom" + IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" + Focusable="False" PopupAnimation="Slide"> + <Grid Name="DropDown" SnapsToDevicePixels="True" + MinWidth="{TemplateBinding ActualWidth}" + MaxHeight="{TemplateBinding MaxDropDownHeight}"> + <Border x:Name="DropDownBorder" Background="White" + BorderThickness="2" BorderBrush="Gray" /> + <ListView Name="PopupListBox" BorderThickness="0" + ScrollViewer.VerticalScrollBarVisibility="Disabled" + ScrollViewer.HorizontalScrollBarVisibility="Auto" + Margin="10,10,10,10" ItemsSource="{Binding AllModules}"> + <ListView.ItemsPanel> + <ItemsPanelTemplate> + <WrapPanel Orientation="Vertical" /> + </ItemsPanelTemplate> + </ListView.ItemsPanel> + <ListView.ItemTemplate> + <DataTemplate> + <CheckBox Content="{Binding Name}" + IsChecked="{Binding IsSelected}" + IsEnabled="{Binding IsEnabled}" VerticalAlignment="Center" + VerticalContentAlignment="Center" Click="Module_Click" + Margin="4"> + <CheckBox.ContentTemplate> + <DataTemplate> + <Grid> + <Rectangle Fill="#FF36B31A" RadiusX="4" RadiusY="4" + Height="20" /> + <Label Content="{Binding}" Foreground="White" /> + </Grid> + </DataTemplate> + </CheckBox.ContentTemplate> + </CheckBox> + </DataTemplate> + </ListView.ItemTemplate> + <ListView.ItemContainerStyle> + <Style TargetType="ListViewItem"> + <Style.Setters> + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="ListViewItem"> + <ContentPresenter + HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" + VerticalAlignment="{TemplateBinding VerticalContentAlignment}" + SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style.Setters> + </Style> + </ListView.ItemContainerStyle> + </ListView> + </Grid> + </Popup> + </Grid> + </ControlTemplate> + </ComboBox.Template> + </ComboBox> + </DataTemplate> + </DataGridTemplateColumn.CellTemplate> + </DataGridTemplateColumn> + </DataGrid.Columns> + </DataGrid> + <StackPanel Name="ErrorPanel" Orientation="Horizontal" Grid.Row="1" Margin="0,10" + Visibility="Hidden"> + <Image Name="ErrorIcon" Height="16" Width="16" Margin="5,0" /> + <Label Name="ErrorMsg" Foreground="Red" /> + </StackPanel> + </Grid> + </Grid> + <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Grid.Row="2" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" MinWidth="75">< _Previous</Button> + <Button MinWidth="75" Name="NextButton" Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" Click="OnFinishButtonClick" Margin="10,0,0,0" IsDefault="True" + IsEnabled="{Binding Path=FinishButtonEnabled}" Name="FinishButton" + VerticalAlignment="Bottom">_Finish</Button> + <Button Click="OnCancelButtonClick" MinWidth="75" Margin="10,0,0,0" Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml.cs new file mode 100644 index 0000000..fb0a4e8 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/ConfigPage.xaml.cs @@ -0,0 +1,478 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Microsoft.Win32; +using QtVsTools.Common; + +namespace QtVsTools.Wizards.ProjectWizard +{ + using QtVsTools.Core; + + public partial class ConfigPage : WizardPage + { + interface ICloneable<T> where T : ICloneable<T> + { + T Clone(); + } + + class Module : ICloneable<Module> + { + public string Name { get; set; } + public string Id { get; set; } + public bool IsSelected { get; set; } + public bool IsReadOnly { get; set; } + public bool IsEnabled => !IsReadOnly; + + public Module Clone() + { + return new Module + { + Name = Name, + Id = Id, + IsSelected = IsSelected, + IsReadOnly = IsReadOnly + }; + } + } + + class Config : ICloneable<Config>, IWizardConfiguration + { + public string Name { get; set; } + public VersionInformation QtVersion { get; set; } + public string QtVersionName { get; set; } + public string QtVersionPath { get; set; } + public string Target { get; set; } + public string Platform { get; set; } + public bool IsDebug { get; set; } + + public Dictionary<string, Module> Modules { get; set; } + + public IEnumerable<Module> AllModules + => Modules.Values; + public IEnumerable<Module> SelectedModules + => Modules.Values.Where((Module m) => m.IsSelected); + + IEnumerable<string> IWizardConfiguration.Modules + => SelectedModules.SelectMany((Module m) => m.Id.Split(' ')); + + public Config Clone() + { + return new Config + { + Name = Name, + QtVersion = QtVersion, + QtVersionName = QtVersionName, + Target = Target, + Platform = Platform, + IsDebug = IsDebug, + Modules = AllModules + .Select((Module m) => m.Clone()) + .ToDictionary((Module m) => m.Name) + }; + } + } + + class CloneableList<T> : List<T> where T : ICloneable<T> + { + public CloneableList() : base() + { } + + public CloneableList(IEnumerable<T> collection) : base(collection) + { } + + public CloneableList<T> Clone() + { + return new CloneableList<T>(this.Select(x => x.Clone())); + } + } + + const string QT_VERSION_DEFAULT = "<Default>"; + const string QT_VERSION_BROWSE = "<Browse...>"; + + IEnumerable<string> qtVersionList = new[] { QT_VERSION_DEFAULT, QT_VERSION_BROWSE } + .Union(QtVersionManager.The().GetVersions()); + + QtVersionManager qtVersionManager = QtVersionManager.The(); + VersionInformation defaultQtVersionInfo; + + CloneableList<Config> defaultConfigs; + List<Config> currentConfigs; + bool initialNextButtonIsEnabled; + bool initialFinishButtonIsEnabled; + + public ConfigPage() + { + InitializeComponent(); + + string defaultQtVersionName = qtVersionManager.GetDefaultVersion(); + defaultQtVersionInfo = qtVersionManager.GetVersionInfo(defaultQtVersionName); + + ErrorIcon.Source = Imaging.CreateBitmapSourceFromHIcon( + SystemIcons.Exclamation.Handle, Int32Rect.Empty, + BitmapSizeOptions.FromEmptyOptions()); + + DataContext = this; + Loaded += OnLoaded; + } + + void OnLoaded(object sender, RoutedEventArgs e) + { + Loaded -= OnLoaded; + + var qtModules = QtModules.Instance.GetAvailableModules() + .Where((QtModule mi) => mi.Selectable) + .Select((QtModule mi) => new Module() + { + Name = mi.Name, + Id = mi.proVarQT, + IsSelected = Data.DefaultModules.Contains(mi.LibraryPrefix), + IsReadOnly = Data.DefaultModules.Contains(mi.LibraryPrefix), + }); + + qtVersionList = new[] { QT_VERSION_DEFAULT, QT_VERSION_BROWSE } + .Union(QtVersionManager.The().GetVersions()); + + if (defaultQtVersionInfo == null) { + Validate(); + return; + } + + defaultConfigs = new CloneableList<Config> { + new Config { + Name = "Debug", + IsDebug = true, + QtVersion = defaultQtVersionInfo, + QtVersionName = defaultQtVersionInfo.name, + Target = defaultQtVersionInfo.isWinRT() + ? ProjectTargets.WindowsStore.Cast<string>() + : ProjectTargets.Windows.Cast<string>(), + Platform = defaultQtVersionInfo.is64Bit() + ? ProjectPlatforms.X64.Cast<string>() + : ProjectPlatforms.Win32.Cast<string>(), + Modules = qtModules.ToDictionary((Module m) => m.Name), + }, + new Config { + Name = "Release", + IsDebug = false, + QtVersion = defaultQtVersionInfo, + QtVersionName = defaultQtVersionInfo.name, + Target = defaultQtVersionInfo.isWinRT() + ? ProjectTargets.WindowsStore.Cast<string>() + : ProjectTargets.Windows.Cast<string>(), + Platform = defaultQtVersionInfo.is64Bit() + ? ProjectPlatforms.X64.Cast<string>() + : ProjectPlatforms.Win32.Cast<string>(), + Modules = qtModules.ToDictionary((Module m) => m.Name), + } + }; + currentConfigs = defaultConfigs.Clone(); + ConfigTable.ItemsSource = currentConfigs; + + initialNextButtonIsEnabled = NextButton.IsEnabled; + initialFinishButtonIsEnabled = FinishButton.IsEnabled; + + Validate(); + } + + /// <summary> + /// Callback to validate selected configurations. + /// Must return an error message in case of failed validation. + /// Otherwise, return empty string or null. + /// </summary> + public Func<IEnumerable<IWizardConfiguration>, string> ValidateConfigs { get; set; } + + void Validate() + { + if (currentConfigs == null) { + ErrorMsg.Content = "Register at least one Qt version using \"Qt VS Tools\"" + + " -> \"Qt Options\"."; + ErrorPanel.Visibility = Visibility.Visible; + NextButton.IsEnabled = false; + FinishButton.IsEnabled = false; + } else if (currentConfigs // "$(Configuration)|$(Platform)" must be unique + .GroupBy((Config c) => string.Format("{0}|{1}", c.Name, c.Platform)) + .Where((IGrouping<string, Config> g) => g.Count() > 1) + .Any()) { + ErrorMsg.Content = "(Configuration, Platform) must be unique"; + ErrorPanel.Visibility = Visibility.Visible; + NextButton.IsEnabled = false; + FinishButton.IsEnabled = false; + } else if (ValidateConfigs != null + && ValidateConfigs(currentConfigs) is string errorMsg + && !string.IsNullOrEmpty(errorMsg)) { + ErrorMsg.Content = errorMsg; + ErrorPanel.Visibility = Visibility.Visible; + NextButton.IsEnabled = false; + FinishButton.IsEnabled = false; + } else { + ErrorMsg.Content = string.Empty; + ErrorPanel.Visibility = Visibility.Hidden; + NextButton.IsEnabled = initialNextButtonIsEnabled; + FinishButton.IsEnabled = initialFinishButtonIsEnabled; + } + } + + void RemoveConfig_Click(object sender, RoutedEventArgs e) + { + if (sender is Button buttonRemove + && GetBinding(buttonRemove) is Config config) { + currentConfigs.Remove(config); + if (!currentConfigs.Any()) { + currentConfigs = defaultConfigs.Clone(); + ConfigTable.ItemsSource = currentConfigs; + } + ConfigTable.Items.Refresh(); + Validate(); + } + } + + void DuplicateConfig_Click(object sender, RoutedEventArgs e) + { + if (sender is Button buttonDuplicate + && GetBinding(buttonDuplicate) is Config config) { + currentConfigs.Add(config.Clone()); + ConfigTable.Items.Refresh(); + Validate(); + } + } + + void Name_TextChanged(object sender, TextChangedEventArgs e) + { + if (sender is TextBox txt && GetBinding(txt) is Config cfg) + cfg.Name = txt.Text; + Validate(); + } + + void QtVersion_ComboBox_Loaded(object sender, RoutedEventArgs e) + { + if (sender is ComboBox comboBoxQtVersion + && GetBinding(comboBoxQtVersion) is Config config) { + comboBoxQtVersion.IsEnabled = false; + comboBoxQtVersion.ItemsSource = qtVersionList; + comboBoxQtVersion.Text = config.QtVersionName; + comboBoxQtVersion.IsEnabled = true; + } + } + + void QtVersion_TextChanged(object sender, TextChangedEventArgs e) + { + if (sender is ComboBox comboBoxQtVersion + && comboBoxQtVersion.IsEnabled + && GetBinding(comboBoxQtVersion) is Config config + && config.QtVersionName != comboBoxQtVersion.Text) { + var oldQtVersion = config.QtVersion; + if (comboBoxQtVersion.Text == QT_VERSION_DEFAULT) { + config.QtVersion = defaultQtVersionInfo; + config.QtVersionName = defaultQtVersionInfo.name; + config.QtVersionPath = defaultQtVersionInfo.qtDir; + comboBoxQtVersion.Text = defaultQtVersionInfo.name; + } else if (comboBoxQtVersion.Text == QT_VERSION_BROWSE) { + var openFileDialog = new OpenFileDialog + { + Filter = "qmake (qmake.exe)|qmake.exe" + }; + if (openFileDialog.ShowDialog() == true) { + IEnumerable<string> binPath = Path.GetDirectoryName(openFileDialog.FileName) + .Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + string lastDirName = binPath.LastOrDefault(); + if ("bin".Equals(lastDirName, StringComparison.InvariantCultureIgnoreCase)) + binPath = binPath.Take(binPath.Count() - 1); + + var qtVersion = string.Join( + Path.DirectorySeparatorChar.ToString(), binPath); + var versionInfo = VersionInformation.Get(qtVersion); + if (versionInfo != null) { + versionInfo.name = qtVersion; + config.QtVersion = versionInfo; + config.QtVersionName = versionInfo.name; + config.QtVersionPath = config.QtVersion.qtDir; + } + } + comboBoxQtVersion.Text = config.QtVersionName; + } else if (qtVersionManager.GetVersions().Contains(comboBoxQtVersion.Text)) { + config.QtVersion = qtVersionManager.GetVersionInfo(comboBoxQtVersion.Text); + config.QtVersionName = comboBoxQtVersion.Text; + config.QtVersionPath = qtVersionManager.GetInstallPath(comboBoxQtVersion.Text); + } else { + config.QtVersion = null; + config.QtVersionName = config.QtVersionPath = comboBoxQtVersion.Text; + } + + if (oldQtVersion != config.QtVersion) { + if (config.QtVersion != null) { + config.Target = config.QtVersion.isWinRT() + ? ProjectTargets.WindowsStore.Cast<string>() + : ProjectTargets.Windows.Cast<string>(); + config.Platform = config.QtVersion.is64Bit() + ? ProjectPlatforms.X64.Cast<string>() + : ProjectPlatforms.Win32.Cast<string>(); + } else if (config.QtVersionPath.StartsWith("SSH:")) { + config.Target = ProjectTargets.LinuxSSH.Cast<string>(); + } else if (config.QtVersionPath.StartsWith("WSL:")) { + config.Target = ProjectTargets.LinuxWSL.Cast<string>(); + } + ConfigTable.Items.Refresh(); + } + Validate(); + } + } + + void Target_ComboBox_Loaded(object sender, RoutedEventArgs e) + { + if (sender is ComboBox comboBoxTarget + && GetBinding(comboBoxTarget) is Config config) { + comboBoxTarget.IsEnabled = false; + comboBoxTarget.ItemsSource = EnumExt.GetValues<string>(typeof(ProjectTargets)); + comboBoxTarget.Text = config.Target; + comboBoxTarget.IsEnabled = true; + } + } + + void Target_TextChanged(object sender, TextChangedEventArgs e) + { + if (sender is ComboBox comboBoxTarget + && comboBoxTarget.IsEnabled + && GetBinding(comboBoxTarget) is Config config + && config.Target != comboBoxTarget.Text) { + config.Target = comboBoxTarget.Text; + ConfigTable.Items.Refresh(); + Validate(); + } + } + + void Platform_ComboBox_Loaded(object sender, RoutedEventArgs e) + { + if (sender is ComboBox comboBoxPlatform + && GetBinding(comboBoxPlatform) is Config config) { + comboBoxPlatform.IsEnabled = false; + comboBoxPlatform.ItemsSource = EnumExt.GetValues<string>(typeof(ProjectPlatforms)); + comboBoxPlatform.Text = config.Platform; + comboBoxPlatform.IsEnabled = true; + } + } + + void Platform_TextChanged(object sender, TextChangedEventArgs e) + { + if (sender is ComboBox comboBoxPlatform + && comboBoxPlatform.IsEnabled + && GetBinding(comboBoxPlatform) is Config config + && config.Platform != comboBoxPlatform.Text) { + config.Platform = comboBoxPlatform.Text; + ConfigTable.Items.Refresh(); + Validate(); + } + } + + void Debug_Click(object sender, RoutedEventArgs e) + { + if (sender is CheckBox checkBox && GetBinding(checkBox) is Config config) { + config.IsDebug = checkBox.IsChecked ?? false; + if (config.IsDebug && config.Name.EndsWith("Release")) { + config.Name = string.Format("{0}Debug", + config.Name.Substring(0, config.Name.Length - "Release".Length)); + ConfigTable.Items.Refresh(); + } else if (!config.IsDebug && config.Name.EndsWith("Debug")) { + config.Name = string.Format("{0}Release", + config.Name.Substring(0, config.Name.Length - "Debug".Length)); + ConfigTable.Items.Refresh(); + } + Validate(); + } + } + + void Module_Click(object sender, RoutedEventArgs e) + { + if (sender is CheckBox checkBoxModule + && (checkBoxModule.TemplatedParent as ContentPresenter)?.Content is Module module + && GetBinding(checkBoxModule) is Config config + && FindAncestor(checkBoxModule, "Modules") is ComboBox comboBoxModules + && FindDescendant(comboBoxModules, "SelectedModules") is ListView selectedModules) { + selectedModules.ItemsSource = config.SelectedModules; + Validate(); + } + } + + protected override void OnNextButtonClick(object sender, RoutedEventArgs e) + { + Data.Configs = currentConfigs.Cast<IWizardConfiguration>(); + base.OnNextButtonClick(sender, e); + } + + protected override void OnFinishButtonClick(object sender, RoutedEventArgs e) + { + Data.Configs = currentConfigs.Cast<IWizardConfiguration>(); + base.OnFinishButtonClick(sender, e); + } + + static object GetBinding(FrameworkElement control) + { + if (control.BindingGroup == null + || control.BindingGroup.Items == null + || control.BindingGroup.Items.Count == 0) { + return null; + } + return control.BindingGroup.Items[0]; + } + + static FrameworkElement FindAncestor(FrameworkElement control, string name) + { + while (control != null && control.Name != name) { + object parent = control.Parent + ?? control.TemplatedParent + ?? VisualTreeHelper.GetParent(control); + control = parent as FrameworkElement; + } + return control; + } + + static FrameworkElement FindDescendant(FrameworkElement control, string name) + { + var stack = new Stack<FrameworkElement>(new[] { control }); + while (stack.Any()) { + control = stack.Pop(); + if (control?.Name == name && control is FrameworkElement result) + return result; + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(control); ++i) { + if (VisualTreeHelper.GetChild(control, i) is FrameworkElement child) + stack.Push(child); + } + } + return null; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Console/ConsoleWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Console/ConsoleWizard.cs new file mode 100644 index 0000000..9a253cc --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Console/ConsoleWizard.cs @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using QtVsTools.Common; + +namespace QtVsTools.Wizards.ProjectWizard +{ + public class ConsoleWizard : ProjectTemplateWizard + { + protected override Options TemplateType => Options.Application | Options.ConsoleSystem; + + WizardData _WizardData; + protected override WizardData WizardData => _WizardData + ?? (_WizardData = new WizardData + { + DefaultModules = new List<string> { "QtCore" } + }); + + WizardWindow _WizardWindow; + protected override WizardWindow WizardWindow => _WizardWindow + ?? (_WizardWindow = new WizardWindow(title: "Qt Console Application Wizard") + { + new WizardIntroPage { + Data = WizardData, + Header = @"Welcome to the Qt Console Application Wizard", + Message = @"This wizard generates a Qt console application " + + @"project. The application derives from QCoreApplication " + + @"and does not present a GUI." + System.Environment.NewLine + + System.Environment.NewLine + "To continue, click Next.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new ConfigPage { + Data = WizardData, + Header = @"Welcome to the Qt Console Application Wizard", + Message = + @"Setup the configurations you want to include in your project. " + + @"The recommended settings for this project are selected by default.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true, + ValidateConfigs = ValidateConfigsForConsoleApp + } + }); + + string ValidateConfigsForConsoleApp(IEnumerable<IWizardConfiguration> configs) + { + foreach (var config in configs) { + if (config.Target.EqualTo(ProjectTargets.WindowsStore)) { + return string.Format( + "Console Application project not available for the '{0}' target.", + config.Target); + } + } + return string.Empty; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml b/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml new file mode 100644 index 0000000..6fd20d5 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml @@ -0,0 +1,310 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.ProjectWizard.DesignerPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:util="clr-namespace:QtVsTools.Wizards.Util" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="445" + d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" + VerticalAlignment="Top" + Margin="0,25,0,0" + RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" + ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" + Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" + Grid.Row="0"> + <Run FontWeight="Bold" + Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + <LineBreak /> + </TextBlock> + <Grid Grid.Row="1"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <Style TargetType="{x:Type TextBox}"> + <Style.Triggers> + <Trigger Property="Validation.HasError" + Value="true"> + <Setter Property="ToolTip" + Value="{Binding RelativeSource={RelativeSource Self}, + Path=(Validation.Errors)[0].ErrorContent}" /> + </Trigger> + </Style.Triggers> + </Style> + </Grid.Resources> + <TextBlock Grid.Row="0" + Margin="0,0,10,5"> + <Run FontWeight="Bold" + Text="Custom Widget "/> + <Run Text="- Class Name:"/> + </TextBlock> + <TextBox Grid.Row="1" + Margin="0,0,10,10" + x:Name="ClassName" + TextChanged="OnClassNameChanged"> + <TextBox.Text> + <Binding Path="Data.ClassName" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Column="1" + Grid.Row="0" + Text="Base class:" + Margin="0,0,0,5" /> + <TextBox Grid.Column="1" + Grid.Row="1" + Name="BaseClass" + Margin="0,0,0,10"> + <TextBox.Text> + <Binding Path="Data.BaseClass" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule AllowEmptyIdentifier="True"/> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Row="2" + Margin="0,0,10,5" + Text="Header (.h) file:" /> + <TextBox Grid.Row="3" + Margin="0,0,10,30" + Name="ClassHeaderFile"> + <TextBox.Text> + <Binding Path="Data.ClassHeaderFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".h" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Column="1" + Grid.Row="2" + Margin="0,0,0,5" + Text="Source (.cpp) file:" /> + <TextBox Grid.Column="1" + Grid.Row="3" + Margin="0,0,0,30" + Name="ClassSourceFile"> + <TextBox.Text> + <Binding Path="Data.ClassSourceFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".cpp" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Column="0" + Grid.Row="4" + Margin="0,0,0,5" + > + <Run FontWeight="Bold" + Text="Plugin"/> + <Run Text=" - Class Name:"/> + </TextBlock> + <TextBox Grid.Column="0" + Grid.Row="5" + Margin="0,0,0,10" + Name="PluginClass"> + <TextBox.Text> + <Binding Path="Data.PluginClass" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Row="6" + Margin="0,0,10,5" + Text="Header (.h) file:" /> + <TextBox Grid.Row="7" + Margin="0,0,10,0" + Name="PluginHeaderFile"> + <TextBox.Text> + <Binding Path="Data.PluginHeaderFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".h" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Column="1" + Grid.Row="6" + Margin="0,0,0,5" + Text="Source (.cpp) file:" /> + <TextBox Grid.Column="1" + Grid.Row="7" + Margin="0" + Name="PluginSourceFile"> + <TextBox.Text> + <Binding Path="Data.PluginSourceFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".cpp" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <CheckBox Grid.Row="8" + Content="Lower case file names" + Margin="0,20,0,5" + Name="LowerCaseFileNames" + Click="OnLowerCaseFileNamesClick" /> + <CheckBox Grid.Row="9" + Content="Precompiled header" + Margin="0,10,0,0" + IsChecked="{Binding Path=Data.UsePrecompiledHeader}" /> + </Grid> + <StackPanel HorizontalAlignment="Right" + Orientation="Horizontal" + Grid.Row="2" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" + Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" + MinWidth="75">< _Previous</Button> + <Button MinWidth="75" + Name="NextButton" + Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" + Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" + Click="OnFinishButtonClick" + Margin="10,0,0,0" + IsDefault="True" + Name="FinishButton" + Content="_Finish" + VerticalAlignment="Bottom"> + <Button.Style> + <Style TargetType="{x:Type Button}"> + <Setter Property="IsEnabled" + Value="false" /> + <Style.Triggers> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding Path=FinishButtonEnabled}" + Value="true" /> + <Condition Binding="{Binding ElementName=ClassName, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=BaseClass, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassHeaderFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassSourceFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=PluginClass, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=PluginHeaderFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=PluginSourceFile, + Path=(Validation.HasError)}" + Value="false" /> + </MultiDataTrigger.Conditions> + <Setter Property="IsEnabled" + Value="true" /> + </MultiDataTrigger> + </Style.Triggers> + </Style> + </Button.Style> + </Button> + <Button Click="OnCancelButtonClick" + MinWidth="75" + Margin="10,0,0,0" + Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" + IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml.cs new file mode 100644 index 0000000..c289956 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerPage.xaml.cs @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Windows; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.ProjectWizard +{ + public partial class DesignerPage : WizardPage + { + public DesignerPage() + { + InitializeComponent(); + DataContext = this; + } + + private void OnClassNameChanged(object sender, TextChangedEventArgs e) + { + UpdateFileNames(); + } + + private void OnLowerCaseFileNamesClick(object sender, RoutedEventArgs e) + { + UpdateFileNames(); + } + + private void UpdateFileNames() + { + var filename = ClassName.Text; + var pluginFilename = PluginClass.Text; + if (LowerCaseFileNames.IsChecked.GetValueOrDefault()) { + filename = filename.ToLower(); + pluginFilename = pluginFilename.ToLower(); + } + + ClassHeaderFile.Text = filename + @".h"; + ClassSourceFile.Text = filename + @".cpp"; + + PluginHeaderFile.Text = pluginFilename + @".h"; + PluginSourceFile.Text = pluginFilename + @".cpp"; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerWizard.cs new file mode 100644 index 0000000..326aa5a --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Designer/DesignerWizard.cs @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Controls; +using EnvDTE; +using QtVsTools.Common; + +namespace QtVsTools.Wizards.ProjectWizard +{ + using static EnumExt; + + public class DesignerWizard : ProjectTemplateWizard + { + protected override Options TemplateType => + Options.PluginProject | Options.DynamicLibrary | Options.GUISystem; + + enum NewClass + { + [String("classname")] ClassName, + [String("baseclass")] BaseClass, + [String("sourcefilename")] SourceFileName, + [String("headerfilename")] HeaderFileName, + [String("include")] Include, + } + + enum NewDesignerPlugin + { + [String("plugin_class")] ClassName, + [String("objname")] ObjectName, + [String("pluginsourcefilename")] SourceFileName, + [String("pluginheaderfilename")] HeaderFileName, + [String("plugin_json")] JsonFileName, + } + + WizardData _WizardData; + protected override WizardData WizardData => _WizardData + ?? (_WizardData = new WizardData + { + DefaultModules = new List<string> { + "QtCore", "QtGui", "QtWidgets", "QtXml" + } + }); + + string[] _ExtraModules; + protected override IEnumerable<string> ExtraModules => _ExtraModules + ?? (_ExtraModules = new[] { "designer" }); + + string[] _ExtraDefines; + protected override IEnumerable<string> ExtraDefines => _ExtraDefines + ?? (_ExtraDefines = new[] { "QT_PLUGIN" }); + + WizardWindow _WizardWindow; + protected override WizardWindow WizardWindow => _WizardWindow + ?? (_WizardWindow = new WizardWindow(title: "Qt Custom Designer Widget") + { + new WizardIntroPage { + Data = WizardData, + Header = @"Welcome to the Qt Custom Designer Widget", + Message = @"This wizard generates a custom designer widget which can be " + + @"used in Qt Designer or Visual Studio." + + System.Environment.NewLine + System.Environment.NewLine + + "To continue, click Next.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new ConfigPage { + Data = WizardData, + Header = @"Welcome to the Qt Custom Designer Widget", + Message = + @"Setup the configurations you want to include in your project. " + + @"The recommended settings for this project are selected by default.", + PreviousButtonEnabled = true, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true, + ValidateConfigs = ValidateConfigsForDesignerWidget + }, + new DesignerPage { + Data = WizardData, + Header = @"Welcome to the Qt Custom Designer Widget", + Message = @"This wizard generates a custom designer widget which can be " + + @"used in Qt Designer or Visual Studio.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true + } + } + ); + + string ValidateConfigsForDesignerWidget(IEnumerable<IWizardConfiguration> configs) + { + foreach (var config in configs) { + if (config.Target.EqualTo(ProjectTargets.WindowsStore)) { + return string.Format( + "Custom Designer Widget project not available for the '{0}' target.", + config.Target); + } + } + return string.Empty; + } + + protected override void BeforeWizardRun() + { + var className = Parameter[NewProject.SafeName]; + className = Regex.Replace(className, @"[^a-zA-Z0-9_]", string.Empty); + className = Regex.Replace(className, @"^[\d-]*\s*", string.Empty); + var result = new Util.ClassNameValidationRule().Validate(className, null); + if (result != ValidationResult.ValidResult) + className = @"MyDesignerWidget"; + + WizardData.ClassName = className; + WizardData.BaseClass = @"QWidget"; + WizardData.ClassHeaderFile = className + @".h"; + WizardData.ClassSourceFile = className + @".cpp"; + + WizardData.PluginClass = className + @"Plugin"; + WizardData.PluginHeaderFile = WizardData.PluginClass + @".h"; + WizardData.PluginSourceFile = WizardData.PluginClass + @".cpp"; + } + + protected override void BeforeTemplateExpansion() + { + Parameter[NewClass.ClassName] = WizardData.ClassName; + Parameter[NewClass.BaseClass] = WizardData.BaseClass; + Parameter[NewClass.HeaderFileName] = WizardData.ClassHeaderFile; + Parameter[NewClass.SourceFileName] = WizardData.ClassSourceFile; + + var include = new StringBuilder(); + include.AppendLine(string.Format("#include \"{0}\"", WizardData.ClassHeaderFile)); + if (UsePrecompiledHeaders) + include.AppendLine(string.Format("#include \"{0}\"", PrecompiledHeader.Include)); + Parameter[NewClass.Include] = FormatParam(include); + + Parameter[NewDesignerPlugin.ClassName] = WizardData.PluginClass; + Parameter[NewDesignerPlugin.HeaderFileName] = WizardData.PluginHeaderFile; + Parameter[NewDesignerPlugin.SourceFileName] = WizardData.PluginSourceFile; + Parameter[NewDesignerPlugin.JsonFileName] = WizardData.PluginClass.ToLower() + ".json"; + Parameter[NewDesignerPlugin.ObjectName] = string.Format("{0}{1}", + WizardData.ClassName[0], + WizardData.ClassName.Substring(1)); + } + + protected override void OnProjectGenerated(Project project) + { + project.Globals["IsDesignerPlugin"] = true.ToString(); + if (!project.Globals.get_VariablePersists("IsDesignerPlugin")) + project.Globals.set_VariablePersists("IsDesignerPlugin", true); + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Empty/EmptyWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Empty/EmptyWizard.cs new file mode 100644 index 0000000..28f8ac6 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Empty/EmptyWizard.cs @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace QtVsTools.Wizards.ProjectWizard +{ + public class EmptyWizard : ProjectTemplateWizard + { + protected override Options TemplateType => Options.Application | Options.GUISystem; + + WizardData _WizardData; + protected override WizardData WizardData => _WizardData + ?? (_WizardData = new WizardData + { + DefaultModules = new List<string> { } + }); + + WizardWindow _WizardWindow; + protected override WizardWindow WizardWindow => _WizardWindow + ?? (_WizardWindow = new WizardWindow(title: "Qt Empty Application Wizard") + { + new WizardIntroPage { + Data = WizardData, + Header = @"Welcome to the Qt Empty Application Wizard", + Message = @"This wizard generates an empty Qt application project." + + System.Environment.NewLine + + "Click Finish to create the project.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new ConfigPage { + Data = WizardData, + Header = @"Welcome to the Qt Empty Application Wizard", + Message = + @"Setup the configurations you want to include in your project. " + + @"The recommended settings for this project are selected by default.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true, + } + }); + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml b/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml new file mode 100644 index 0000000..77b2708 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml @@ -0,0 +1,261 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.ProjectWizard.GuiPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:util="clr-namespace:QtVsTools.Wizards.Util" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="445" + d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" + VerticalAlignment="Top" + Margin="0,25,0,0" + RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" + ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" + Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" + Grid.Row="0"> + <Run FontWeight="Bold" + Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + <LineBreak /> + </TextBlock> + <Grid Grid.Row="1"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <Style TargetType="{x:Type TextBox}"> + <Style.Triggers> + <Trigger Property="Validation.HasError" + Value="true"> + <Setter Property="ToolTip" + Value="{Binding RelativeSource={RelativeSource Self}, + Path=(Validation.Errors)[0].ErrorContent}" /> + </Trigger> + </Style.Triggers> + </Style> + </Grid.Resources> + <StackPanel Grid.Column="0"> + <TextBlock Text="Class Name:" + Margin="0,0,10,0" /> + <TextBox Margin="0,0,10,15" + Name="ClassName" + TextChanged="OnClassNameChanged" + TabIndex="0"> + <TextBox.Text> + <Binding Path="Data.ClassName" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Text="Header (.h) file:" + Margin="0,14,10,5" /> + <TextBox Name="ClassHeaderFile" + Margin="0,0,10,10" + TabIndex="2"> + <TextBox.Text> + <Binding Path="Data.ClassHeaderFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".h" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Text="User Interface (.ui) file:" + Margin="0,0,10,5" /> + <TextBox Name="UiFile" + Margin="0,0,10,10" + TabIndex="4"> + <TextBox.Text> + <Binding Path="Data.UiFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".ui" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <CheckBox Content="Lower case file names" + Margin="0,10,0,5" + Name="LowerCaseFileNames" + TabIndex="6" + Click="OnLowerCaseFileNamesClick" /> + <CheckBox Content="Precompiled header" + Margin="0,10,0,5" + IsChecked="{Binding Path=Data.UsePrecompiledHeader}" + TabIndex="7" /> + <CheckBox Content="Add default application icon" + Margin="0,0,0,5" + IsChecked="{Binding Path=Data.AddDefaultAppIcon}" + TabIndex="8" /> + </StackPanel> + <StackPanel Grid.Column="1"> + <TextBlock Text="Base class:" /> + <ComboBox Margin="0,0,0,15" + IsReadOnly="True" + SelectedIndex="0" + Text="{Binding Path=Data.BaseClass}" + IsSynchronizedWithCurrentItem="True" + TabIndex="1"> + <ComboBoxItem Content="QMainWindow" + IsSelected="True" /> + <ComboBoxItem Content="QWidget" /> + <ComboBoxItem Content="QDialog" /> + </ComboBox> + <TextBlock Text="Source (.cpp) file:" + Margin="0,10,0,5" /> + <TextBox Name="ClassSourceFile" + Margin="0,0,0,10" + TabIndex="3"> + <TextBox.Text> + <Binding Path="Data.ClassSourceFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".cpp" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Margin="0,0,0,5" + Text="Resource (.qrc) file:" /> + <TextBox Name="QrcFile" + Grid.Row="5" + Margin="0,0,0,10" + TabIndex="5"> + <TextBox.Text> + <Binding Path="Data.QrcFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".qrc" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + </StackPanel> + </Grid> + <StackPanel HorizontalAlignment="Right" + Orientation="Horizontal" + Grid.Row="2" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" + Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" + MinWidth="75">< _Previous</Button> + <Button MinWidth="75" + Name="NextButton" + Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" + Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" + Click="OnFinishButtonClick" + Margin="10,0,0,0" + IsDefault="True" + Name="FinishButton" + Content="_Finish" + VerticalAlignment="Bottom"> + <Button.Style> + <Style TargetType="{x:Type Button}"> + <Setter Property="IsEnabled" + Value="false" /> + <Style.Triggers> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding Path=FinishButtonEnabled}" + Value="true" /> + <Condition Binding="{Binding ElementName=ClassName, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassHeaderFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassSourceFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=UiFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=QrcFile, + Path=(Validation.HasError)}" + Value="false" /> + </MultiDataTrigger.Conditions> + <Setter Property="IsEnabled" + Value="true" /> + </MultiDataTrigger> + </Style.Triggers> + </Style> + </Button.Style> + </Button> + <Button Click="OnCancelButtonClick" + MinWidth="75" + Margin="10,0,0,0" + Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" + IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml.cs new file mode 100644 index 0000000..70d7632 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiPage.xaml.cs @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Windows; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.ProjectWizard +{ + public partial class GuiPage : WizardPage + { + public GuiPage() + { + InitializeComponent(); + DataContext = this; + } + + private void OnClassNameChanged(object sender, TextChangedEventArgs e) + { + UpdateFileNames(); + } + + private void OnLowerCaseFileNamesClick(object sender, RoutedEventArgs e) + { + UpdateFileNames(); + } + + private void UpdateFileNames() + { + var filename = ClassName.Text; + if (LowerCaseFileNames.IsChecked.GetValueOrDefault()) + filename = filename.ToLower(); + + ClassHeaderFile.Text = filename + @".h"; + ClassSourceFile.Text = filename + @".cpp"; + UiFile.Text = filename + @".ui"; + QrcFile.Text = filename + @".qrc"; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiWizard.cs new file mode 100644 index 0000000..ded5f15 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Gui/GuiWizard.cs @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Controls; +using EnvDTE; +using QtVsTools.Core; +using QtVsTools.Common; + +namespace QtVsTools.Wizards.ProjectWizard +{ + using static EnumExt; + + public class GuiWizard : ProjectTemplateWizard + { + protected override Options TemplateType => Options.Application | Options.GUISystem; + + readonly Func<IWizardConfiguration, bool> whereConfigTargetIsWindowsStore + = (IWizardConfiguration config) => config.Target.EqualTo(ProjectTargets.WindowsStore); + + enum NewClass + { + [String("classname")] ClassName, + [String("baseclass")] BaseClass, + [String("sourcefilename")] SourceFileName, + [String("headerfilename")] HeaderFileName, + [String("include")] Include, + } + + enum NewGuiProject + { + [String("centralwidget")] CentralWidget, + [String("qrcfilename")] QrcFileName, + [String("uifilename")] UiFileName, + [String("ui_hdr")] UiHeaderName, + } + + WizardData _WizardData; + protected override WizardData WizardData => _WizardData + ?? (_WizardData = new WizardData + { + DefaultModules = new List<string> { "QtCore", "QtGui", "QtWidgets" } + }); + + WizardWindow _WizardWindow; + protected override WizardWindow WizardWindow => _WizardWindow + ?? (_WizardWindow = new WizardWindow(title: "Qt Widgets Application Wizard") + { + new WizardIntroPage + { + Data = WizardData, + Header = @"Welcome to the Qt Widgets Application Wizard", + Message = @"This wizard generates a Qt Widgets application project. The " + + @"application derives from QApplication and includes an empty " + + @"widget." + System.Environment.NewLine + + System.Environment.NewLine + "To continue, click Next.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new ConfigPage + { + Data = WizardData, + Header = @"Welcome to the Qt Widgets Application Wizard", + Message = + @"Setup the configurations you want to include in your project. " + + @"The recommended settings for this project are selected by default.", + PreviousButtonEnabled = true, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new GuiPage + { + Data = WizardData, + Header = @"Welcome to the Qt Widgets Application Wizard", + Message = @"This wizard generates a Qt Widgets application project. The " + + @"application derives from QApplication and includes an empty " + + @"widget.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true + } + }); + + List<ItemDef> _ExtraItems; + protected override IEnumerable<ItemDef> ExtraItems => _ExtraItems; + + public GuiWizard() + { + _ExtraItems = new List<ItemDef> + { + new ItemDef + { + ItemType = "AppxManifest", + Include = "Package.appxmanifest", + Filter = "Resource Files", + WhereConfig = whereConfigTargetIsWindowsStore + }, + new ItemDef + { + ItemType = "Image", + Include = "assets/logo_store.png", + Filter = "Resource Files", + WhereConfig = whereConfigTargetIsWindowsStore + }, + new ItemDef + { + ItemType = "Image", + Include = "assets/logo_620x300.png", + Filter = "Resource Files", + WhereConfig = whereConfigTargetIsWindowsStore + }, + new ItemDef + { + ItemType = "Image", + Include = "assets/logo_150x150.png", + Filter = "Resource Files", + WhereConfig = whereConfigTargetIsWindowsStore + }, + new ItemDef + { + ItemType = "Image", + Include = "assets/logo_44x44.png", + Filter = "Resource Files", + WhereConfig = whereConfigTargetIsWindowsStore + }, + }; + } + + protected override void BeforeWizardRun() + { + var className = Parameter[NewProject.SafeName]; + className = Regex.Replace(className, @"[^a-zA-Z0-9_]", string.Empty); + className = Regex.Replace(className, @"^[\d-]*\s*", string.Empty); + var result = new Util.ClassNameValidationRule().Validate(className, null); + if (result != ValidationResult.ValidResult) + className = @"QtWidgetsApplication"; + + WizardData.ClassName = className; + WizardData.BaseClass = @"QMainWindow"; + WizardData.ClassHeaderFile = className + @".h"; + WizardData.ClassSourceFile = className + @".cpp"; + WizardData.UiFile = WizardData.ClassName + @".ui"; + WizardData.QrcFile = WizardData.ClassName + @".qrc"; + } + + protected override void BeforeTemplateExpansion() + { + Parameter[NewClass.ClassName] = WizardData.ClassName; + Parameter[NewClass.BaseClass] = WizardData.BaseClass; + Parameter[NewClass.HeaderFileName] = WizardData.ClassHeaderFile; + Parameter[NewClass.SourceFileName] = WizardData.ClassSourceFile; + Parameter[NewGuiProject.UiFileName] = WizardData.UiFile; + + var include = new StringBuilder(); + include.AppendLine(string.Format("#include \"{0}\"", WizardData.ClassHeaderFile)); + if (UsePrecompiledHeaders) + include.AppendLine(string.Format("#include \"{0}\"", PrecompiledHeader.Include)); + Parameter[NewClass.Include] = FormatParam(include); + + Parameter[NewGuiProject.UiHeaderName] = string.Format("ui_{0}.h", + Path.GetFileNameWithoutExtension(WizardData.UiFile)); + + Parameter[NewGuiProject.QrcFileName] = WizardData.QrcFile; + QtProject.CreateQrcFile( + Parameter[NewProject.DestinationDirectory], + WizardData.ClassName, WizardData.QrcFile); + + if (WizardData.BaseClass == "QMainWindow") { + Parameter[NewGuiProject.CentralWidget] = FormatParam(@" + <widget class=""QMenuBar"" name=""menuBar"" /> + <widget class=""QToolBar"" name=""mainToolBar"" /> + <widget class=""QWidget"" name=""centralWidget"" /> + <widget class=""QStatusBar"" name=""statusBar"" />"); + } + + StringBuilder winRcFile = new StringBuilder(); + + if (WizardData.AddDefaultAppIcon) { + var projectIcon = Path.Combine( + Parameter[NewProject.DestinationDirectory], + Parameter[NewProject.SafeName] + ".ico"); + var iconExists = File.Exists(projectIcon); + if (!iconExists) { + try { + var uri = + new Uri(System.Reflection.Assembly.GetExecutingAssembly().EscapedCodeBase); + var pkgInstallPath + = Path.GetDirectoryName(Uri.UnescapeDataString(uri.AbsolutePath)) + @"\"; + var templateIcon + = Path.Combine(pkgInstallPath, @"ProjectTemplates\VC\Qt\1033\gui\gui.ico"); + File.Copy(templateIcon, projectIcon); + File.SetAttributes(projectIcon, + File.GetAttributes(projectIcon) & (~FileAttributes.ReadOnly)); + iconExists = true; + } catch (Exception /*ex*/) { + // Silently ignore any error, the project is working + // without icon too. + } + } + + if (iconExists) { + _ExtraItems.Add(new ItemDef + { + ItemType = "None", + Include = Parameter[NewProject.SafeName] + ".ico", + Filter = "Resource Files" + }); + winRcFile.AppendLine( + string.Format("IDI_ICON1\t\tICON\t\tDISCARDABLE\t\"{0}.ico\"", + /*{0}*/ Parameter[NewProject.SafeName])); + } + } + + if (winRcFile.Length > 0) { + _ExtraItems.Add(new ItemDef + { + ItemType = "ResourceCompile", + Include = Parameter[NewProject.SafeName] + ".rc", + Filter = "Resource Files" + }); + File.WriteAllText( + Path.Combine( + Parameter[NewProject.DestinationDirectory], + Parameter[NewProject.SafeName] + ".rc"), + winRcFile.ToString()); + } + } + + protected override void OnProjectGenerated(Project project) + { + var qtProject = QtProject.Create(project); + + IWizardConfiguration configWinRT = Configurations + .Where(whereConfigTargetIsWindowsStore) + .FirstOrDefault(); + + if (configWinRT != null) { + var projDir = Parameter[NewProject.DestinationDirectory]; + var qmakeTmpDir = Path.Combine(projDir, "qmake_tmp"); + Directory.CreateDirectory(qmakeTmpDir); + + var dummyPro = Path.Combine(qmakeTmpDir, + string.Format("{0}.pro", Parameter[NewProject.SafeName])); + File.WriteAllText(dummyPro, "SOURCES = main.cpp\r\n"); + + var qmake = new QMakeImport(configWinRT.QtVersion, dummyPro); + qmake.Run(setVCVars: true); + + var qmakeAssetsDir = Path.Combine(qmakeTmpDir, "assets"); + var projAssetsDir = Path.Combine(projDir, "assets"); + if (Directory.Exists(qmakeAssetsDir)) { + if (Directory.Exists(projAssetsDir)) + Directory.Delete(projAssetsDir, recursive: true); + Directory.Move(qmakeAssetsDir, projAssetsDir); + } + + var manifestFile = Path.Combine(qmakeTmpDir, "Package.appxmanifest"); + if (File.Exists(manifestFile)) { + File.Move(manifestFile, Path.Combine(projDir, "Package.appxmanifest")); + } + + Directory.Delete(qmakeTmpDir, recursive: true); + } + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml b/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml new file mode 100644 index 0000000..d678961 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml @@ -0,0 +1,238 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.ProjectWizard.LibraryClassPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:util="clr-namespace:QtVsTools.Wizards.Util" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="445" + d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" + VerticalAlignment="Top" + Margin="0,25,0,0" + RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" + ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" + Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" + Grid.Row="0"> + <Run FontWeight="Bold" + Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + <LineBreak /> + </TextBlock> + <Grid Grid.Row="1"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <Style TargetType="{x:Type TextBox}"> + <Style.Triggers> + <Trigger Property="Validation.HasError" + Value="true"> + <Setter Property="ToolTip" + Value="{Binding RelativeSource={RelativeSource Self}, + Path=(Validation.Errors)[0].ErrorContent}" /> + </Trigger> + </Style.Triggers> + </Style> + </Grid.Resources> + <TextBlock Grid.Row="0" + Text="Class Name:" + Margin="0,0,10,0" /> + <TextBox Grid.Row="1" + Margin="0,0,10,30" + Name="ClassName" + TextChanged="OnClassNameChanged" + TabIndex="0"> + <TextBox.Text> + <Binding Path="Data.ClassName" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <Grid Grid.Row="2" + Grid.ColumnSpan="2"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock Grid.Row="0" + Margin="0,0,10,5" + Text="Header (.h) file:" /> + <TextBox Grid.Row="1" + Margin="0,0,10,0" + Name="ClassHeaderFile" + TabIndex="1"> + <TextBox.Text> + <Binding Path="Data.ClassHeaderFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".h" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Row="0" + Grid.Column="1" + Margin="0,0,0,5" + Text="Source (.cpp) file:" /> + <TextBox Grid.Row="1" + Grid.Column="1" + Margin="0,0,0,00" + Name="ClassSourceFile" + TabIndex="2"> + <TextBox.Text> + <Binding Path="Data.ClassSourceFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".cpp" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + </Grid> + <CheckBox Grid.Row="3" + Content="Lower case file names" + Name="LowerCaseFileNames" + Click="OnLowerCaseFileNamesClick" + Margin="0,20,10,5" + TabIndex="3" /> + <CheckBox Grid.Row="4" + Content="Precompiled header" + IsChecked="{Binding Path=Data.UsePrecompiledHeader}" + Margin="0,10,10,5" + TabIndex="4" /> + <CheckBox Grid.Row="5" + Content="Create Static Library (.lib)" + IsChecked="{Binding Path=Data.CreateStaticLibrary}" + Margin="0,0,10,0" + TabIndex="5" /> + </Grid> + <StackPanel HorizontalAlignment="Right" + Orientation="Horizontal" + Grid.Row="2" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" + Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" + MinWidth="75">< _Previous</Button> + <Button MinWidth="75" + Name="NextButton" + Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" + Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" + Click="OnFinishButtonClick" + Margin="10,0,0,0" + IsDefault="True" + Name="FinishButton" + Content="_Finish" + VerticalAlignment="Bottom"> + <Button.Style> + <Style TargetType="{x:Type Button}"> + <Setter Property="IsEnabled" + Value="false" /> + <Style.Triggers> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding Path=FinishButtonEnabled}" + Value="true" /> + <Condition Binding="{Binding ElementName=ClassName, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassHeaderFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassSourceFile, + Path=(Validation.HasError)}" + Value="false" /> + </MultiDataTrigger.Conditions> + <Setter Property="IsEnabled" + Value="true" /> + </MultiDataTrigger> + </Style.Triggers> + </Style> + </Button.Style> + </Button> + <Button Click="OnCancelButtonClick" + MinWidth="75" + Margin="10,0,0,0" + Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" + IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml.cs new file mode 100644 index 0000000..b7c36c7 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryClassPage.xaml.cs @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Windows; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.ProjectWizard +{ + public partial class LibraryClassPage : WizardPage + { + public LibraryClassPage() + { + InitializeComponent(); + DataContext = this; + } + + private void OnClassNameChanged(object sender, TextChangedEventArgs e) + { + UpdateFileNames(); + } + + private void OnLowerCaseFileNamesClick(object sender, RoutedEventArgs e) + { + UpdateFileNames(); + } + + private void UpdateFileNames() + { + var filename = ClassName.Text; + if (LowerCaseFileNames.IsChecked.GetValueOrDefault()) + filename = filename.ToLower(); + + ClassHeaderFile.Text = filename + @".h"; + ClassSourceFile.Text = filename + @".cpp"; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryWizard.cs new file mode 100644 index 0000000..1ca4f99 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Library/LibraryWizard.cs @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Controls; +using QtVsTools.Common; + +namespace QtVsTools.Wizards.ProjectWizard +{ + using static EnumExt; + + public class LibraryWizard : ProjectTemplateWizard + { + protected override Options TemplateType => Options.GUISystem + | (WizardData.CreateStaticLibrary ? Options.StaticLibrary : Options.DynamicLibrary); + + enum NewLibClass + { + [String("classname")] ClassName, + [String("baseclass")] BaseClass, + [String("sourcefilename")] SourceFileName, + [String("headerfilename")] HeaderFileName, + [String("include")] Include, + [String("saveglobal")] GlobalHeader, + [String("pro_lib_define")] LibDefine, + [String("pro_lib_export")] LibExport, + } + + WizardData _WizardData; + protected override WizardData WizardData => _WizardData + ?? (_WizardData = new WizardData + { + DefaultModules = new List<string> { "QtCore" } + }); + + List<string> _ExtraDefines = new List<string>(); + protected override IEnumerable<string> ExtraDefines => _ExtraDefines; + + WizardWindow _WizardWindow; + protected override WizardWindow WizardWindow => _WizardWindow + ?? (_WizardWindow = new WizardWindow(title: "Qt Class Library Wizard") + { + new WizardIntroPage { + Data = WizardData, + Header = @"Welcome to the Qt Class Library Wizard", + Message = @"This wizard generates a Qt Class Library project. The " + + @"resulting library is linked dynamically with Qt." + + System.Environment.NewLine + System.Environment.NewLine + + @"To continue, click Next.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new ConfigPage { + Data = WizardData, + Header = @"Welcome to the Qt Class Library Wizard", + Message = + @"Setup the configurations you want to include in your project. " + + @"The recommended settings for this project are selected by default.", + PreviousButtonEnabled = true, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new LibraryClassPage { + Data = WizardData, + Header = @"Welcome to the Qt Class Library Wizard", + Message = @"This wizard generates a Qt Class Library project. The " + + @"resulting library is linked dynamically with Qt.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true + } + }); + + protected override void BeforeWizardRun() + { + var safeprojectname = Parameter[NewProject.SafeName]; + safeprojectname = Regex.Replace(safeprojectname, @"[^a-zA-Z0-9_]", string.Empty); + safeprojectname = Regex.Replace(safeprojectname, @"^[\d-]*\s*", string.Empty); + var result = new Util.ClassNameValidationRule().Validate(safeprojectname, null); + if (result != ValidationResult.ValidResult) + safeprojectname = @"QtClassLibrary"; + + WizardData.ClassName = safeprojectname; + WizardData.ClassHeaderFile = safeprojectname + @".h"; + WizardData.ClassSourceFile = safeprojectname + @".cpp"; + } + + protected override void BeforeTemplateExpansion() + { + Parameter[NewLibClass.ClassName] = WizardData.ClassName; + Parameter[NewLibClass.HeaderFileName] = WizardData.ClassHeaderFile; + Parameter[NewLibClass.SourceFileName] = WizardData.ClassSourceFile; + + var include = new StringBuilder(); + include.AppendLine(string.Format("#include \"{0}\"", WizardData.ClassHeaderFile)); + if (UsePrecompiledHeaders) + include.AppendLine(string.Format("#include \"{0}\"", PrecompiledHeader.Include)); + Parameter[NewLibClass.Include] = FormatParam(include); + + var safeprojectname = Parameter[NewProject.SafeName]; + Parameter[NewLibClass.GlobalHeader] = safeprojectname.ToLower(); + Parameter[NewLibClass.LibDefine] = safeprojectname.ToUpper() + "_LIB"; + Parameter[NewLibClass.LibExport] = safeprojectname.ToUpper() + "_EXPORT"; + + _ExtraDefines.Add(Parameter[NewLibClass.LibDefine]); + if (WizardData.CreateStaticLibrary) + _ExtraDefines.Add("BUILD_STATIC"); + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/ProjectTemplateWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/ProjectTemplateWizard.cs new file mode 100644 index 0000000..5f2c619 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/ProjectTemplateWizard.cs @@ -0,0 +1,723 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +using EnvDTE; +using Microsoft.Internal.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TemplateWizard; + +using QtVsTools.Common; +using QtVsTools.VisualStudio; + +using QtVsTools.Core; +using QtVsTools.Core.QtMsBuild; + +namespace QtVsTools.Wizards.ProjectWizard +{ + using static EnumExt; + using WhereConfig = Func<IWizardConfiguration, bool>; + + public interface IWizardConfiguration + { + string Name { get; } + VersionInformation QtVersion { get; } + string QtVersionName { get; } + string QtVersionPath { get; } + string Target { get; } + string Platform { get; } + bool IsDebug { get; } + IEnumerable<string> Modules { get; } + } + + public enum ProjectTargets + { + Windows, + [String("Windows Store")] WindowsStore, + [String("Linux (SSH)")] LinuxSSH, + [String("Linux (WSL)")] LinuxWSL + } + + public enum ProjectPlatforms + { + [String("x64")] X64, + Win32, + ARM64, + ARM, + } + + public abstract class ProjectTemplateWizard : IWizard + { + protected readonly WhereConfig WhereConfig_SelectAll = (x => true); + + protected struct ItemProperty + { + public string Key { get; } + public string Value { get; } + public WhereConfig WhereConfig { get; } + + public ItemProperty(string key, string value, WhereConfig whereConfig = null) + { + Key = key; + Value = value; + WhereConfig = whereConfig; + } + + public static implicit operator ItemProperty[](ItemProperty that) + { + return new[] { that }; + } + } + + protected class ItemGlobalDef + { + public string ItemType { get; set; } + public ItemProperty[] Properties { get; set; } + } + + protected class ItemDef + { + public string ItemType { get; set; } + public string Include { get; set; } + public ItemProperty[] Properties { get; set; } + public string Filter { get; set; } + public WhereConfig WhereConfig { get; set; } + } + + // keep in sync with QtVsTools.Core.TemplateType + + [Flags] + protected enum Options : uint + { + Application = 0x000, + DynamicLibrary = 0x001, + StaticLibrary = 0x002, + GUISystem = 0x004, + ConsoleSystem = 0x008, + PluginProject = 0x100 + } + + protected abstract Options TemplateType { get; } + protected abstract WizardData WizardData { get; } + protected abstract WizardWindow WizardWindow { get; } + + protected virtual IDictionary<string, ItemGlobalDef> ItemGlobals => null; + protected virtual IEnumerable<ItemDef> ExtraItems => Enumerable.Empty<ItemDef>(); + protected virtual IEnumerable<string> ExtraModules => Enumerable.Empty<string>(); + protected virtual IEnumerable<string> ExtraDefines => Enumerable.Empty<string>(); + + protected virtual IEnumerable<IWizardConfiguration> Configurations => WizardData.Configs; + protected virtual bool UsePrecompiledHeaders => WizardData.UsePrecompiledHeader; + + protected Dictionary<string, string> ParameterValues { get; private set; } + protected EnvDTE.DTE Dte { get; private set; } + + ItemDef _PrecompiledHeaderFile; + protected virtual ItemDef PrecompiledHeader => _PrecompiledHeaderFile + ?? (_PrecompiledHeaderFile = new ItemDef + { + ItemType = "ClInclude", + Include = "stdafx.h", + Filter = "Header Files" + }); + + ItemDef _PrecompiledHeaderSourceFile; + protected virtual ItemDef PrecompiledHeaderSource => _PrecompiledHeaderSourceFile + ?? (_PrecompiledHeaderSourceFile = new ItemDef + { + ItemType = "ClCompile", + Include = "stdafx.cpp", + Properties = new ItemProperty("PrecompiledHeader", "Create"), + Filter = "Source Files", + }); + + protected class TemplateParameters + { + public ProjectTemplateWizard Template { get; set; } + + string ParamKey(Enum param) + { + return string.Format("${0}$", param.Cast<string>()); + } + + public string this[Enum param] + { + get => Template.ParameterValues[ParamKey(param)]; + set => Template.ParameterValues[ParamKey(param)] = value; + } + } + + protected enum NewProject + { + // Read-only parameters + [String("projectname")] Name, + [String("safeprojectname")] SafeName, + [String("destinationdirectory")] DestinationDirectory, + [String("solutiondirectory")] SolutionDirectory, + + // Custom parameters + ToolsVersion, + ProjectConfigurations, + Properties, + ProjectGuid, + Keyword, + Globals, + Configurations, + PropertySheets, + QtSettings, + BuildSettings, + ProjectItems, + FilterItems, + } + + TemplateParameters _Parameter; + protected TemplateParameters Parameter => _Parameter + ?? (_Parameter = new TemplateParameters { Template = this }); + + protected QtVersionManager VersionManager => QtVersionManager.The(); + + public virtual void ProjectItemFinishedGenerating(ProjectItem projectItem) { } + public virtual void BeforeOpeningFile(ProjectItem projectItem) { } + public virtual void RunFinished() { } + public virtual bool ShouldAddProjectItem(string filePath) => true; + + protected virtual void BeforeWizardRun() { } + protected virtual void BeforeTemplateExpansion() { } + protected virtual void OnProjectGenerated(Project project) { } + + public virtual void RunStarted( + object automationObject, + Dictionary<string, string> parameterValues, + WizardRunKind runKind, + object[] customParams) + { + Dte = automationObject as DTE; + ParameterValues = parameterValues; + + Debug.Assert(WizardWindow != null); + + BeforeWizardRun(); + + var iVsUIShell = VsServiceProvider.GetService<SVsUIShell, IVsUIShell>(); + if (iVsUIShell == null) + throw new NullReferenceException("IVsUIShell"); + + try { + IntPtr hwnd; + iVsUIShell.EnableModeless(0); + iVsUIShell.GetDialogOwnerHwnd(out hwnd); + WindowHelper.ShowModal(WizardWindow, hwnd); + } catch (QtVSException exception) { + Messages.DisplayErrorMessage(exception.Message); + throw; + } finally { + iVsUIShell.EnableModeless(1); + } + if (!WizardWindow.DialogResult ?? false) { + try { + Directory.Delete(Parameter[NewProject.DestinationDirectory]); + Directory.Delete(Parameter[NewProject.SolutionDirectory]); + } catch { } + throw new WizardBackoutException(); + } + + BeforeTemplateExpansion(); + Expand(); + } + + public virtual void ProjectFinishedGenerating(Project project) + { + OnProjectGenerated(project); + } + + protected static bool IsLinux(IWizardConfiguration wizConfig) + { + return wizConfig.Target.EqualTo(ProjectTargets.LinuxSSH) + || wizConfig.Target.EqualTo(ProjectTargets.LinuxWSL); + } + + protected static string GetLinuxCompilerPath(IWizardConfiguration wizConfig) + { + if (!IsLinux(wizConfig)) + return string.Empty; + if (string.IsNullOrEmpty(wizConfig.QtVersionPath)) + return string.Empty; + string[] linuxPaths = wizConfig.QtVersionPath.Split(':'); + if (linuxPaths == null || linuxPaths.Length <= 2) + return string.Empty; + return linuxPaths[2]; + } + + protected virtual void Expand() + { + Debug.Assert(ParameterValues != null); + Debug.Assert(Dte != null); + Debug.Assert(Configurations != null); + Debug.Assert(ExtraItems != null); + + StringBuilder xml; + + /////////////////////////////////////////////////////////////////////////////////////// + // Tools version = VS version + // + Parameter[NewProject.ToolsVersion] = Dte.Version; + + /////////////////////////////////////////////////////////////////////////////////////// + // Configurations + // + xml = new StringBuilder(); + foreach (IWizardConfiguration c in Configurations) { + xml.AppendLine(string.Format(@" + <ProjectConfiguration Include=""{0}|{1}""> + <Configuration>{0}</Configuration> + <Platform>{1}</Platform> + </ProjectConfiguration>", + /*{0}*/ c.Name, + /*{1}*/ c.Platform)); + } + Parameter[NewProject.ProjectConfigurations] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // Properties + // + xml = new StringBuilder(); + foreach (IWizardConfiguration c in Configurations) { + xml.AppendLine(string.Format(@" + <PropertyGroup Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">", + /*{0}*/ c.Name, + /*{1}*/ c.Platform)); + if (IsLinux(c)) { + var compilerPath = GetLinuxCompilerPath(c); + if (!string.IsNullOrEmpty(compilerPath)) + xml.AppendLine(string.Format(@" + <RemoteCCompileToolExe>{0}</RemoteCCompileToolExe> + <RemoteCppCompileToolExe>{0}</RemoteCppCompileToolExe> + <RemoteLdToolExe>{0}</RemoteLdToolExe>", + /*{0}*/ compilerPath)); + } + xml.AppendLine(@" + </PropertyGroup>"); + } + Parameter[NewProject.Properties] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // Globals + // + xml = new StringBuilder(); + Parameter[NewProject.ProjectGuid] = HelperFunctions.NewProjectGuid(); + Parameter[NewProject.Keyword] = Resources.QtVSVersionTag; + + /////////////////////////////////////////////////////////////////////////////////////// + // Globals: Windows + // + foreach (IWizardConfiguration c in Configurations + .Where(c => c.Target.EqualTo(ProjectTargets.Windows))) { + if (!string.IsNullOrEmpty(c.QtVersion.VC_WindowsTargetPlatformVersion)) { + xml.AppendLine(string.Format(@" + <WindowsTargetPlatformVersion Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">{2}</WindowsTargetPlatformVersion>", + /*{0}*/ c.Name, + /*{1}*/ c.Platform, + /*{2}*/ c.QtVersion.VC_WindowsTargetPlatformVersion)); + } + } + + /////////////////////////////////////////////////////////////////////////////////////// + // Globals: Windows Store + // + foreach (IWizardConfiguration c in Configurations + .Where(c => c.Target.EqualTo(ProjectTargets.WindowsStore))) { + xml.AppendLine(string.Format(@" + <ApplicationType Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">Windows Store</ApplicationType> + <WindowsTargetPlatformVersion Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">{2}</WindowsTargetPlatformVersion> + <WindowsTargetPlatformMinVersion Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">{3}</WindowsTargetPlatformMinVersion> + <MinimumVisualStudioVersion Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">{4}</MinimumVisualStudioVersion> + <ApplicationTypeRevision Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">{5}</ApplicationTypeRevision> + <DefaultLanguage Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">en</DefaultLanguage> + <AppContainerApplication Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">true</AppContainerApplication>", + /*{0}*/ c.Name, + /*{1}*/ c.Platform, + /*{2}*/ c.QtVersion.VC_WindowsTargetPlatformVersion, + /*{3}*/ c.QtVersion.VC_WindowsTargetPlatformMinVersion, + /*{4}*/ c.QtVersion.VC_MinimumVisualStudioVersion, + /*{5}*/ c.QtVersion.VC_ApplicationTypeRevision)); + } + + /////////////////////////////////////////////////////////////////////////////////////// + // Globals: Linux + // + foreach (IWizardConfiguration c in Configurations.Where(c => IsLinux(c))) { + xml.AppendLine(string.Format(@" + <ApplicationType Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">Linux</ApplicationType> + <ApplicationTypeRevision Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">1.0</ApplicationTypeRevision> + <TargetLinuxPlatform Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">Generic</TargetLinuxPlatform> + <LinuxProjectType Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">{{D51BCBC9-82E9-4017-911E-C93873C4EA2B}}</LinuxProjectType>", + /*{0}*/ c.Name, + /*{1}*/ c.Platform)); + } + + Parameter[NewProject.Globals] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // VC Configurations + // + xml = new StringBuilder(); + foreach (IWizardConfiguration c in Configurations) { + ProjectTargets target; + if (!c.Target.TryCast(out target)) + continue; + xml.AppendLine(string.Format(@" + <PropertyGroup Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"" Label=""Configuration"">", + /*{0}*/ c.Name, + /*{1}*/ c.Platform)); + if (TemplateType.HasFlag(Options.DynamicLibrary)) { + xml.AppendLine(@" + <ConfigurationType>DynamicLibrary</ConfigurationType>"); + } else if (TemplateType.HasFlag(Options.StaticLibrary)) { + xml.AppendLine(@" + <ConfigurationType>StaticLibrary</ConfigurationType>"); + } else { + xml.AppendLine(@" + <ConfigurationType>Application</ConfigurationType>"); + } + switch (target) { + case ProjectTargets.Windows: + case ProjectTargets.WindowsStore: + xml.AppendLine(string.Format(@" + <PlatformToolset>v{0}</PlatformToolset>", + /*{0}*/ BuildConfig.PlatformToolset)); + break; + case ProjectTargets.LinuxSSH: + xml.AppendLine(@" + <PlatformToolset>Remote_GCC_1_0</PlatformToolset>"); + break; + case ProjectTargets.LinuxWSL: + xml.AppendLine(@" + <PlatformToolset>WSL_1_0</PlatformToolset>"); + break; + } + if (IsLinux(c)) { + xml.AppendLine(string.Format(@" + <UseDebugLibraries>{0}</UseDebugLibraries>", + /*{0}*/ c.IsDebug ? "true" : "false")); + } else if (target == ProjectTargets.WindowsStore) { + xml.AppendLine(@" + <GenerateManifest>false</GenerateManifest> + <EmbedManifest>false</EmbedManifest>"); + } + xml.AppendLine(@" + </PropertyGroup>"); + } + Parameter[NewProject.Configurations] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // Property sheets + // + xml = new StringBuilder(); + foreach (IWizardConfiguration c in Configurations) { + xml.AppendLine(string.Format(@" + <ImportGroup Label=""PropertySheets"" Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'""> + <Import Project=""$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"" Condition=""exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"" Label=""LocalAppDataPlatform"" /> + <Import Project=""$(QtMsBuild)\Qt.props"" /> + </ImportGroup>", + /*{0}*/ c.Name, + /*{1}*/ c.Platform)); + } + Parameter[NewProject.PropertySheets] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // Qt settings + // + xml = new StringBuilder(); + foreach (IWizardConfiguration c in Configurations) { + xml.AppendLine(string.Format(@" + <PropertyGroup Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"" Label=""QtSettings""> + <QtInstall>{2}</QtInstall> + <QtModules>{3}</QtModules> + <QtBuildConfig>{4}</QtBuildConfig>", + /*{0}*/ c.Name, + /*{1}*/ c.Platform, + /*{2}*/ c.QtVersionName, + /*{3}*/ string.Join(";", c.Modules.Union(ExtraModules)), + /*{4}*/ c.IsDebug ? "debug" : "release")); + if (c.Target.EqualTo(ProjectTargets.WindowsStore)) { + xml.AppendLine(@" + <QtDeploy>true</QtDeploy> + <QtDeployToProjectDir>true</QtDeployToProjectDir> + <QtDeployVsContent>true</QtDeployVsContent>"); + } + xml.AppendLine(@" + </PropertyGroup>"); + } + Parameter[NewProject.QtSettings] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // Build settings + // + IEnumerable<ItemProperty> mocProperties + = ItemGlobals?[QtMoc.ItemTypeName]?.Properties ?? Enumerable.Empty<ItemProperty>(); + IEnumerable<ItemProperty> clProperties + = ItemGlobals?["ClCompile"]?.Properties ?? Enumerable.Empty<ItemProperty>(); + IEnumerable<ItemProperty> linkProperties + = ItemGlobals?["Link"]?.Properties ?? Enumerable.Empty<ItemProperty>(); + + xml = new StringBuilder(); + foreach (IWizardConfiguration c in Configurations) { + xml.AppendLine(string.Format(@" + <ItemDefinitionGroup Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"" Label=""Configuration"">", + /*{0}*/ c.Name, + /*{1}*/ c.Platform)); + + + /////////////////////////////////////////////////////////////////////////////////// + // Build settings: C++ compiler + // + if (!IsLinux(c)) { + // Windows + xml.AppendLine(@" + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation>"); + if (c.IsDebug) { + xml.AppendLine(@" + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <Optimization>Disabled</Optimization> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>"); + } else { + xml.AppendLine(@" + <DebugInformationFormat>None</DebugInformationFormat> + <Optimization>MaxSpeed</Optimization> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>"); + } + if (c.Target.EqualTo(ProjectTargets.WindowsStore)) { + xml.AppendLine(@" + <CompileAsWinRT>false</CompileAsWinRT> + <PrecompiledHeader>NotUsing</PrecompiledHeader> + <RuntimeTypeInfo>true</RuntimeTypeInfo>"); + } + if (UsePrecompiledHeaders) { + xml.AppendLine(string.Format(@" + <UsePrecompiledHeader>Use</UsePrecompiledHeader> + <PrecompiledHeaderFile>{0}</PrecompiledHeaderFile>", + /*{0}*/ PrecompiledHeader.Include)); + } + if (ExtraDefines?.Any() == true) { + xml.AppendLine(string.Format(@" + <PreprocessorDefinitions>{0};%(PreprocessorDefinitions)</PreprocessorDefinitions>", + /*{0}*/ string.Join(";", ExtraDefines))); + } + foreach (ItemProperty p in clProperties) { + xml.AppendLine(string.Format(@" + <{0}>{1}</{0}>", + /*{0}*/ p.Key, + /*{1}*/ p.Value)); + } + xml.AppendLine(@" + </ClCompile>"); + } else { + // Linux + xml.AppendLine(@" + <ClCompile> + <PositionIndependentCode>true</PositionIndependentCode> + </ClCompile>"); + } + + /////////////////////////////////////////////////////////////////////////////////// + // Build settings: Linker + // + if (!IsLinux(c)) { + // Windows + xml.AppendLine(string.Format(@" + <Link> + <SubSystem>{0}</SubSystem> + <GenerateDebugInformation>{1}</GenerateDebugInformation>", + /*{0}*/ TemplateType.HasFlag(Options.ConsoleSystem) ? "Console" : "Windows", + /*{1}*/ c.IsDebug ? "true" : "false")); + if (c.Target.EqualTo(ProjectTargets.WindowsStore)) { + xml.AppendLine(string.Format(@" + <AdditionalOptions>/APPCONTAINER %(AdditionalOptions)</AdditionalOptions> + <GenerateManifest>false</GenerateManifest> + <GenerateWindowsMetadata>false</GenerateWindowsMetadata> + <TargetMachine>{0}</TargetMachine>", + /*{0}*/ c.QtVersion.VC_Link_TargetMachine)); + } + foreach (ItemProperty p in linkProperties) { + xml.AppendLine(string.Format(@" + <{0}>{1}</{0}>", + /*{0}*/ p.Key, + /*{1}*/ p.Value)); + } + xml.AppendLine(@" + </Link>"); + } + + /////////////////////////////////////////////////////////////////////////////////// + // Build settings: moc + // + if (UsePrecompiledHeaders || mocProperties.Any()) { + xml.AppendLine(string.Format(@" + <{0}>", QtMoc.ItemTypeName)); + foreach (ItemProperty p in mocProperties) { + xml.AppendLine(string.Format(@" + <{0}>{1}</{0}>", + /*{0}*/ p.Key, + /*{1}*/ p.Value)); + } + if (UsePrecompiledHeaders) { + xml.AppendLine(string.Format(@" + <{0}>{1};%({0})</{0}>", + /*{0}*/ QtMoc.Property.PrependInclude, + /*{1}*/ PrecompiledHeader.Include)); + } + xml.AppendLine(string.Format(@" + </{0}>", QtMoc.ItemTypeName)); + } + + /////////////////////////////////////////////////////////////////////////////////// + // Build settings: remaining item types + // + if (ItemGlobals != null) { + foreach (string itemType in ItemGlobals.Keys + .Except(new[] { "ClCompile", "Link", QtMoc.ItemTypeName })) { + xml.AppendLine(string.Format(@" + <{0}>", + /*{0}*/ itemType)); + foreach (ItemProperty p in ItemGlobals[itemType].Properties) { + xml.AppendLine(string.Format(@" + <{0}>{1}</{0}>", + /*{0}*/ p.Key, + /*{1}*/ p.Value)); + } + xml.AppendLine(string.Format(@" + </{0}>", + /*{0}*/ itemType)); + } + } + xml.AppendLine(@" + </ItemDefinitionGroup>"); + } + + Parameter[NewProject.BuildSettings] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // Project items + // + IEnumerable<ItemDef> projectItems = ExtraItems + .Where((ItemDef item) => item.WhereConfig == null + || Configurations.Where(item.WhereConfig).Any()) + .Union(UsePrecompiledHeaders + ? new[] { PrecompiledHeader, PrecompiledHeaderSource } + : Enumerable.Empty<ItemDef>()); + + xml = new StringBuilder(); + foreach (ItemDef item in projectItems) { + bool itemHasProperties = (item.WhereConfig != null || item.Properties != null); + xml.Append(string.Format(@" + <{0} Include=""{1}""{2}", + /*{0}*/ item.ItemType, + /*{1}*/ item.Include, + /*{2}*/ itemHasProperties ? ">" : " />")); + + if (item.Properties != null) { + foreach (ItemProperty property in item.Properties) { + IEnumerable<IWizardConfiguration> configs = Configurations + .Where(property.WhereConfig ?? WhereConfig_SelectAll); + foreach (IWizardConfiguration config in configs) { + xml.AppendLine(string.Format(@" + <{0} Condition=""'$(Configuration)|$(Platform)' == '{1}|{2}'"">{3}</{0}>", + /*{0}*/ property.Key, + /*{1}*/ config.Name, + /*{2}*/ config.Platform, + /*{3}*/ property.Value)); + } + } + } + + if (item.WhereConfig != null) { + IEnumerable<IWizardConfiguration> excludedConfigs = Configurations + .Where(config => !item.WhereConfig(config)); + foreach (var excludedConfig in excludedConfigs) { + xml.AppendLine(string.Format(@" + <ExcludedFromBuild Condition=""'$(Configuration)|$(Platform)' == '{0}|{1}'"">true</ExcludedFromBuild>", + /*{0}*/ excludedConfig.Name, + /*{1}*/ excludedConfig.Platform)); + } + } + + if (itemHasProperties) { + xml.AppendLine(string.Format(@" + </{0}>", + /*{0}*/ item.ItemType)); + } + } + Parameter[NewProject.ProjectItems] = FormatParam(xml); + + /////////////////////////////////////////////////////////////////////////////////////// + // Project items: filters + // + xml = new StringBuilder(); + foreach (ItemDef item in projectItems) { + xml.Append(string.Format(@" + <{0} Include=""{1}"">", + /*{0}*/ item.ItemType, + /*{1}*/ item.Include)); + xml.AppendLine(string.Format(@" + <Filter>{0}</Filter>", + /*{0}*/ item.Filter)); + xml.AppendLine(string.Format(@" + </{0}>", + /*{0}*/ item.ItemType)); + } + Parameter[NewProject.FilterItems] = FormatParam(xml); + } + + // Matches empty lines; captures first newline + static readonly Regex patternEmptyLines + = new Regex(@"(?:^|(?<FIRST_NL>\r\n))(?:\r\n)+(?![\r\n]|$)|(?:\r\n)+$"); + + protected static string FormatParam(StringBuilder paramValue) + { + return FormatParam(paramValue.ToString()); + } + + protected static string FormatParam(string paramValue) + { + // Remove empty lines; replace with first newline (if any) + paramValue = patternEmptyLines.Replace(paramValue, + (Match m) => m.Groups["FIRST_NL"].Value); + + return paramValue; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Quick/QuickWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Quick/QuickWizard.cs new file mode 100644 index 0000000..24c8fd1 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Quick/QuickWizard.cs @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace QtVsTools.Wizards.ProjectWizard +{ + public class QuickWizard : ProjectTemplateWizard + { + protected override Options TemplateType => Options.Application | Options.GUISystem; + + WizardData _WizardData; + protected override WizardData WizardData => _WizardData + ?? (_WizardData = new WizardData + { + DefaultModules = new List<string> { "QtQuick" } + }); + + WizardWindow _WizardWindow; + protected override WizardWindow WizardWindow => _WizardWindow + ?? (_WizardWindow = new WizardWindow(title: "Qt Quick Application Wizard") + { + new WizardIntroPage { + Data = WizardData, + Header = @"Welcome to the Qt Quick Application Wizard", + Message = @"This wizard generates a Qt Quick application project." + + System.Environment.NewLine + + "Click Finish to create the project.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new ConfigPage { + Data = WizardData, + Header = @"Welcome to the Qt Quick Application Wizard", + Message = + @"Setup the configurations you want to include in your project. " + + @"The recommended settings for this project are selected by default.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true, + } + }); + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml b/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml new file mode 100644 index 0000000..f76e885 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml @@ -0,0 +1,259 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.ProjectWizard.ServerPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:util="clr-namespace:QtVsTools.Wizards.Util" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="445" + d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" + VerticalAlignment="Top" + Margin="0,25,0,0" + RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" + ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" + Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" + Grid.Row="0"> + <Run FontWeight="Bold" + Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + <LineBreak /> + </TextBlock> + <Grid Grid.Row="1"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Grid.Resources> + <Style TargetType="{x:Type TextBox}"> + <Style.Triggers> + <Trigger Property="Validation.HasError" + Value="true"> + <Setter Property="ToolTip" + Value="{Binding RelativeSource={RelativeSource Self}, + Path=(Validation.Errors)[0].ErrorContent}" /> + </Trigger> + </Style.Triggers> + </Style> + </Grid.Resources> + <TextBlock Grid.Row="0" + Text="Class Name:" + Margin="0,0,10,0" /> + <TextBox Grid.Row="1" + Margin="0,0,10,30" + Name="ClassName" + TextChanged="OnClassNameChanged" + TabIndex="0"> + <TextBox.Text> + <Binding Path="Data.ClassName" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:ClassNameValidationRule /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <Grid Grid.Row="2" + Grid.ColumnSpan="2"> + <Grid.RowDefinitions> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <TextBlock Grid.Row="0" + Grid.Column="0" + Margin="0,0,10,5" + Text="Header (.h) file:" /> + <TextBox Grid.Row="1" + Grid.Column="0" + Margin="0,0,10,10" + Name="ClassHeaderFile" + TabIndex="1"> + <TextBox.Text> + <Binding Path="Data.ClassHeaderFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".h" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Row="2" + Grid.Column="0" + Margin="0,0,10,5" + Text="User Interface (.ui) file:" /> + <TextBox Grid.Row="3" + Grid.Column="0" + Margin="0,0,10,0" + Name="UiFile" + TabIndex="3"> + <TextBox.Text> + <Binding Path="Data.UiFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".ui" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + <TextBlock Grid.Row="0" + Grid.Column="1" + Margin="0,0,0,5" + Text="Source (.cpp) file:" /> + <TextBox Grid.Row="1" + Grid.Column="1" + Margin="0,0,0,10" + Name="ClassSourceFile" + TabIndex="2"> + <TextBox.Text> + <Binding Path="Data.ClassSourceFile" + NotifyOnValidationError="True" + UpdateSourceTrigger="PropertyChanged"> + <Binding.ValidationRules> + <util:FileNameValidationRule FileExt=".cpp" /> + </Binding.ValidationRules> + </Binding> + </TextBox.Text> + </TextBox> + </Grid> + <CheckBox Grid.Row="3" + Content="Lower case file names" + Name="LowerCaseFileNames" + Click="OnLowerCaseFileNamesClick" + Margin="0,20,10,5" + TabIndex="4" /> + <CheckBox Grid.Row="4" + Content="Precompiled header" + IsChecked="{Binding Path=Data.UsePrecompiledHeader}" + Margin="0,10,10,5" + TabIndex="5" /> + </Grid> + <StackPanel HorizontalAlignment="Right" + Orientation="Horizontal" + Grid.Row="2" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" + Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" + MinWidth="75">< _Previous</Button> + <Button MinWidth="75" + Name="NextButton" + Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" + Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" + Click="OnFinishButtonClick" + Margin="10,0,0,0" + IsDefault="True" + Name="FinishButton" + Content="_Finish" + VerticalAlignment="Bottom"> + <Button.Style> + <Style TargetType="{x:Type Button}"> + <Setter Property="IsEnabled" + Value="false" /> + <Style.Triggers> + <MultiDataTrigger> + <MultiDataTrigger.Conditions> + <Condition Binding="{Binding Path=FinishButtonEnabled}" + Value="true" /> + <Condition Binding="{Binding ElementName=ClassName, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassHeaderFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=ClassSourceFile, + Path=(Validation.HasError)}" + Value="false" /> + <Condition Binding="{Binding ElementName=UiFile, + Path=(Validation.HasError)}" + Value="false" /> + </MultiDataTrigger.Conditions> + <Setter Property="IsEnabled" + Value="true" /> + </MultiDataTrigger> + </Style.Triggers> + </Style> + </Button.Style> + </Button> + <Button Click="OnCancelButtonClick" + MinWidth="75" + Margin="10,0,0,0" + Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" + IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml.cs new file mode 100644 index 0000000..360b1b0 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerPage.xaml.cs @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Windows; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.ProjectWizard +{ + public partial class ServerPage : WizardPage + { + public ServerPage() + { + InitializeComponent(); + DataContext = this; + } + + private void OnClassNameChanged(object sender, TextChangedEventArgs e) + { + UpdateFileNames(); + } + + private void OnLowerCaseFileNamesClick(object sender, RoutedEventArgs e) + { + UpdateFileNames(); + } + + private void UpdateFileNames() + { + Data.LowerCaseFileNames = LowerCaseFileNames.IsChecked.GetValueOrDefault(); + var filename = Data.LowerCaseFileNames ? ClassName.Text.ToLower() : ClassName.Text; + + ClassHeaderFile.Text = filename + @".h"; + ClassSourceFile.Text = filename + @".cpp"; + UiFile.Text = filename + @".ui"; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerWizard.cs b/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerWizard.cs new file mode 100644 index 0000000..963f9f8 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/ProjectWizard/Server/ServerWizard.cs @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Controls; +using EnvDTE; +using QtVsTools.Core; +using QtVsTools.Common; + +namespace QtVsTools.Wizards.ProjectWizard +{ + using static EnumExt; + + public class ServerWizard : ProjectTemplateWizard + { + protected override Options TemplateType => Options.DynamicLibrary | Options.GUISystem; + + enum NewClass + { + [String("classname")] ClassName, + [String("sourcefilename")] SourceFileName, + [String("headerfilename")] HeaderFileName, + [String("include")] Include, + } + + enum NewActiveQtProject + { + [String("pro_name")] Name, + [String("uifilename")] UiFileName, + [String("ui_hdr")] UiHeaderName, + } + + WizardData _WizardData; + protected override WizardData WizardData => _WizardData + ?? (_WizardData = new WizardData + { + DefaultModules = new List<string> { "QtCore", "QtGui", "QtWidgets", "QtAxServer" } + }); + + WizardWindow _WizardWindow; + protected override WizardWindow WizardWindow => _WizardWindow + ?? (_WizardWindow = new WizardWindow(title: "Qt ActiveQt Server Wizard") + { + new WizardIntroPage { + Data = WizardData, + Header = @"Welcome to the Qt ActiveQt Server Wizard", + Message = @"This wizard generates a Qt ActiveQt server project. It " + + @"creates a simple ActiveQt widget with the required files." + + System.Environment.NewLine + System.Environment.NewLine + + "To continue, click Next.", + PreviousButtonEnabled = false, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true + }, + new ConfigPage { + Data = WizardData, + Header = @"Welcome to the Qt ActiveQt Server Wizard", + Message = + @"Setup the configurations you want to include in your project. " + + @"The recommended settings for this project are selected by default.", + PreviousButtonEnabled = true, + NextButtonEnabled = true, + FinishButtonEnabled = false, + CancelButtonEnabled = true, + ValidateConfigs = ValidateConfigsForActiveQtServer + }, + new ServerPage { + Data = WizardData, + Header = @"Welcome to the Qt ActiveQt Server Wizard", + Message = @"This wizard generates a Qt ActiveQt server project. It " + + @"creates a simple ActiveQt widget with the required files.", + PreviousButtonEnabled = true, + NextButtonEnabled = false, + FinishButtonEnabled = true, + CancelButtonEnabled = true + } + }); + + string ValidateConfigsForActiveQtServer(IEnumerable<IWizardConfiguration> configs) + { + foreach (var config in configs) { + if (config.Target.EqualTo(ProjectTargets.WindowsStore)) { + return string.Format( + "ActiveQt Server project not available for the '{0}' target.", + config.Target); + } + } + return string.Empty; + } + + protected override void BeforeWizardRun() + { + // midl.exe does not support spaces in project name. Fails while generating the + // IDL file (library attribute), e.g. 'library Active QtServer1Lib' is illegal. + if (Parameter[NewProject.SafeName].Contains(" ")) + throw new QtVSException("Project name shall not contain spaces."); + + var className = Parameter[NewProject.SafeName]; + className = Regex.Replace(className, @"[^a-zA-Z0-9_]", string.Empty); + className = Regex.Replace(className, @"^[\d-]*\s*", string.Empty); + var result = new Util.ClassNameValidationRule().Validate(className, null); + if (result != ValidationResult.ValidResult) + className = @"ActiveQtServer"; + + WizardData.ClassName = className; + WizardData.ClassHeaderFile = className + @".h"; + WizardData.ClassSourceFile = className + @".cpp"; + WizardData.UiFile = WizardData.ClassName + @".ui"; + } + + protected override void BeforeTemplateExpansion() + { + Parameter[NewClass.ClassName] = WizardData.ClassName; + Parameter[NewClass.HeaderFileName] = WizardData.ClassHeaderFile; + Parameter[NewClass.SourceFileName] = WizardData.ClassSourceFile; + Parameter[NewActiveQtProject.UiFileName] = WizardData.UiFile; + + var include = new StringBuilder(); + include.AppendLine(string.Format("#include \"{0}\"", WizardData.ClassHeaderFile)); + if (UsePrecompiledHeaders) + include.AppendLine(string.Format("#include \"{0}\"", PrecompiledHeader.Include)); + Parameter[NewClass.Include] = FormatParam(include); + + Parameter[NewActiveQtProject.UiHeaderName] = string.Format("ui_{0}.h", + Path.GetFileNameWithoutExtension(WizardData.UiFile)); + + Parameter[NewActiveQtProject.Name] = WizardData.LowerCaseFileNames + ? Parameter[NewProject.SafeName].ToLower() + : Parameter[NewProject.SafeName]; + } + + protected override void OnProjectGenerated(Project project) + { + var qtProject = QtProject.Create(project); + qtProject.AddActiveQtBuildStep("1.0", Parameter[NewProject.SafeName] + ".def"); + } + } +} diff --git a/QtVsTools.Wizards/Wizards/Util/ClassNameValidationRule.cs b/QtVsTools.Wizards/Wizards/Util/ClassNameValidationRule.cs new file mode 100644 index 0000000..a9304a8 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/Util/ClassNameValidationRule.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Globalization; +using System.Text.RegularExpressions; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.Util +{ + internal class ClassNameValidationRule : VCLanguageManagerValidationRule + { + public ClassNameValidationRule() + { + SupportNamespaces = false; + AllowEmptyIdentifier = false; + } + + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + if (value is string) { + var identifier = value as string; + if (AllowEmptyIdentifier && string.IsNullOrEmpty(identifier)) + return ValidationResult.ValidResult; + + if (SupportNamespaces) { + var index = identifier.LastIndexOf(@"::", System.StringComparison.Ordinal); + if (index >= 0) + identifier = identifier.Substring(index + 2); + } + + if (Regex.IsMatch(identifier, pattern) && !Vclm.IsReservedName(identifier)) + return ValidationResult.ValidResult; + } + return new ValidationResult(false, @"Invalid identifier."); + } + + public bool SupportNamespaces { get; set; } + public bool AllowEmptyIdentifier { get; set; } + + const string pattern = @"^[a-zA-Z_][a-zA-Z0-9_]*$"; + } +} diff --git a/QtVsTools.Wizards/Wizards/Util/FileExistsInFilterValidationRule.cs b/QtVsTools.Wizards/Wizards/Util/FileExistsInFilterValidationRule.cs new file mode 100644 index 0000000..deecf48 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/Util/FileExistsInFilterValidationRule.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using QtVsTools.Core; +using QtVsTools.VisualStudio; +using System.Globalization; +using System.Linq; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.Util +{ + internal class FileExistsinFilterValidationRule : VCLanguageManagerValidationRule + { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + if (value is string) { + var dte = VsServiceProvider.GetService<SDTE, DTE>(); + if (dte == null) + return ValidationResult.ValidResult; + + var project = HelperFunctions.GetSelectedProject(dte); + if (project == null) + return ValidationResult.ValidResult; + + var files = HelperFunctions.GetProjectFiles(project, Filter); + if (files.Count == 0) + return ValidationResult.ValidResult; + + var fileName = (value as string).ToUpperInvariant(); + if (files.FirstOrDefault(x => x.ToUpperInvariant() == fileName) != null) + return new ValidationResult(false, @"File already exists."); + return ValidationResult.ValidResult; + } + return new ValidationResult(false, @"Invalid file name."); + } + + public FilesToList Filter { get; set; } + } +} diff --git a/QtVsTools.Wizards/Wizards/Util/FileNameValidationRule.cs b/QtVsTools.Wizards/Wizards/Util/FileNameValidationRule.cs new file mode 100644 index 0000000..e3ea756 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/Util/FileNameValidationRule.cs @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Globalization; +using System.IO; +using System.Linq; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.Util +{ + internal class FileNameValidationRule : VCLanguageManagerValidationRule + { + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + if (value is string) { + var filename = value as string; + if (FileExt == @".ui" || FileExt == @".qrc") { + filename = filename.ToLower().Replace(@".h", @".x"); + filename = filename.Replace(FileExt, @".h"); + } + + if (Vclm.ValidateFileName(filename) + && !Path.GetInvalidFileNameChars().Any(filename.Contains)) { + return ValidationResult.ValidResult; + } + } + return new ValidationResult(false, @"Invalid file name."); + } + } +} diff --git a/QtVsTools.Wizards/Wizards/Util/NativeMethods.cs b/QtVsTools.Wizards/Wizards/Util/NativeMethods.cs new file mode 100644 index 0000000..00edfea --- /dev/null +++ b/QtVsTools.Wizards/Wizards/Util/NativeMethods.cs @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace QtVsTools.Wizards.Util +{ + internal static class NativeMethods + { + [ResourceExposure(ResourceScope.None)] + [DllImport("user32.dll", CharSet = CharSet.Auto)] + internal static extern int GetWindowLong(IntPtr hwnd, int index); + } +} diff --git a/QtVsTools.Wizards/Wizards/Util/SortComboBoxItem.cs b/QtVsTools.Wizards/Wizards/Util/SortComboBoxItem.cs new file mode 100644 index 0000000..823b83f --- /dev/null +++ b/QtVsTools.Wizards/Wizards/Util/SortComboBoxItem.cs @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.ComponentModel; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.Util +{ + internal class SortComboBoxItem : ComboBoxItem + { + public ListSortDirection? SortDirection + { + get; set; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/Util/UnsafeNativeMethods.cs b/QtVsTools.Wizards/Wizards/Util/UnsafeNativeMethods.cs new file mode 100644 index 0000000..8fc5f27 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/Util/UnsafeNativeMethods.cs @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace QtVsTools.Wizards.Util +{ + internal static class UnsafeNativeMethods + { + [ResourceExposure(ResourceScope.None)] + [DllImport("user32.dll", CharSet = CharSet.Auto)] + internal static extern int SetWindowLong(IntPtr hwnd, int index, int value); + } +} diff --git a/QtVsTools.Wizards/Wizards/Util/VCLanguageManagerValidationRule.cs b/QtVsTools.Wizards/Wizards/Util/VCLanguageManagerValidationRule.cs new file mode 100644 index 0000000..edbdc72 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/Util/VCLanguageManagerValidationRule.cs @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.VCCodeModel; +using QtVsTools.VisualStudio; +using System.Windows.Controls; + +namespace QtVsTools.Wizards.Util +{ + internal abstract class VCLanguageManagerValidationRule : ValidationRule + { + protected VCLanguageManagerValidationRule() + { + ValidatesOnTargetUpdated = true; + + var dte = VsServiceProvider.GetService<DTE>(); + Vclm = dte.GetObject("VCLanguageManager") as VCLanguageManager; + } + + public string FileExt { get; set; } + public VCLanguageManager Vclm { get; private set; } + } +} diff --git a/QtVsTools.Wizards/Wizards/WizardData.cs b/QtVsTools.Wizards/Wizards/WizardData.cs new file mode 100644 index 0000000..1cfbbc1 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/WizardData.cs @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Collections.Generic; + +namespace QtVsTools.Wizards +{ + using ProjectWizard; + using ClassWizard; + + public class WizardData + { + public WizardData() + { + Modules = new List<string>(); + DefaultModules = new List<string>(); + } + + public string ClassName { get; set; } + public string BaseClass { get; set; } + public string PluginClass { get; set; } + public string ConstructorSignature { get; set; } + + public string ClassHeaderFile { get; set; } + public string ClassSourceFile { get; set; } + public string PluginHeaderFile { get; set; } + public string PluginSourceFile { get; set; } + + public string UiFile { get; set; } + public string QrcFile { get; set; } + + public List<string> Modules { get; set; } + public List<string> DefaultModules { get; set; } + + public bool AddDefaultAppIcon { get; set; } + public bool CreateStaticLibrary { get; set; } + public bool UsePrecompiledHeader { get; set; } + public bool InsertQObjectMacro { get; set; } + public bool LowerCaseFileNames { get; set; } + public UiClassInclusion UiClassInclusion { get; set; } + + public IEnumerable<IWizardConfiguration> Configs { get; set; } + } +} diff --git a/QtVsTools.Wizards/Wizards/WizardIntroPage.xaml b/QtVsTools.Wizards/Wizards/WizardIntroPage.xaml new file mode 100644 index 0000000..04bb1eb --- /dev/null +++ b/QtVsTools.Wizards/Wizards/WizardIntroPage.xaml @@ -0,0 +1,102 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<wizards:WizardPage x:Class="QtVsTools.Wizards.WizardIntroPage" + xmlns:wizards="clr-namespace:QtVsTools.Wizards" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + KeepAlive="True" + mc:Ignorable="d" + d:DesignHeight="445" + d:DesignWidth="585"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + <Image Grid.Column="0" + HorizontalAlignment="Center" + Source="/QtVsTools.Wizards;component/Resources/Qt-logo-small.png" + VerticalAlignment="Top" + Margin="0,25,0,0" + RenderTransformOrigin="1,0"> + <Image.RenderTransform> + <TransformGroup> + <ScaleTransform ScaleY="0.86" + ScaleX="0.86" /> + </TransformGroup> + </Image.RenderTransform> + </Image> + <Grid Grid.Column="1" + Margin="25,25,10,0"> + <Grid.RowDefinitions> + <RowDefinition Height="*" /> + <RowDefinition Height="Auto" /> + </Grid.RowDefinitions> + <TextBlock TextWrapping="Wrap" + Grid.Row="0"> + <Run FontWeight="Bold" + Text="{Binding Path=Header}" /> + <LineBreak /> + <LineBreak /> + <Run Text="{Binding Path=Message}" /> + </TextBlock> + <StackPanel HorizontalAlignment="Right" + Orientation="Horizontal" + Grid.Row="1" + Margin="0,0,0,10"> + <Button Click="OnPreviousButtonClick" + Name="PreviousButton" + IsEnabled="{Binding Path=PreviousButtonEnabled}" + MinWidth="75">< _Previous</Button> + <Button MinWidth="75" + Name="NextButton" + Click="OnNextButtonClick" + IsEnabled="{Binding Path=NextButtonEnabled}" + Margin="10,0,0,0">_Next ></Button> + <Button MinWidth="75" + Click="OnFinishButtonClick" + Margin="10,0,0,0" + IsDefault="True" + IsEnabled="{Binding Path=FinishButtonEnabled}" + Name="FinishButton" + VerticalAlignment="Bottom">_Finish</Button> + <Button Click="OnCancelButtonClick" + MinWidth="75" + Margin="10,0,0,0" + Name="CancelButton" + IsEnabled="{Binding Path=CancelButtonEnabled}" + IsCancel="True">_Cancel</Button> + </StackPanel> + </Grid> + </Grid> +</wizards:WizardPage> diff --git a/QtVsTools.Wizards/Wizards/WizardIntroPage.xaml.cs b/QtVsTools.Wizards/Wizards/WizardIntroPage.xaml.cs new file mode 100644 index 0000000..f0fb113 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/WizardIntroPage.xaml.cs @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Wizards +{ + public partial class WizardIntroPage : WizardPage + { + public WizardIntroPage() + { + InitializeComponent(); + DataContext = this; + } + } +} diff --git a/QtVsTools.Wizards/Wizards/WizardPage.cs b/QtVsTools.Wizards/Wizards/WizardPage.cs new file mode 100644 index 0000000..d88060e --- /dev/null +++ b/QtVsTools.Wizards/Wizards/WizardPage.cs @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Windows; +using System.Windows.Navigation; + +namespace QtVsTools.Wizards +{ + public class WizardPage : PageFunction<WizardResult> + { + public string Header { get; set; } + public string Message { get; set; } + + public WizardData Data { get; set; } + public WizardWindow Wizard { get; set; } + + public bool PreviousButtonEnabled { get; set; } + public bool NextButtonEnabled { get; set; } + public bool FinishButtonEnabled { get; set; } + public bool CancelButtonEnabled { get; set; } + + public event EventHandler NavigateForward; + public event EventHandler NavigatedBackward; + + public event ReturnEventHandler<WizardResult> ReturnEx; + public void OnReturnEx(ReturnEventArgs<WizardResult> e) + { + if (ReturnEx != null) + ReturnEx.Invoke(this, e); + } + + protected virtual void OnPreviousButtonClick(object sender, RoutedEventArgs e) + { + if (NavigationService == null || !NavigationService.CanGoBack) + return; + + NavigationService.GoBack(); + OnNavigatedBackward(new EventArgs()); + } + + protected virtual void OnNextButtonClick(object sender, RoutedEventArgs e) + { + if (NavigationService == null || Wizard == null) + return; + + try { + OnNavigateForward(new EventArgs()); + } catch (InvalidOperationException) { + return; // we can't navigate any further + } + + if (!NavigationService.CanGoForward) { + Wizard.NextPage.ReturnEx += OnPageReturn; + NavigationService.Navigate(Wizard.NextPage); + } else { + NavigationService.GoForward(); + } + } + + protected virtual void OnFinishButtonClick(object sender, RoutedEventArgs e) + { + OnReturnEx(new ReturnEventArgs<WizardResult>(WizardResult.Finished)); + } + + protected virtual void OnCancelButtonClick(object sender, RoutedEventArgs e) + { + OnReturnEx(new ReturnEventArgs<WizardResult>(WizardResult.Canceled)); + OnReturn(null); + } + + protected virtual void OnNavigateForward(EventArgs e) + { + if (NavigateForward != null) + NavigateForward.Invoke(this, e); + } + + protected virtual void OnNavigatedBackward(EventArgs e) + { + if (NavigatedBackward != null) + NavigatedBackward.Invoke(this, e); + } + + private void OnPageReturn(object sender, ReturnEventArgs<WizardResult> e) + { + OnReturnEx(e); + OnReturn(null); + } + } +} diff --git a/QtVsTools.Wizards/Wizards/WizardResult.cs b/QtVsTools.Wizards/Wizards/WizardResult.cs new file mode 100644 index 0000000..dacbb34 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/WizardResult.cs @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +namespace QtVsTools.Wizards +{ + public enum WizardResult + { + Canceled, + Finished, + Exception + } +} diff --git a/QtVsTools.Wizards/Wizards/WizardWindow.xaml b/QtVsTools.Wizards/Wizards/WizardWindow.xaml new file mode 100644 index 0000000..a3cda2f --- /dev/null +++ b/QtVsTools.Wizards/Wizards/WizardWindow.xaml @@ -0,0 +1,44 @@ +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<NavigationWindow x:Class="QtVsTools.Wizards.WizardWindow" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + WindowStartupLocation="CenterOwner" + ShowInTaskbar="False" + ShowsNavigationUI="False" + Title="Wizard Window" + ResizeMode="CanResizeWithGrip" + MinHeight="480" + MinWidth="640" + Height="480" + Width="640" + MaxHeight="768" + MaxWidth="1024" /> diff --git a/QtVsTools.Wizards/Wizards/WizardWindow.xaml.cs b/QtVsTools.Wizards/Wizards/WizardWindow.xaml.cs new file mode 100644 index 0000000..8327db8 --- /dev/null +++ b/QtVsTools.Wizards/Wizards/WizardWindow.xaml.cs @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Windows.Navigation; + +namespace QtVsTools.Wizards +{ + using System.Collections; + using Util; + + public partial class WizardWindow : NavigationWindow, IEnumerable<WizardPage> + { + + public WizardWindow(IEnumerable<WizardPage> pages = null, string title = null) + { + InitializeComponent(); + SourceInitialized += onSourceInitialized; + + if (title != null) + Title = title; + + Pages = new List<WizardPage>(); + + if (pages != null) { + foreach (var page in pages) + Add(page); + } + } + + public void Add(WizardPage page) + { + bool isFirstPage = (Pages.Count == 0); + page.Wizard = this; + page.NavigateForward += OnNavigateForward; + page.NavigatedBackward += OnNavigatedBackwards; + Pages.Add(page); + + if (isFirstPage) { + NextPage.ReturnEx += OnPageReturn; + Navigate(NextPage); // put on navigation stack + } + } + + public WizardPage NextPage + { + get + { + return Pages[currentPage]; + } + } + + public List<WizardPage> Pages + { + get; + private set; + } + + public IEnumerator<WizardPage> GetEnumerator() + { + return ((IEnumerable<WizardPage>)Pages).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable<WizardPage>)Pages).GetEnumerator(); + } + + private int currentPage; + + private void onSourceInitialized(object sender, EventArgs e) + { + try { + var STYLE = -16; // see winuser.h + var hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle; + UnsafeNativeMethods.SetWindowLong(hwnd, STYLE, + NativeMethods.GetWindowLong(hwnd, STYLE) & ~(0x10000 | 0x20000)); + } catch { + // Ignore if we can't remove the buttons. + SourceInitialized -= onSourceInitialized; + } + } + + private void OnNavigateForward(object sender, EventArgs e) + { + var tmp = currentPage + 1; + if (tmp >= Pages.Count) { + throw new InvalidOperationException(@"Current wizard page " + + @"cannot be equal or greather then pages count."); + } + currentPage++; + } + + private void OnPageReturn(object sender, ReturnEventArgs<WizardResult> e) + { + if (DialogResult == null) + DialogResult = (e.Result == WizardResult.Finished); + } + + private void OnNavigatedBackwards(object sender, EventArgs e) + { + var tmp = currentPage - 1; + if (tmp < 0) + throw new InvalidOperationException(@"Current wizard page cannot be less then 0."); + currentPage--; + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa5df16 --- /dev/null +++ b/README.md @@ -0,0 +1,156 @@ +# Qt Visual Studio Tools + +The Qt Visual Studio Tools integrate the Qt development tools into Microsoft Visual Studio. This +enables developers to use the standard Windows development environment without having to worry +about Qt-related build steps or tools. + +## Sources + +Use Git to check out the +[Qt Visual Studio Tools sources](https://code.qt.io/cgit/qt-labs/vstools.git), using one of the +following options: + + git clone git://code.qt.io/qt-labs/vstools.git + + git clone https://code.qt.io/qt-labs/vstools.git + +Contributions to the Qt Visual Studio Tools project must be submitted to the +[`qt-labs/vstools`](https://codereview.qt-project.org/admin/repos/qt-labs/vstools) Gerrit +repository. For instructions on how to set up a Gerrit account and contribute to Qt projects, refer +to the wiki page ["Setting up Gerrit"](https://wiki.qt.io/Setting_up_Gerrit). + +## Qt installation + +To build the Qt Visual Studio Tools, an installation of Qt is required. The version of Qt that is +currently supported is 5.12.9. Either build Qt from the sources available in the +[Qt Project Git Repository Browser](https://code.qt.io/cgit/qt/qt5.git/tag/?h=v5.12.9) +or install a [pre-built binary package](https://download.qt.io/official_releases/qt/5.12/5.12.9/). + +### Building Qt from sources + +See the [Qt documentation](https://wiki.qt.io/Building_Qt_5_from_Git#Windows) for the prerequisites +and steps to build Qt from sources. + +Recommended options for the `configure` tool: + + configure -static -opensource -confirm-license -nomake examples -nomake tests -opengl desktop + +Recommended options for [jom](https://wiki.qt.io/Jom): + + jom module-qtbase module-qtdeclarative + +### 32-bit or 64-bit + +Visual Studio 2022 is a 64-bit application, whereas VS 2019 and 2017 are 32-bit applications. The +target platform for which Qt is built must reflect this: + +- For Visual Studio 2022, use Qt built for the x64 platform. + +- For Visual Studio 2019, use Qt built for the x86 platform. + +- For Visual Studio 2017, use Qt built for the x86 platform. + +## Build + +After cloning the repository, follow the instructions below to build the Qt Visual Studio Tools. + +### Requirements + +The following is required in order to build the Qt Visual Studio solution: + +- Visual Studio 2017, 2019 or 2022, with the following workloads: + - Desktop development with C++ + - .NET desktop development + - [Visual Studio extension development](https://docs.microsoft.com/en-us/visualstudio/extensibility/installing-the-visual-studio-sdk) + - [Linux development with C++](https://devblogs.microsoft.com/cppblog/linux-development-with-c-in-visual-studio/) + +- [`vswhere` tool](https://github.com/microsoft/vswhere) (usually installed with Visual Studio): + - [Version 2.7.1](https://github.com/microsoft/vswhere/releases/tag/2.7.1) or greater. + +- `git` must be installed and included in the `PATH` environment variable. + +### Environment variables + +Set environment variables `QTBUILD_STATIC_VS`_`nnnn`_ according to the installed VS versions, i.e.: +- `QTBUILD_STATIC_VS2017` = _path to Qt installation built with msvc2017_ +- `QTBUILD_STATIC_VS2019` = _path to Qt installation built with msvc2019_ +- `QTBUILD_STATIC_VS2022` = _path to Qt installation built with msvc2022_ + +For example, assuming Qt is installed in the following directory tree: + + C: + +--- build + +--- qt_5.12.9_msvc2019_x86 + | +--- bin + | +--- include + | +--- lib + | (etc.) + | + +--- qt_5.12.9_msvc2022_x64 + +--- bin + +--- include + +--- lib + (etc.) + +In this case, the following environment variables must be set: + + QTBUILD_STATIC_VS2019=C:\build\qt_5.12.9_msvc2019_x86 + QTBUILD_STATIC_VS2022=C:\build\qt_5.12.9_msvc2022_x64 + +### Initialization + +In a command prompt (a "regular" one, *not* a VS Developer/Native Tools prompt), `CD` to the +root of the repository and run `vstools.bat` to initialize the solution and open it in Visual +Studio, with the following arguments: + + C:\...\vstools> vstools -init -startvs + +This will: +- Delete all output files; +- Restore NuGet packages; +- Run an initial text template generation; +- Open the solution in the VS IDE, ready to build/debug. + +This procedure must be repeated when opening the solution on another +version of VS. For example, assuming VS 2022 and VS 2019 are +installed, to open the solution in VS 2019 after it has already been +initialized and used in VS 2022, run the following: + + C:\...\vstools> vstools -vs2019 -init -startvs + +By default, if no VS version is specified, the most recent version is selected. + +### Target platform + +The solution platform must be set to `'x64'` for VS 2022, and `'x86'` +or `'Any CPU'` for VS 2019 and VS 2017. + +## Debug + +To debug the Qt Visual Studio tools extension, the +`QtVsTools.Package` project must be set as the +startup project. Also, the target binary for the debug session must +be set to the Visual Studio executable (`devenv.exe`), with the +option to start an +[experimental instance](https://docs.microsoft.com/en-us/visualstudio/extensibility/the-experimental-instance). + +Follow these instructions to configure the solution for debug: + +- In the solution explorer: right-click QtVsTools.Package > Set as +startup +- In the solution explorer: right-click QtVsTools.Package > Properties +- In the properties dialog: select the Debug page +- In the debug properties page, set the following options: + - _Start external program_ = path to the Visual Studio executable + (`devenv.exe`). + - _Command line arguments_ = `/rootSuffix Exp`. + +## Documentation + +To build the Qt Visual Studio Tools documentation, run +`qmake && jom docs` from the root directory of the `vstools` +repository. You need to have `qdoc` and friends built already. + +See the +[Qt documentation](https://wiki.qt.io/Building_Qt_Documentation) for +the prerequisites and steps to build Qt documentation from sources. diff --git a/Templates/console/Properties/AssemblyInfo.cs b/Templates/console/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..adaa0a6 --- /dev/null +++ b/Templates/console/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Console")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-17 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3BB38C12-5F17-4490-A405-E04FC05C5ECF")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/console/QtTemplate.Project.Console.csproj b/Templates/console/QtTemplate.Project.Console.csproj new file mode 100644 index 0000000..d4bbdfe --- /dev/null +++ b/Templates/console/QtTemplate.Project.Console.csproj @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>console.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Console</RootNamespace> + <AssemblyName>Console</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.VisualStudio.CoreUtility"> + <Private>False</Private> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="console.ico" /> + <None Include="console.vcxproj"> + <SubType>Designer</SubType> + </None> + <None Include="console.vcxproj.filters"> + <SubType>Designer</SubType> + </None> + <None Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <T4Template Include="console.vstemplate_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>console.vstemplate</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>console.vstemplate</LastGenOutput> + <SubType>Designer</SubType> + </T4Template> + <VSTemplate Include="console.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>console.vstemplate_TT</DependentUpon> + </VSTemplate> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> diff --git a/Templates/console/console.ico b/Templates/console/console.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/console/console.ico Binary files differ diff --git a/Templates/console/console.vcxproj b/Templates/console/console.vcxproj new file mode 100644 index 0000000..545c916 --- /dev/null +++ b/Templates/console/console.vcxproj @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$ToolsVersion$" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> +$ProjectConfigurations$ + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$ProjectGuid$</ProjectGuid> + <Keyword>$Keyword$</Keyword> +$Globals$ + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')" + >$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> +$Configurations$ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> +$QtSettings$ + <Target Name="QtMsBuildNotFound" + BeforeTargets="CustomBuild;ClCompile" + Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" + Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> +$PropertySheets$ + <PropertyGroup Label="UserMacros" /> +$Properties$ +$BuildSettings$ + <ItemGroup> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Templates/console/console.vcxproj.filters b/Templates/console/console.vcxproj.filters new file mode 100644 index 0000000..df883a8 --- /dev/null +++ b/Templates/console/console.vcxproj.filters @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> diff --git a/Templates/console/console.vstemplate_TT b/Templates/console/console.vstemplate_TT new file mode 100644 index 0000000..1a51a46 --- /dev/null +++ b/Templates/console/console.vstemplate_TT @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vstemplate" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Project" > + <TemplateData> + <Name>Qt Console Application</Name> + <Description>This wizard generates a Qt console application project.</Description> + <ProjectType>VC</ProjectType> + <SortOrder>1000</SortOrder> + <CreateNewFolder>true</CreateNewFolder> + <DefaultName>QtConsoleApplication</DefaultName> + <ProvideDefaultName>true</ProvideDefaultName> + <LocationField>Enabled</LocationField> + <EnableLocationBrowseButton>true</EnableLocationBrowseButton> + <Icon>console.ico</Icon> + <LanguageTag>Cpp</LanguageTag> + <PlatformTag>Windows</PlatformTag> + <PlatformTag>Linux</PlatformTag> + <ProjectTypeTag>Qt</ProjectTypeTag> + <ProjectTypeTag>Console</ProjectTypeTag> + <ProjectTypeTag>IoT</ProjectTypeTag> + </TemplateData> + <TemplateContent> + <Project TargetFileName="$safeprojectname$.vcxproj" + File="console.vcxproj" + ReplaceParameters="true"> + <ProjectItem ReplaceParameters="false" + OpenInEditor="true">main.cpp</ProjectItem> + <ProjectItem ReplaceParameters="false" + TargetFileName="$safeprojectname$.vcxproj.filters">console.vcxproj.filters</ProjectItem> + </Project> + </TemplateContent> + <WizardExtension> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Assembly>QtVsTools.Wizards, Version=<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>, Culture=neutral, PublicKeyToken=null</Assembly> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <FullClassName>QtVsTools.Wizards.ProjectWizard.ConsoleWizard</FullClassName> + </WizardExtension> +</VSTemplate> diff --git a/Templates/console/main.cpp b/Templates/console/main.cpp new file mode 100644 index 0000000..c028c10 --- /dev/null +++ b/Templates/console/main.cpp @@ -0,0 +1,8 @@ +#include <QtCore/QCoreApplication> + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + return a.exec(); +} diff --git a/Templates/designer/Properties/AssemblyInfo.cs b/Templates/designer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..355e748 --- /dev/null +++ b/Templates/designer/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Designer")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0FE21142-A2BD-4E8B-AD33-327157075341")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/designer/QtTemplate.Project.Designer.csproj b/Templates/designer/QtTemplate.Project.Designer.csproj new file mode 100644 index 0000000..0e19350 --- /dev/null +++ b/Templates/designer/QtTemplate.Project.Designer.csproj @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>designer.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{191B2B91-D5FD-4138-A72A-AB0B149988C6}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Designer</RootNamespace> + <AssemblyName>Designer</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="designer.ico" /> + <None Include="designer.vcxproj"> + <SubType>Designer</SubType> + </None> + <None Include="designer.vcxproj.filters"> + <SubType>Designer</SubType> + </None> + <None Include="plugin.json" /> + <T4Template Include="designer.vstemplate_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>designer.vstemplate</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>designer.vstemplate</LastGenOutput> + </T4Template> + <VSTemplate Include="designer.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>designer.vstemplate_TT</DependentUpon> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Content Include="plugin.cpp" /> + <Content Include="plugin.h" /> + <Content Include="stdafx.cpp" /> + <Content Include="stdafx.h" /> + <Content Include="widget.cpp" /> + <Content Include="widget.h" /> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/designer/designer.ico b/Templates/designer/designer.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/designer/designer.ico Binary files differ diff --git a/Templates/designer/designer.vcxproj b/Templates/designer/designer.vcxproj new file mode 100644 index 0000000..ff60b70 --- /dev/null +++ b/Templates/designer/designer.vcxproj @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$ToolsVersion$" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> +$ProjectConfigurations$ + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$ProjectGuid$</ProjectGuid> + <Keyword>$Keyword$</Keyword> +$Globals$ + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')" + >$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> +$Configurations$ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> +$QtSettings$ + <Target Name="QtMsBuildNotFound" + BeforeTargets="CustomBuild;ClCompile" + Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" + Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> +$PropertySheets$ + <PropertyGroup Label="UserMacros" /> +$Properties$ +$BuildSettings$ + <ItemGroup> + <QtMoc Include="$headerfilename$"/> + <ClCompile Include="$sourcefilename$"/> + <QtMoc Include="$pluginheaderfilename$"/> + <ClCompile Include="$pluginsourcefilename$"/> + <None Include="$plugin_json$" /> +$ProjectItems$ + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Templates/designer/designer.vcxproj.filters b/Templates/designer/designer.vcxproj.filters new file mode 100644 index 0000000..7b7bab4 --- /dev/null +++ b/Templates/designer/designer.vcxproj.filters @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <QtMoc Include="$headerfilename$"> + <Filter>Header Files</Filter> + </QtMoc> + <ClCompile Include="$sourcefilename$"> + <Filter>Source Files</Filter> + </ClCompile> + <QtMoc Include="$pluginheaderfilename$"> + <Filter>Header Files</Filter> + </QtMoc> + <ClCompile Include="$pluginsourcefilename$"> + <Filter>Source Files</Filter> + </ClCompile> + <None Include="$plugin_json$"> + <Filter>Resource Files</Filter> + </None> +$FilterItems$ + </ItemGroup> +</Project> diff --git a/Templates/designer/designer.vstemplate_TT b/Templates/designer/designer.vstemplate_TT new file mode 100644 index 0000000..e980345 --- /dev/null +++ b/Templates/designer/designer.vstemplate_TT @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vstemplate" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Project" > + <TemplateData> + <Name>Qt Designer Custom Widget</Name> + <Description>This wizard generates a Qt Designer Custom widget.</Description> + <ProjectType>VC</ProjectType> + <SortOrder>1000</SortOrder> + <CreateNewFolder>true</CreateNewFolder> + <DefaultName>QtDesignerWidget</DefaultName> + <ProvideDefaultName>true</ProvideDefaultName> + <LocationField>Enabled</LocationField> + <EnableLocationBrowseButton>true</EnableLocationBrowseButton> + <Icon>designer.ico</Icon> + <LanguageTag>Cpp</LanguageTag> + <PlatformTag>Windows</PlatformTag> + <PlatformTag>Linux</PlatformTag> + <ProjectTypeTag>Qt</ProjectTypeTag> + <ProjectTypeTag>Library</ProjectTypeTag> + </TemplateData> + <TemplateContent> + <Project TargetFileName="$safeprojectname$.vcxproj" + File="designer.vcxproj" + ReplaceParameters="true"> + <ProjectItem ReplaceParameters="true" + OpenInEditor="true" + TargetFileName="$pluginsourcefilename$">plugin.cpp</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$pluginheaderfilename$">plugin.h</ProjectItem> + <ProjectItem ReplaceParameters="false" + TargetFileName="$plugin_json$">plugin.json</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.cpp</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.h</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$sourcefilename$">widget.cpp</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$headerfilename$">widget.h</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$safeprojectname$.vcxproj.filters">designer.vcxproj.filters</ProjectItem> + </Project> + </TemplateContent> + <WizardExtension> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Assembly>QtVsTools.Wizards, Version=<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>, Culture=neutral, PublicKeyToken=null</Assembly> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <FullClassName>QtVsTools.Wizards.ProjectWizard.DesignerWizard</FullClassName> + </WizardExtension> +</VSTemplate> diff --git a/Templates/designer/plugin.cpp b/Templates/designer/plugin.cpp new file mode 100644 index 0000000..9b5771f --- /dev/null +++ b/Templates/designer/plugin.cpp @@ -0,0 +1,77 @@ +$include$ +#include "$pluginheaderfilename$" + +#include <QtCore/QtPlugin> + +$plugin_class$::$plugin_class$(QObject *parent) + : QObject(parent) +{ + initialized = false; +} + +void $plugin_class$::initialize(QDesignerFormEditorInterface * /*core*/) +{ + if (initialized) + return; + + initialized = true; +} + +bool $plugin_class$::isInitialized() const +{ + return initialized; +} + +QWidget *$plugin_class$::createWidget(QWidget *parent) +{ + return new $classname$(parent); +} + +QString $plugin_class$::name() const +{ + return "$classname$"; +} + +QString $plugin_class$::group() const +{ + return "My Plugins"; +} + +QIcon $plugin_class$::icon() const +{ + return QIcon(); +} + +QString $plugin_class$::toolTip() const +{ + return QString(); +} + +QString $plugin_class$::whatsThis() const +{ + return QString(); +} + +bool $plugin_class$::isContainer() const +{ + return false; +} + +QString $plugin_class$::domXml() const +{ + return "<widget class=\"$classname$\" name=\"$objname$\">\n" + " <property name=\"geometry\">\n" + " <rect>\n" + " <x>0</x>\n" + " <y>0</y>\n" + " <width>100</width>\n" + " <height>100</height>\n" + " </rect>\n" + " </property>\n" + "</widget>\n"; +} + +QString $plugin_class$::includeFile() const +{ + return "$headerfilename$"; +} diff --git a/Templates/designer/plugin.h b/Templates/designer/plugin.h new file mode 100644 index 0000000..1f88121 --- /dev/null +++ b/Templates/designer/plugin.h @@ -0,0 +1,28 @@ +#pragma once + +#include <QtUiPlugin/QDesignerCustomWidgetInterface> + +class $plugin_class$ : public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface" FILE "$plugin_json$") + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + $plugin_class$(QObject *parent = Q_NULLPTR); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget *createWidget(QWidget *parent); + void initialize(QDesignerFormEditorInterface *core); + +private: + bool initialized; +}; diff --git a/Templates/designer/plugin.json b/Templates/designer/plugin.json new file mode 100644 index 0000000..69a88e3 --- /dev/null +++ b/Templates/designer/plugin.json @@ -0,0 +1 @@ +{} diff --git a/Templates/designer/stdafx.cpp b/Templates/designer/stdafx.cpp new file mode 100644 index 0000000..a27b824 --- /dev/null +++ b/Templates/designer/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/Templates/designer/stdafx.h b/Templates/designer/stdafx.h new file mode 100644 index 0000000..51b684f --- /dev/null +++ b/Templates/designer/stdafx.h @@ -0,0 +1 @@ +#include <QtWidgets> diff --git a/Templates/designer/widget.cpp b/Templates/designer/widget.cpp new file mode 100644 index 0000000..04af3dd --- /dev/null +++ b/Templates/designer/widget.cpp @@ -0,0 +1,6 @@ +$include$ + +$classname$::$classname$(QWidget *parent) + : $baseclass$(parent) +{ +} diff --git a/Templates/designer/widget.h b/Templates/designer/widget.h new file mode 100644 index 0000000..abf56e1 --- /dev/null +++ b/Templates/designer/widget.h @@ -0,0 +1,11 @@ +#pragma once + +#include <QtWidgets/$baseclass$> + +class $classname$ : public $baseclass$ +{ + Q_OBJECT + +public: + $classname$(QWidget *parent = Q_NULLPTR); +}; diff --git a/Templates/dialogbuttonbottom/Properties/AssemblyInfo.cs b/Templates/dialogbuttonbottom/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ca58979 --- /dev/null +++ b/Templates/dialogbuttonbottom/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DialogButtonBottom")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9E87B4A4-52B9-4989-BC4C-225186725FF4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/dialogbuttonbottom/QtTemplate.Item.DialogButtonBottom.csproj b/Templates/dialogbuttonbottom/QtTemplate.Item.DialogButtonBottom.csproj new file mode 100644 index 0000000..5c6cf84 --- /dev/null +++ b/Templates/dialogbuttonbottom/QtTemplate.Item.DialogButtonBottom.csproj @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>dialogbuttonbottom.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{F7407750-5F72-460F-9C53-27CF509A39B1}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>DialogButtonBottom</RootNamespace> + <AssemblyName>DialogButtonBottom</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <Content Include="dialogbuttonbottom.ico" /> + <None Include="dialogbuttonbottom.ui" /> + <VSTemplate Include="dialogbuttonbottom.vstemplate"> + <OutputSubPath>Qt</OutputSubPath> + <SubType>Designer</SubType> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/dialogbuttonbottom/dialogbuttonbottom.ico b/Templates/dialogbuttonbottom/dialogbuttonbottom.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/dialogbuttonbottom/dialogbuttonbottom.ico Binary files differ diff --git a/Templates/dialogbuttonbottom/dialogbuttonbottom.ui b/Templates/dialogbuttonbottom/dialogbuttonbottom.ui new file mode 100644 index 0000000..5159f82 --- /dev/null +++ b/Templates/dialogbuttonbottom/dialogbuttonbottom.ui @@ -0,0 +1,100 @@ +<ui version="4.0"> + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <widget class="QWidget" name="layoutWidget"> + <property name="geometry"> + <rect> + <x>20</x> + <y>250</y> + <width>351</width> + <height>33</height> + </rect> + </property> + <layout class="QHBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint"> + <size> + <width>131</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton"> + <property name="text"> + <string>OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>278</x> + <y>253</y> + </hint> + <hint type="destinationlabel"> + <x>96</x> + <y>254</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>369</x> + <y>253</y> + </hint> + <hint type="destinationlabel"> + <x>179</x> + <y>282</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Templates/dialogbuttonbottom/dialogbuttonbottom.vstemplate b/Templates/dialogbuttonbottom/dialogbuttonbottom.vstemplate new file mode 100644 index 0000000..6cdb417 --- /dev/null +++ b/Templates/dialogbuttonbottom/dialogbuttonbottom.vstemplate @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Item"> + <TemplateData> + <Name>Qt Dialog Form File (Button Bottom)</Name> + <Icon>dialogbuttonbottom.ico</Icon> + <ProjectType>VC</ProjectType> + <AppliesTo>VisualC</AppliesTo> + <DefaultName>DialogButtonBottom.ui</DefaultName> + <TemplateGroupID>QtVsTools</TemplateGroupID> + <TemplateID>QtVsTools-QDialog.ButtonBottom</TemplateID> + <Description>Qt Dialog Form File (.ui) with two buttons on the bottom.</Description> + </TemplateData> + <TemplateContent> + <ProjectItem>dialogbuttonbottom.ui</ProjectItem> + </TemplateContent> +</VSTemplate> diff --git a/Templates/dialogbuttonright/Properties/AssemblyInfo.cs b/Templates/dialogbuttonright/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e463053 --- /dev/null +++ b/Templates/dialogbuttonright/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DialogButtonRight")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("BF0A5A93-25BE-464D-BD0D-6EB0702944BB")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/dialogbuttonright/QtTemplate.Item.DialogButtonRight.csproj b/Templates/dialogbuttonright/QtTemplate.Item.DialogButtonRight.csproj new file mode 100644 index 0000000..ad22581 --- /dev/null +++ b/Templates/dialogbuttonright/QtTemplate.Item.DialogButtonRight.csproj @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>dialogbuttonright.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{F2166B59-E41B-4328-B31D-9E2B9AC5A59C}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>DialogButtonRight</RootNamespace> + <AssemblyName>DialogButtonRight</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <Content Include="dialogbuttonright.ico" /> + <None Include="dialogbuttonright.ui" /> + <VSTemplate Include="dialogbuttonright.vstemplate"> + <OutputSubPath>Qt</OutputSubPath> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/dialogbuttonright/dialogbuttonright.ico b/Templates/dialogbuttonright/dialogbuttonright.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/dialogbuttonright/dialogbuttonright.ico Binary files differ diff --git a/Templates/dialogbuttonright/dialogbuttonright.ui b/Templates/dialogbuttonright/dialogbuttonright.ui new file mode 100644 index 0000000..b15ef07 --- /dev/null +++ b/Templates/dialogbuttonright/dialogbuttonright.ui @@ -0,0 +1,100 @@ +<ui version="4.0"> + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Dialog</class> + <widget class="QDialog" name="Dialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <widget class="QWidget" name="layoutWidget"> + <property name="geometry"> + <rect> + <x>300</x> + <y>20</y> + <width>77</width> + <height>106</height> + </rect> + </property> + <layout class="QVBoxLayout"> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item> + <widget class="QPushButton" name="okButton"> + <property name="text"> + <string>OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton"> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>351</x> + <y>38</y> + </hint> + <hint type="destinationlabel"> + <x>233</x> + <y>29</y> + </hint> + </hints> + </connection> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>330</x> + <y>66</y> + </hint> + <hint type="destinationlabel"> + <x>197</x> + <y>125</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Templates/dialogbuttonright/dialogbuttonright.vstemplate b/Templates/dialogbuttonright/dialogbuttonright.vstemplate new file mode 100644 index 0000000..8500be3 --- /dev/null +++ b/Templates/dialogbuttonright/dialogbuttonright.vstemplate @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Item"> + <TemplateData> + <Name>Qt Dialog Form File (Button Right)</Name> + <Icon>dialogbuttonright.ico</Icon> + <ProjectType>VC</ProjectType> + <AppliesTo>VisualC</AppliesTo> + <DefaultName>DialogButtonRight.ui</DefaultName> + <TemplateGroupID>QtVsTools</TemplateGroupID> + <TemplateID>QtVsTools-QDialog.ButtonRight</TemplateID> + <Description>Qt Dialog Form File (.ui) with two buttons on the right side.</Description> + </TemplateData> + <TemplateContent> + <ProjectItem>dialogbuttonright.ui</ProjectItem> + </TemplateContent> +</VSTemplate> diff --git a/Templates/empty/Properties/AssemblyInfo.cs b/Templates/empty/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8f05c2f --- /dev/null +++ b/Templates/empty/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Empty")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("863A59E4-25DB-4C28-9D50-1111A2DAD989")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/empty/QtTemplate.Project.Empty.csproj b/Templates/empty/QtTemplate.Project.Empty.csproj new file mode 100644 index 0000000..536168f --- /dev/null +++ b/Templates/empty/QtTemplate.Project.Empty.csproj @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>empty.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Empty</RootNamespace> + <AssemblyName>Empty</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="empty.ico" /> + <None Include="empty.vcxproj"> + <SubType>Designer</SubType> + </None> + <None Include="empty.vcxproj.filters"> + <SubType>Designer</SubType> + </None> + <T4Template Include="empty.vstemplate_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>empty.vstemplate</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>empty.vstemplate</LastGenOutput> + </T4Template> + <VSTemplate Include="empty.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>empty.vstemplate_TT</DependentUpon> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/empty/empty.ico b/Templates/empty/empty.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/empty/empty.ico Binary files differ diff --git a/Templates/empty/empty.vcxproj b/Templates/empty/empty.vcxproj new file mode 100644 index 0000000..169cce4 --- /dev/null +++ b/Templates/empty/empty.vcxproj @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$ToolsVersion$" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + $ProjectConfigurations$ + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$ProjectGuid$</ProjectGuid> + <Keyword>$Keyword$</Keyword> +$Globals$ + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')" + >$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> +$Configurations$ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> +$QtSettings$ + <Target Name="QtMsBuildNotFound" + BeforeTargets="CustomBuild;ClCompile" + Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" + Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> +$PropertySheets$ + <PropertyGroup Label="UserMacros" /> +$Properties$ +$BuildSettings$ + <ItemGroup> +$ProjectItems$ + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Templates/empty/empty.vcxproj.filters b/Templates/empty/empty.vcxproj.filters new file mode 100644 index 0000000..eb9be8d --- /dev/null +++ b/Templates/empty/empty.vcxproj.filters @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> +</Project> diff --git a/Templates/empty/empty.vstemplate_TT b/Templates/empty/empty.vstemplate_TT new file mode 100644 index 0000000..80a67a7 --- /dev/null +++ b/Templates/empty/empty.vstemplate_TT @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vstemplate" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Project" > + <TemplateData> + <Name>Qt Empty Application</Name> + <Description>This wizard generates an empty Qt application project.</Description> + <ProjectType>VC</ProjectType> + <SortOrder>1000</SortOrder> + <CreateNewFolder>true</CreateNewFolder> + <DefaultName>QtApplication</DefaultName> + <ProvideDefaultName>true</ProvideDefaultName> + <LocationField>Enabled</LocationField> + <EnableLocationBrowseButton>true</EnableLocationBrowseButton> + <Icon>empty.ico</Icon> + <LanguageTag>Cpp</LanguageTag> + <PlatformTag>Windows</PlatformTag> + <PlatformTag>Linux</PlatformTag> + <ProjectTypeTag>Qt</ProjectTypeTag> + </TemplateData> + <TemplateContent> + <Project TargetFileName="$safeprojectname$.vcxproj" + File="empty.vcxproj" + ReplaceParameters="true"> + <ProjectItem ReplaceParameters="true" + TargetFileName="$safeprojectname$.vcxproj.filters">empty.vcxproj.filters</ProjectItem> + <ProjectItem ReplaceParameters="false">empty.ico</ProjectItem> + </Project> + </TemplateContent> + <WizardExtension> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Assembly>QtVsTools.Wizards, Version=<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>, Culture=neutral, PublicKeyToken=null</Assembly> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <FullClassName>QtVsTools.Wizards.ProjectWizard.EmptyWizard</FullClassName> + </WizardExtension> +</VSTemplate> diff --git a/Templates/gui/Properties/AssemblyInfo.cs b/Templates/gui/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9fd19bc --- /dev/null +++ b/Templates/gui/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Gui")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("F4A39ED8-47DA-439F-A2EF-C1F721618ED3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/gui/QtTemplate.Project.Gui.csproj b/Templates/gui/QtTemplate.Project.Gui.csproj new file mode 100644 index 0000000..1b373ae --- /dev/null +++ b/Templates/gui/QtTemplate.Project.Gui.csproj @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>gui.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{3E1F9741-01D3-480A-A484-3CD373AE4A7F}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Gui</RootNamespace> + <AssemblyName>Gui</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="Microsoft.VisualStudio.CoreUtility"> + <Private>False</Private> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="gui.ico" /> + <None Include="gui.vcxproj"> + <SubType>Designer</SubType> + </None> + <None Include="gui.vcxproj.filters"> + <SubType>Designer</SubType> + </None> + <None Include="main.cpp" /> + <None Include="stdafx.cpp" /> + <None Include="stdafx.h" /> + <None Include="widget.cpp" /> + <None Include="widget.h" /> + <None Include="widget.ui" /> + </ItemGroup> + <ItemGroup> + <T4Template Include="gui.vstemplate_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>gui.vstemplate</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>gui.vstemplate</LastGenOutput> + </T4Template> + <VSTemplate Include="gui.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>gui.vstemplate_TT</DependentUpon> + </VSTemplate> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/gui/gui.ico b/Templates/gui/gui.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/gui/gui.ico Binary files differ diff --git a/Templates/gui/gui.vcxproj b/Templates/gui/gui.vcxproj new file mode 100644 index 0000000..1787545 --- /dev/null +++ b/Templates/gui/gui.vcxproj @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$ToolsVersion$" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> +$ProjectConfigurations$ + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$ProjectGuid$</ProjectGuid> + <Keyword>$Keyword$</Keyword> +$Globals$ + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')" + >$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> +$Configurations$ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> +$QtSettings$ + <Target Name="QtMsBuildNotFound" + BeforeTargets="CustomBuild;ClCompile" + Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" + Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> +$PropertySheets$ + <PropertyGroup Label="UserMacros" /> +$Properties$ +$BuildSettings$ + <ItemGroup> + <QtRcc Include="$qrcfilename$"/> + <QtUic Include="$uifilename$"/> + <QtMoc Include="$headerfilename$"/> + <ClCompile Include="$sourcefilename$"/> + <ClCompile Include="main.cpp"/> +$ProjectItems$ + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Templates/gui/gui.vcxproj.filters b/Templates/gui/gui.vcxproj.filters new file mode 100644 index 0000000..90f5b7a --- /dev/null +++ b/Templates/gui/gui.vcxproj.filters @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <QtRcc Include="$qrcfilename$"> + <Filter>Resource Files</Filter> + </QtRcc> + <QtUic Include="$uifilename$"> + <Filter>Form Files</Filter> + </QtUic> + <QtMoc Include="$headerfilename$"> + <Filter>Header Files</Filter> + </QtMoc> + <ClCompile Include="$sourcefilename$"> + <Filter>Source Files</Filter> + </ClCompile> + <ResourceCompile Include="$pro_name$.rc"> + </ResourceCompile> + <None Include="$pro_name$.ico"> + <Filter>Resource Files</Filter> + </None> + <None Include="$pro_name$.def"> + <Filter>Resource Files</Filter> + </None> +$FilterItems$ + </ItemGroup> +</Project> diff --git a/Templates/gui/gui.vstemplate_TT b/Templates/gui/gui.vstemplate_TT new file mode 100644 index 0000000..7a09db3 --- /dev/null +++ b/Templates/gui/gui.vstemplate_TT @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vstemplate" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Project" > + <TemplateData> + <Name>Qt Widgets Application</Name> + <Description>This wizard generates a Qt Widgets application project.</Description> + <ProjectType>VC</ProjectType> + <SortOrder>1000</SortOrder> + <CreateNewFolder>true</CreateNewFolder> + <DefaultName>QtWidgetsApplication</DefaultName> + <ProvideDefaultName>true</ProvideDefaultName> + <LocationField>Enabled</LocationField> + <EnableLocationBrowseButton>true</EnableLocationBrowseButton> + <Icon>gui.ico</Icon> + <LanguageTag>Cpp</LanguageTag> + <PlatformTag>Windows</PlatformTag> + <PlatformTag>Linux</PlatformTag> + <ProjectTypeTag>Qt</ProjectTypeTag> + <ProjectTypeTag>Desktop</ProjectTypeTag> + </TemplateData> + <TemplateContent> + <Project TargetFileName="$safeprojectname$.vcxproj" + File="gui.vcxproj" + ReplaceParameters="true"> + <ProjectItem ReplaceParameters="true">main.cpp</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.cpp</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.h</ProjectItem> + <ProjectItem ReplaceParameters="true" + OpenInEditor="true" + TargetFileName="$sourcefilename$">widget.cpp</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$headerfilename$">widget.h</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$uifilename$">widget.ui</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$safeprojectname$.vcxproj.filters">gui.vcxproj.filters</ProjectItem> + <ProjectItem ReplaceParameters="false">gui.ico</ProjectItem> + </Project> + </TemplateContent> + <WizardExtension> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Assembly>QtVsTools.Wizards, Version=<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>, Culture=neutral, PublicKeyToken=null</Assembly> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <FullClassName>QtVsTools.Wizards.ProjectWizard.GuiWizard</FullClassName> + </WizardExtension> +</VSTemplate> diff --git a/Templates/gui/main.cpp b/Templates/gui/main.cpp new file mode 100644 index 0000000..6478e3f --- /dev/null +++ b/Templates/gui/main.cpp @@ -0,0 +1,10 @@ +$include$ +#include <QtWidgets/QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + $classname$ w; + w.show(); + return a.exec(); +} diff --git a/Templates/gui/stdafx.cpp b/Templates/gui/stdafx.cpp new file mode 100644 index 0000000..a27b824 --- /dev/null +++ b/Templates/gui/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/Templates/gui/stdafx.h b/Templates/gui/stdafx.h new file mode 100644 index 0000000..51b684f --- /dev/null +++ b/Templates/gui/stdafx.h @@ -0,0 +1 @@ +#include <QtWidgets> diff --git a/Templates/gui/widget.cpp b/Templates/gui/widget.cpp new file mode 100644 index 0000000..28c285b --- /dev/null +++ b/Templates/gui/widget.cpp @@ -0,0 +1,7 @@ +$include$ + +$classname$::$classname$(QWidget *parent) + : $baseclass$(parent) +{ + ui.setupUi(this); +} diff --git a/Templates/gui/widget.h b/Templates/gui/widget.h new file mode 100644 index 0000000..578cb5e --- /dev/null +++ b/Templates/gui/widget.h @@ -0,0 +1,15 @@ +#pragma once + +#include <QtWidgets/$baseclass$> +#include "$ui_hdr$" + +class $classname$ : public $baseclass$ +{ + Q_OBJECT + +public: + $classname$(QWidget *parent = Q_NULLPTR); + +private: + Ui::$classname$Class ui; +}; diff --git a/Templates/gui/widget.ui b/Templates/gui/widget.ui new file mode 100644 index 0000000..6ee2bb0 --- /dev/null +++ b/Templates/gui/widget.ui @@ -0,0 +1,25 @@ +<UI version="4.0" > + <class>$classname$Class</class> + <widget class="$baseclass$" name="$classname$Class" > + <property name="objectName" > + <string notr="true">$classname$Class</string> + </property> + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle" > + <string>$classname$</string> + </property>$centralwidget$ + </widget> + <layoutDefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <resources> + <include location="$qrcfilename$"/> + </resources> + <connections/> +</UI> diff --git a/Templates/lib/Properties/AssemblyInfo.cs b/Templates/lib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9bd5d54 --- /dev/null +++ b/Templates/lib/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Library")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0FE20142-A7BD-4E8B-AD32-327157075371")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/lib/QtTemplate.Project.Lib.csproj b/Templates/lib/QtTemplate.Project.Lib.csproj new file mode 100644 index 0000000..1b7c02e --- /dev/null +++ b/Templates/lib/QtTemplate.Project.Lib.csproj @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>lib.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{191B1B91-D9FD-4138-A72A-AB0B149958C6}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Library</RootNamespace> + <AssemblyName>Library</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="lib.ico" /> + <None Include="lib.vcxproj"> + <SubType>Designer</SubType> + </None> + <None Include="lib.vcxproj.filters"> + <SubType>Designer</SubType> + </None> + <T4Template Include="lib.vstemplate_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>lib.vstemplate</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>lib.vstemplate</LastGenOutput> + </T4Template> + <VSTemplate Include="lib.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>lib.vstemplate_TT</DependentUpon> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Content Include="global.h" /> + <Content Include="header.h" /> + <Content Include="source.cpp" /> + <Content Include="stdafx.cpp" /> + <Content Include="stdafx.h" /> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/lib/global.h b/Templates/lib/global.h new file mode 100644 index 0000000..a4a9c67 --- /dev/null +++ b/Templates/lib/global.h @@ -0,0 +1,13 @@ +#pragma once + +#include <QtCore/qglobal.h> + +#ifndef BUILD_STATIC +# if defined($pro_lib_define$) +# define $pro_lib_export$ Q_DECL_EXPORT +# else +# define $pro_lib_export$ Q_DECL_IMPORT +# endif +#else +# define $pro_lib_export$ +#endif diff --git a/Templates/lib/header.h b/Templates/lib/header.h new file mode 100644 index 0000000..9fa9306 --- /dev/null +++ b/Templates/lib/header.h @@ -0,0 +1,9 @@ +#pragma once + +#include "$saveglobal$_global.h" + +class $pro_lib_export$ $classname$ +{ +public: + $classname$(); +}; diff --git a/Templates/lib/lib.ico b/Templates/lib/lib.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/lib/lib.ico Binary files differ diff --git a/Templates/lib/lib.vcxproj b/Templates/lib/lib.vcxproj new file mode 100644 index 0000000..332e40f --- /dev/null +++ b/Templates/lib/lib.vcxproj @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$ToolsVersion$" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> +$ProjectConfigurations$ + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$ProjectGuid$</ProjectGuid> + <Keyword>$Keyword$</Keyword> +$Globals$ + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')" + >$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> +$Configurations$ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> +$QtSettings$ + <Target Name="QtMsBuildNotFound" + BeforeTargets="CustomBuild;ClCompile" + Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" + Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> +$PropertySheets$ + <PropertyGroup Label="UserMacros" /> +$Properties$ +$BuildSettings$ + <ItemGroup> + <ClInclude Include="$saveglobal$_global.h"/> + <QtMoc Include="$headerfilename$"/> + <ClCompile Include="$sourcefilename$"/> +$ProjectItems$ + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Templates/lib/lib.vcxproj.filters b/Templates/lib/lib.vcxproj.filters new file mode 100644 index 0000000..1630278 --- /dev/null +++ b/Templates/lib/lib.vcxproj.filters @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="$saveglobal$_global.h"> + <Filter>Header Files</Filter> + </ClInclude> + <QtMoc Include="$headerfilename$"> + <Filter>Header Files</Filter> + </QtMoc> + <ClCompile Include="$sourcefilename$"> + <Filter>Source Files</Filter> + </ClCompile> +$FilterItems$ + </ItemGroup> +</Project> diff --git a/Templates/lib/lib.vstemplate_TT b/Templates/lib/lib.vstemplate_TT new file mode 100644 index 0000000..5232d32 --- /dev/null +++ b/Templates/lib/lib.vstemplate_TT @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vstemplate" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Project" > + <TemplateData> + <Name>Qt Class Library</Name> + <Description>This wizard generates a Qt class library project.</Description> + <ProjectType>VC</ProjectType> + <SortOrder>1000</SortOrder> + <CreateNewFolder>true</CreateNewFolder> + <DefaultName>QtClassLibrary</DefaultName> + <ProvideDefaultName>true</ProvideDefaultName> + <LocationField>Enabled</LocationField> + <EnableLocationBrowseButton>true</EnableLocationBrowseButton> + <Icon>lib.ico</Icon> + <LanguageTag>Cpp</LanguageTag> + <PlatformTag>Windows</PlatformTag> + <PlatformTag>Linux</PlatformTag> + <ProjectTypeTag>Qt</ProjectTypeTag> + <ProjectTypeTag>Library</ProjectTypeTag> + <ProjectTypeTag>Desktop</ProjectTypeTag> + <ProjectTypeTag>IoT</ProjectTypeTag> + </TemplateData> + <TemplateContent> + <Project TargetFileName="$safeprojectname$.vcxproj" + File="lib.vcxproj" + ReplaceParameters="true"> + <ProjectItem ReplaceParameters="true" + OpenInEditor="true" + TargetFileName="$sourcefilename$">source.cpp</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$headerfilename$">header.h</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$saveglobal$_global.h">global.h</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.cpp</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.h</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$safeprojectname$.vcxproj.filters">lib.vcxproj.filters</ProjectItem> + </Project> + </TemplateContent> + <WizardExtension> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Assembly>QtVsTools.Wizards, Version=<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>, Culture=neutral, PublicKeyToken=null</Assembly> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <FullClassName>QtVsTools.Wizards.ProjectWizard.LibraryWizard</FullClassName> + </WizardExtension> +</VSTemplate> diff --git a/Templates/lib/source.cpp b/Templates/lib/source.cpp new file mode 100644 index 0000000..394b126 --- /dev/null +++ b/Templates/lib/source.cpp @@ -0,0 +1,5 @@ +$include$ + +$classname$::$classname$() +{ +} diff --git a/Templates/lib/stdafx.cpp b/Templates/lib/stdafx.cpp new file mode 100644 index 0000000..a27b824 --- /dev/null +++ b/Templates/lib/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/Templates/lib/stdafx.h b/Templates/lib/stdafx.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Templates/lib/stdafx.h diff --git a/Templates/mainwindow/Properties/AssemblyInfo.cs b/Templates/mainwindow/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6b35cb2 --- /dev/null +++ b/Templates/mainwindow/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MainWindow")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("BF96E89B-4C36-4473-8A3A-0039A3EAE8EC")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/mainwindow/QtTemplate.Item.MainWindow.csproj b/Templates/mainwindow/QtTemplate.Item.MainWindow.csproj new file mode 100644 index 0000000..9683ca1 --- /dev/null +++ b/Templates/mainwindow/QtTemplate.Item.MainWindow.csproj @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>mainwindow.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{20055427-1352-44FB-8442-BF7F15F9C59E}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>MainWindow</RootNamespace> + <AssemblyName>MainWindow</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <Content Include="mainwindow.ico" /> + <None Include="mainwindow.ui" /> + <VSTemplate Include="mainwindow.vstemplate"> + <OutputSubPath>Qt</OutputSubPath> + <SubType>Designer</SubType> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/mainwindow/mainwindow.ico b/Templates/mainwindow/mainwindow.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/mainwindow/mainwindow.ico Binary files differ diff --git a/Templates/mainwindow/mainwindow.ui b/Templates/mainwindow/mainwindow.ui new file mode 100644 index 0000000..1ee5640 --- /dev/null +++ b/Templates/mainwindow/mainwindow.ui @@ -0,0 +1,51 @@ +<ui version="4.0"> + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>21</y> + <width>800</width> + <height>560</height> + </rect> + </property> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>21</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>581</y> + <width>800</width> + <height>19</height> + </rect> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <connections/> +</ui> diff --git a/Templates/mainwindow/mainwindow.vstemplate b/Templates/mainwindow/mainwindow.vstemplate new file mode 100644 index 0000000..5ad1a84 --- /dev/null +++ b/Templates/mainwindow/mainwindow.vstemplate @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Item"> + <TemplateData> + <Name>Qt MainWindow Form File</Name> + <Icon>mainwindow.ico</Icon> + <ProjectType>VC</ProjectType> + <AppliesTo>VisualC</AppliesTo> + <DefaultName>MainWindow.ui</DefaultName> + <Description>Qt MainWindow Form File (.ui)</Description> + <TemplateID>QtVsTools-QMainWindow</TemplateID> + <TemplateGroupID>QtVsTools</TemplateGroupID> + </TemplateData> + <TemplateContent> + <ProjectItem>mainwindow.ui</ProjectItem> + </TemplateContent> +</VSTemplate> diff --git a/Templates/qml/NewFile.qml b/Templates/qml/NewFile.qml new file mode 100644 index 0000000..da86988 --- /dev/null +++ b/Templates/qml/NewFile.qml @@ -0,0 +1,12 @@ +import QtQuick 2.3 + +Rectangle { + width: 200 + height: 100 + color: "red" + + Text { + anchors.centerIn: parent + text: "Hello, World!" + } +} diff --git a/Templates/qml/Properties/AssemblyInfo.cs b/Templates/qml/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..17693b9 --- /dev/null +++ b/Templates/qml/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Resource")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2020 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e9ed6bc1-f840-4ab2-8115-9fb754cd9a11")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/qml/QtTemplate.Item.QMLFile.csproj b/Templates/qml/QtTemplate.Item.QMLFile.csproj new file mode 100644 index 0000000..1d26bac --- /dev/null +++ b/Templates/qml/QtTemplate.Item.QMLFile.csproj @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>qml.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{DC1AE91B-45CE-4C5B-8F77-CDB58566038F}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>QMLFile</RootNamespace> + <AssemblyName>QMLFile</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="NewFile.qml" /> + </ItemGroup> + <ItemGroup> + <VSTemplate Include="qml.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Content Include="qml.ico" /> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file diff --git a/Templates/qml/qml.ico b/Templates/qml/qml.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/qml/qml.ico Binary files differ diff --git a/Templates/qml/qml.vstemplate b/Templates/qml/qml.vstemplate new file mode 100644 index 0000000..fc5547e --- /dev/null +++ b/Templates/qml/qml.vstemplate @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2020 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Item"> + <TemplateData> + <Name>Qt QML File</Name> + <Icon>qml.ico</Icon> + <ProjectType>VC</ProjectType> + <AppliesTo>VisualC</AppliesTo> + <DefaultName>NewFile.qml</DefaultName> + <TemplateGroupID>QtVsTools</TemplateGroupID> + <TemplateID>QtVsTools-QMLFile</TemplateID> + <Description>Qt QML File (.qml)</Description> + </TemplateData> + <TemplateContent> + <ProjectItem>NewFile.qml</ProjectItem> + </TemplateContent> +</VSTemplate> diff --git a/Templates/qmldir/Properties/AssemblyInfo.cs b/Templates/qmldir/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..17693b9 --- /dev/null +++ b/Templates/qmldir/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Resource")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2020 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e9ed6bc1-f840-4ab2-8115-9fb754cd9a11")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/qmldir/QtTemplate.Item.QMLDir.csproj b/Templates/qmldir/QtTemplate.Item.QMLDir.csproj new file mode 100644 index 0000000..a3cdb07 --- /dev/null +++ b/Templates/qmldir/QtTemplate.Item.QMLDir.csproj @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>qml.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{7AF6C34B-65D2-4010-92F6-420E59DDE9BF}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>QMLDir</RootNamespace> + <AssemblyName>QMLDir</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="qmldir" /> + </ItemGroup> + <ItemGroup> + <VSTemplate Include="qmldir.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Content Include="qml.ico" /> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> \ No newline at end of file diff --git a/Templates/qmldir/qml.ico b/Templates/qmldir/qml.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/qmldir/qml.ico Binary files differ diff --git a/Templates/qmldir/qmldir b/Templates/qmldir/qmldir new file mode 100644 index 0000000..2de5c03 --- /dev/null +++ b/Templates/qmldir/qmldir @@ -0,0 +1 @@ +MyQmlType 1.0 MyQmlType.qml diff --git a/Templates/qmldir/qmldir.vstemplate b/Templates/qmldir/qmldir.vstemplate new file mode 100644 index 0000000..d2035ad --- /dev/null +++ b/Templates/qmldir/qmldir.vstemplate @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2020 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Item"> + <TemplateData> + <Name>Qt QML Module Definition</Name> + <Icon>qml.ico</Icon> + <ProjectType>VC</ProjectType> + <AppliesTo>VisualC</AppliesTo> + <DefaultName>qmldir</DefaultName> + <TemplateGroupID>QtVsTools</TemplateGroupID> + <TemplateID>QtVsTools-QMLDir</TemplateID> + <Description>Qt QML Module Definition (qmldir)</Description> + </TemplateData> + <TemplateContent> + <ProjectItem>qmldir</ProjectItem> + </TemplateContent> +</VSTemplate> diff --git a/Templates/quick/Properties/AssemblyInfo.cs b/Templates/quick/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0aa83ed --- /dev/null +++ b/Templates/quick/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Quick")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("863A59E4-25DB-4C28-9D50-1111A2DAD989")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/quick/QtTemplate.Project.Quick.csproj b/Templates/quick/QtTemplate.Project.Quick.csproj new file mode 100644 index 0000000..3137fb0 --- /dev/null +++ b/Templates/quick/QtTemplate.Project.Quick.csproj @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>quick.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Quick</RootNamespace> + <AssemblyName>Quick</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="qml.qrc" /> + <None Include="quick.ico" /> + <None Include="quick.vcxproj"> + <SubType>Designer</SubType> + </None> + <None Include="quick.vcxproj.filters"> + <SubType>Designer</SubType> + </None> + <T4Template Include="quick.vstemplate_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>quick.vstemplate</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>quick.vstemplate</LastGenOutput> + <SubType>Designer</SubType> + </T4Template> + <VSTemplate Include="quick.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>quick.vstemplate_TT</DependentUpon> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <ItemGroup> + <Content Include="main.cpp" /> + <Content Include="main.qml" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> +</Project> \ No newline at end of file diff --git a/Templates/quick/main.cpp b/Templates/quick/main.cpp new file mode 100644 index 0000000..ffe6b20 --- /dev/null +++ b/Templates/quick/main.cpp @@ -0,0 +1,18 @@ +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char *argv[]) +{ +#if defined(Q_OS_WIN) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); +} diff --git a/Templates/quick/main.qml b/Templates/quick/main.qml new file mode 100644 index 0000000..ddf6885 --- /dev/null +++ b/Templates/quick/main.qml @@ -0,0 +1,9 @@ +import QtQuick 2.9 +import QtQuick.Window 2.2 + +Window { + visible: true + width: 640 + height: 480 + title: qsTr("Hello World") +} diff --git a/Templates/quick/qml.qrc b/Templates/quick/qml.qrc new file mode 100644 index 0000000..219fc5b --- /dev/null +++ b/Templates/quick/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/Templates/quick/quick.ico b/Templates/quick/quick.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/quick/quick.ico Binary files differ diff --git a/Templates/quick/quick.vcxproj b/Templates/quick/quick.vcxproj new file mode 100644 index 0000000..417bb63 --- /dev/null +++ b/Templates/quick/quick.vcxproj @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$ToolsVersion$" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + $ProjectConfigurations$ + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$ProjectGuid$</ProjectGuid> + <Keyword>$Keyword$</Keyword> +$Globals$ + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')" + >$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> +$Configurations$ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> +$QtSettings$ + <Target Name="QtMsBuildNotFound" + BeforeTargets="CustomBuild;ClCompile" + Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" + Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> +$PropertySheets$ + <PropertyGroup Label="UserMacros" /> +$Properties$ +$BuildSettings$ + <ItemGroup> + <ClCompile Include="main.cpp"/> + <QtRcc Include="qml.qrc"/> + <None Include="main.qml"/> +$ProjectItems$ + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/Templates/quick/quick.vcxproj.filters b/Templates/quick/quick.vcxproj.filters new file mode 100644 index 0000000..1ed1ade --- /dev/null +++ b/Templates/quick/quick.vcxproj.filters @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <QtRcc Include="qml.qrc"> + <Filter>Resource Files</Filter> + </QtRcc> + <None Include="main.qml"> + <Filter>Source Files</Filter> + </None> +$FilterItems$ + </ItemGroup> +</Project> diff --git a/Templates/quick/quick.vstemplate_TT b/Templates/quick/quick.vstemplate_TT new file mode 100644 index 0000000..323f355 --- /dev/null +++ b/Templates/quick/quick.vstemplate_TT @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2020 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vstemplate" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Project" > + <TemplateData> + <Name>Qt Quick Application</Name> + <Description>This wizard generates a Qt Quick application project.</Description> + <ProjectType>VC</ProjectType> + <SortOrder>1000</SortOrder> + <CreateNewFolder>true</CreateNewFolder> + <DefaultName>QtQuickApplication</DefaultName> + <ProvideDefaultName>true</ProvideDefaultName> + <LocationField>Enabled</LocationField> + <EnableLocationBrowseButton>true</EnableLocationBrowseButton> + <Icon>quick.ico</Icon> + <LanguageTag>Cpp</LanguageTag> + <LanguageTag>QML</LanguageTag> + <PlatformTag>Windows</PlatformTag> + <PlatformTag>Linux</PlatformTag> + <ProjectTypeTag>Qt</ProjectTypeTag> + <ProjectTypeTag>Desktop</ProjectTypeTag> + <ProjectTypeTag>IoT</ProjectTypeTag> + </TemplateData> + <TemplateContent> + <Project TargetFileName="$safeprojectname$.vcxproj" + File="quick.vcxproj" + ReplaceParameters="true"> + <ProjectItem ReplaceParameters="true" + TargetFileName="$safeprojectname$.vcxproj.filters">quick.vcxproj.filters</ProjectItem> + <ProjectItem ReplaceParameters="false">quick.ico</ProjectItem> + <ProjectItem ReplaceParameters="true">main.cpp</ProjectItem> + <ProjectItem ReplaceParameters="true">main.qml</ProjectItem> + <ProjectItem ReplaceParameters="true">qml.qrc</ProjectItem> + </Project> + </TemplateContent> + <WizardExtension> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Assembly>QtVsTools.Wizards, Version=<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>, Culture=neutral, PublicKeyToken=null</Assembly> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <FullClassName>QtVsTools.Wizards.ProjectWizard.QuickWizard</FullClassName> + </WizardExtension> +</VSTemplate> diff --git a/Templates/resource/Properties/AssemblyInfo.cs b/Templates/resource/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0c1084d --- /dev/null +++ b/Templates/resource/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Resource")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e9ed6bc1-f840-4ab2-8115-9fb754cd9a11")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/resource/QtTemplate.Item.Resource.csproj b/Templates/resource/QtTemplate.Item.Resource.csproj new file mode 100644 index 0000000..6881f8a --- /dev/null +++ b/Templates/resource/QtTemplate.Item.Resource.csproj @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>resource.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Resource</RootNamespace> + <AssemblyName>Resource</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="resource.qrc" /> + </ItemGroup> + <ItemGroup> + <VSTemplate Include="resource.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Content Include="resource.ico" /> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/resource/resource.ico b/Templates/resource/resource.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/resource/resource.ico Binary files differ diff --git a/Templates/resource/resource.qrc b/Templates/resource/resource.qrc new file mode 100644 index 0000000..2fade32 --- /dev/null +++ b/Templates/resource/resource.qrc @@ -0,0 +1,4 @@ +<RCC> + <qresource prefix="/new/prefix1"> + </qresource> +</RCC> diff --git a/Templates/resource/resource.vstemplate b/Templates/resource/resource.vstemplate new file mode 100644 index 0000000..e870b29 --- /dev/null +++ b/Templates/resource/resource.vstemplate @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Item"> + <TemplateData> + <Name>Qt Resource File</Name> + <Icon>resource.ico</Icon> + <ProjectType>VC</ProjectType> + <AppliesTo>VisualC</AppliesTo> + <DefaultName>Resource.qrc</DefaultName> + <TemplateGroupID>QtVsTools</TemplateGroupID> + <TemplateID>QtVsTools-QResource</TemplateID> + <Description>Qt Resource Collection File (.qrc)</Description> + </TemplateData> + <TemplateContent> + <ProjectItem>resource.qrc</ProjectItem> + </TemplateContent> +</VSTemplate> diff --git a/Templates/server/Properties/AssemblyInfo.cs b/Templates/server/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0f1254b --- /dev/null +++ b/Templates/server/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Server")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0FE20142-A7BD-4E8B-AD32-327827075371")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/server/QtTemplate.Project.Server.csproj b/Templates/server/QtTemplate.Project.Server.csproj new file mode 100644 index 0000000..b30bb48 --- /dev/null +++ b/Templates/server/QtTemplate.Project.Server.csproj @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>server.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{8AE9D385-A379-4F5F-A703-3DF643DA6742}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Server</RootNamespace> + <AssemblyName>Server</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <None Include="server.ico" /> + <None Include="server.vcxproj"> + <SubType>Designer</SubType> + </None> + <None Include="server.vcxproj.filters"> + <SubType>Designer</SubType> + </None> + <T4Template Include="server.vstemplate_TT"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>server.vstemplate</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>server.vstemplate</LastGenOutput> + </T4Template> + <VSTemplate Include="server.vstemplate"> + <SubType>Designer</SubType> + <OutputSubPath>Qt</OutputSubPath> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>server.vstemplate_TT</DependentUpon> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Content Include="header.h" /> + <Content Include="server.def" /> + <Content Include="server.rc" /> + <Content Include="source.cpp" /> + <Content Include="stdafx.cpp" /> + <Content Include="stdafx.h" /> + <Content Include="widget.ui" /> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/server/header.h b/Templates/server/header.h new file mode 100644 index 0000000..b3a1e6e --- /dev/null +++ b/Templates/server/header.h @@ -0,0 +1,17 @@ +#pragma once + +#include <QtWidgets/QWidget> +#include <ActiveQt/QAxBindable> + +#include "$ui_hdr$" + +class $classname$ : public QWidget, public QAxBindable +{ + Q_OBJECT + +public: + $classname$(QWidget *parent = Q_NULLPTR); + +private: + Ui::$classname$Class ui; +}; diff --git a/Templates/server/server.def b/Templates/server/server.def new file mode 100644 index 0000000..3e022af --- /dev/null +++ b/Templates/server/server.def @@ -0,0 +1,8 @@ +; Declares the module parameters. + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + DumpIDL PRIVATE diff --git a/Templates/server/server.ico b/Templates/server/server.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/server/server.ico Binary files differ diff --git a/Templates/server/server.rc b/Templates/server/server.rc new file mode 100644 index 0000000..4620a24 --- /dev/null +++ b/Templates/server/server.rc @@ -0,0 +1,2 @@ +1 TYPELIB "$pro_name$.rc" +1 ICON DISCARDABLE "$pro_name$.ico" diff --git a/Templates/server/server.vcxproj b/Templates/server/server.vcxproj new file mode 100644 index 0000000..e1bc701 --- /dev/null +++ b/Templates/server/server.vcxproj @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$ToolsVersion$" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> +$ProjectConfigurations$ + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>$ProjectGuid$</ProjectGuid> + <Keyword>$Keyword$</Keyword> +$Globals$ + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')" + >$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> +$Configurations$ + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> +$QtSettings$ + <Target Name="QtMsBuildNotFound" + BeforeTargets="CustomBuild;ClCompile" + Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" + Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> +$PropertySheets$ + <PropertyGroup Label="UserMacros" /> +$Properties$ +$BuildSettings$ + <ItemGroup> + <QtUic Include="$uifilename$" /> + <QtMoc Include="$headerfilename$" /> + <ClCompile Include="$sourcefilename$" /> + <ResourceCompile Include="$pro_name$.rc" /> + <None Include="$pro_name$.ico" /> + <None Include="$pro_name$.def" /> +$ProjectItems$ + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Templates/server/server.vcxproj.filters b/Templates/server/server.vcxproj.filters new file mode 100644 index 0000000..1dca9ac --- /dev/null +++ b/Templates/server/server.vcxproj.filters @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> +</Project> diff --git a/Templates/server/server.vstemplate_TT b/Templates/server/server.vstemplate_TT new file mode 100644 index 0000000..10133bb --- /dev/null +++ b/Templates/server/server.vstemplate_TT @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +<#@output extension="vstemplate" #> +<#@include file="$(SolutionDir)\version.tt" #> + ** <#=WARNING_GENERATED_FILE#> + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Project" > + <TemplateData> + <Name>Qt ActiveQt Server</Name> + <Description>This wizard generates a Qt ActiveQt server project.</Description> + <ProjectType>VC</ProjectType> + <SortOrder>1000</SortOrder> + <CreateNewFolder>true</CreateNewFolder> + <DefaultName>ActiveQtServer</DefaultName> + <ProvideDefaultName>true</ProvideDefaultName> + <LocationField>Enabled</LocationField> + <EnableLocationBrowseButton>true</EnableLocationBrowseButton> + <Icon>server.ico</Icon> + <LanguageTag>Cpp</LanguageTag> + <PlatformTag>Windows</PlatformTag> + <ProjectTypeTag>Qt</ProjectTypeTag> + <ProjectTypeTag>Library</ProjectTypeTag> + <ProjectTypeTag>Desktop</ProjectTypeTag> + </TemplateData> + <TemplateContent> + <Project TargetFileName="$safeprojectname$.vcxproj" + File="server.vcxproj" + ReplaceParameters="true"> + <ProjectItem ReplaceParameters="true" + TargetFileName="$headerfilename$">header.h</ProjectItem> + <ProjectItem ReplaceParameters="false" + TargetFileName="$pro_name$.def">server.def</ProjectItem> + <ProjectItem ReplaceParameters="false" + TargetFileName="$pro_name$.ico">server.ico</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$pro_name$.rc">server.rc</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$safeprojectname$.vcxproj.filters">server.vcxproj.filters</ProjectItem> + <ProjectItem ReplaceParameters="true" + OpenInEditor="true" + TargetFileName="$sourcefilename$">source.cpp</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.cpp</ProjectItem> + <ProjectItem ReplaceParameters="false">stdafx.h</ProjectItem> + <ProjectItem ReplaceParameters="true" + TargetFileName="$uifilename$">widget.ui</ProjectItem> + </Project> + </TemplateContent> + <WizardExtension> + <!-- BEGIN Generated Text <#=XML_COMMENT_END#> + <Assembly>QtVsTools.Wizards, Version=<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>, Culture=neutral, PublicKeyToken=null</Assembly> + <#=XML_COMMENT_BEGIN#> END Generated Text --> + <FullClassName>QtVsTools.Wizards.ProjectWizard.ServerWizard</FullClassName> + </WizardExtension> +</VSTemplate> diff --git a/Templates/server/source.cpp b/Templates/server/source.cpp new file mode 100644 index 0000000..a79dab1 --- /dev/null +++ b/Templates/server/source.cpp @@ -0,0 +1,17 @@ +$include$ + +#include <ActiveQt/QAxFactory> + +$classname$::$classname$(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); +} + +QAXFACTORY_DEFAULT($classname$, + "{$guid1$}", + "{$guid2$}", + "{$guid3$}", + "{$guid4$}", + "{$guid5$}" +) diff --git a/Templates/server/stdafx.cpp b/Templates/server/stdafx.cpp new file mode 100644 index 0000000..a27b824 --- /dev/null +++ b/Templates/server/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/Templates/server/stdafx.h b/Templates/server/stdafx.h new file mode 100644 index 0000000..51b684f --- /dev/null +++ b/Templates/server/stdafx.h @@ -0,0 +1 @@ +#include <QtWidgets> diff --git a/Templates/server/widget.ui b/Templates/server/widget.ui new file mode 100644 index 0000000..3c15d1e --- /dev/null +++ b/Templates/server/widget.ui @@ -0,0 +1,20 @@ +<UI version="4.0" > + <class>$classname$Class</class> + <widget class="QWidget" name="$classname$Class" > + <property name="objectName" > + <string notr="true">$classname$Class</string> + </property> + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>482</height> + </rect> + </property> + <property name="windowTitle" > + <string>$classname$</string> + </property> + </widget> + <layoutDefault spacing="6" margin="11" /> +</UI> diff --git a/Templates/widget/Properties/AssemblyInfo.cs b/Templates/widget/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cd236e1 --- /dev/null +++ b/Templates/widget/Properties/AssemblyInfo.cs @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +**************************************************************************** +<#@output extension="tt.cs" #> +<#@include file="$(SolutionDir)\version.tt" #> +** <#=WARNING_GENERATED_FILE#> +****************************************************************************/ + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Widget")] +[assembly: AssemblyDescription("The Qt Visual Studio Tools allow developers to use the standard development environment without having to worry about any Qt-related build steps or tools.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Qt Company Ltd.")] +[assembly: AssemblyProduct("Qt Visual Studio Tools")] +[assembly: AssemblyCopyright("Copyright (C) 2016-2017 The Qt Company Ltd.")] +[assembly: AssemblyTrademark("The Qt Company Ltd. Qt and their respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("44E99815-34AC-4B36-A835-7A24E14A9E09")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY#>")] +[assembly: AssemblyFileVersion("<#=QT_VS_TOOLS_VERSION_ASSEMBLY_FILE#>")] diff --git a/Templates/widget/QtTemplate.Item.Widget.csproj b/Templates/widget/QtTemplate.Item.Widget.csproj new file mode 100644 index 0000000..c31a8ea --- /dev/null +++ b/Templates/widget/QtTemplate.Item.Widget.csproj @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <MinimumVisualStudioVersion>$(VisualStudioVersion)</MinimumVisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + </PropertyGroup> + <PropertyGroup> + <ApplicationIcon>widget.ico</ApplicationIcon> + </PropertyGroup> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <ProjectGuid>{40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Widget</RootNamespace> + <AssemblyName>Widget</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <GeneratePkgDefFile>false</GeneratePkgDefFile> + <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> + <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer> + <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment> + <CreateVsixContainer>false</CreateVsixContainer> + <DeployExtension>false</DeployExtension> + <DeployVSTemplates>false</DeployVSTemplates> + <CopyVsixManifestToOutput>false</CopyVsixManifestToOutput> + <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory> + <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <UseVSHostingProcess>false</UseVSHostingProcess> + </PropertyGroup> + <ItemGroup> + <T4Template Include="Properties\AssemblyInfo.cs"> + <Generator>TextTemplatingFileGenerator</Generator> + <OutputFile>Properties\AssemblyInfo.tt.cs</OutputFile> + <DependsOn>$(SolutionDir)\version.tt;$(SolutionDir)\common.tt</DependsOn> + <LastGenOutput>AssemblyInfo.tt.cs</LastGenOutput> + </T4Template> + <Compile Include="Properties\AssemblyInfo.tt.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AssemblyInfo.cs</DependentUpon> + </Compile> + </ItemGroup> + <ItemGroup> + <Content Include="widget.ico" /> + <None Include="widget.ui" /> + <VSTemplate Include="widget.vstemplate"> + <OutputSubPath>Qt</OutputSubPath> + </VSTemplate> + </ItemGroup> + <ItemGroup> + <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project> diff --git a/Templates/widget/widget.ico b/Templates/widget/widget.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/Templates/widget/widget.ico Binary files differ diff --git a/Templates/widget/widget.ui b/Templates/widget/widget.ui new file mode 100644 index 0000000..05e7136 --- /dev/null +++ b/Templates/widget/widget.ui @@ -0,0 +1,24 @@ +<ui version="4.0"> + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form"> + <property name="objectName"> + <string notr="true">Form</string> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + </widget> + <pixmapfunction></pixmapfunction> + <connections/> +</ui> diff --git a/Templates/widget/widget.vstemplate b/Templates/widget/widget.vstemplate new file mode 100644 index 0000000..825d809 --- /dev/null +++ b/Templates/widget/widget.vstemplate @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ***************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + +<VSTemplate Version="3.0.0" + xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" + xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010" + Type="Item"> + <TemplateData> + <Name>Qt Widget Form File</Name> + <Icon>widget.ico</Icon> + <ProjectType>VC</ProjectType> + <AppliesTo>VisualC</AppliesTo> + <DefaultName>Widget.ui</DefaultName> + <Description>Qt Widget Form File (.ui)</Description> + <TemplateID>QtVsTools-QWidget</TemplateID> + <TemplateGroupID>QtVsTools</TemplateGroupID> + </TemplateData> + <TemplateContent> + <ProjectItem>widget.ui</ProjectItem> + </TemplateContent> +</VSTemplate> diff --git a/Tests/BigSolution/generator/App.config b/Tests/BigSolution/generator/App.config new file mode 100644 index 0000000..ee28567 --- /dev/null +++ b/Tests/BigSolution/generator/App.config @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> + </startup> +</configuration> diff --git a/Tests/BigSolution/generator/Program.cs b/Tests/BigSolution/generator/Program.cs new file mode 100644 index 0000000..6100ac6 --- /dev/null +++ b/Tests/BigSolution/generator/Program.cs @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace generator +{ + internal class Program + { + static void Main(string[] args) + { + var projectCount = args.Length > 0 ? Convert.ToInt32(args[0]) : 400; + var pathToTemplateDir = Path.GetFullPath(@"..\..\..\template"); + var pathToGeneratedDir = Path.GetFullPath(@"..\..\..\generated"); + var templateFiles = Directory.GetFiles( + pathToTemplateDir, "*", SearchOption.AllDirectories); + var solutionFilePath = templateFiles + .Where(x => Path.GetExtension(x) == ".sln") + .First(); + var solutionName = Path.GetFileName(solutionFilePath); + var solutionText = File.ReadAllText(solutionFilePath); + var projectFilePath = templateFiles + .Where(x => Path.GetExtension(x) == ".vcxproj") + .First(); + var projectText = File.ReadAllText(projectFilePath); + var projectName = Path.GetFileNameWithoutExtension(projectFilePath); + var projectGuidMatch = Regex.Match(projectText, @"<ProjectGuid>({[^}]+})<"); + var projectGuid = projectGuidMatch.Groups[1].Value; + + var projectRef = Regex + .Match(solutionText, + @"^Project.*" + projectGuid + @"""\r\nEndProject\r\n", RegexOptions.Multiline) + .Value; + + var projectConfigsList = Regex + .Matches(solutionText, + @"^\s+" + projectGuid + @".*\r\n", RegexOptions.Multiline) + .Cast<Match>() + .Select(x => x.Value); + var projectConfigs = string.Join("", projectConfigsList); + + var projectFiles = Directory.GetFiles( + Path.Combine(pathToTemplateDir, projectName), + "*", SearchOption.TopDirectoryOnly); + + if (Directory.Exists(pathToGeneratedDir)) + Directory.Delete(pathToGeneratedDir, true); + + var genSolutionProjectRef = new StringBuilder(); + var genSolutionProjectConfigs = new StringBuilder(); + + for (int i = 1; i <= projectCount; i++) { + var idxStr = string.Format("{0:D3}", i); + var genProjectName = projectName.Replace("NNN", idxStr); + var genProjectDirPath = Path.Combine(pathToGeneratedDir, genProjectName); + var genProjectGuid = projectGuid.Replace("000", idxStr); + + Directory.CreateDirectory(genProjectDirPath); + + foreach (var projectFile in projectFiles) { + var genProjectFileName = Regex.Replace( + Path.GetFileName(projectFile), "NNN", idxStr, RegexOptions.IgnoreCase); + var genProjectFilePath = Path.Combine(genProjectDirPath, genProjectFileName); + + var genProjectFileText = File.ReadAllText(projectFile); + genProjectFileText = Regex.Replace(genProjectFileText, + "NNN", idxStr, RegexOptions.IgnoreCase); + genProjectFileText = Regex.Replace(genProjectFileText, + projectGuid, genProjectGuid, RegexOptions.IgnoreCase); + File.WriteAllText(genProjectFilePath, genProjectFileText); + } + var genProjectRef = Regex.Replace( + Regex.Replace(projectRef, "NNN", idxStr, RegexOptions.IgnoreCase), + projectGuid, genProjectGuid, RegexOptions.IgnoreCase); + genSolutionProjectRef.Append(genProjectRef); + foreach (var projectConfig in projectConfigsList) { + var genProjectConfig = Regex.Replace(projectConfig, + projectGuid, genProjectGuid, RegexOptions.IgnoreCase); + genSolutionProjectConfigs.Append(genProjectConfig); + } + } + var genSolutionText = solutionText + .Replace(projectRef, genSolutionProjectRef.ToString()) + .Replace(projectConfigs, genSolutionProjectConfigs.ToString()); + var genSolutionFilePath = Path.Combine(pathToGeneratedDir, solutionName); + File.WriteAllText(genSolutionFilePath, genSolutionText); + } + } +} diff --git a/Tests/BigSolution/generator/Properties/AssemblyInfo.cs b/Tests/BigSolution/generator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..42c9c8b --- /dev/null +++ b/Tests/BigSolution/generator/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("generator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("generator")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8c16da39-8049-465a-b242-82dfb91e252a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/BigSolution/generator/generator.csproj b/Tests/BigSolution/generator/generator.csproj new file mode 100644 index 0000000..b1aa89c --- /dev/null +++ b/Tests/BigSolution/generator/generator.csproj @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="$(VisualStudioVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{8C16DA39-8049-465A-B242-82DFB91E252A}</ProjectGuid> + <OutputType>Exe</OutputType> + <RootNamespace>generator</RootNamespace> + <AssemblyName>generator</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <PlatformTarget>AnyCPU</PlatformTarget> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="App.config" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project> diff --git a/Tests/BigSolution/generator/generator.sln b/Tests/BigSolution/generator/generator.sln new file mode 100644 index 0000000..5a4d0f0 --- /dev/null +++ b/Tests/BigSolution/generator/generator.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31717.71 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "generator", "generator.csproj", "{8C16DA39-8049-465A-B242-82DFB91E252A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8C16DA39-8049-465A-B242-82DFB91E252A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C16DA39-8049-465A-B242-82DFB91E252A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C16DA39-8049-465A-B242-82DFB91E252A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C16DA39-8049-465A-B242-82DFB91E252A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C23785F8-8749-42B1-B19E-4A6703DD83BB} + EndGlobalSection +EndGlobal diff --git a/Tests/BigSolution/template/BigProjectNNN/BigClassNNN.cpp b/Tests/BigSolution/template/BigProjectNNN/BigClassNNN.cpp new file mode 100644 index 0000000..971e0e5 --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigClassNNN.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "BigClassNNN.h" + +namespace biglib +{ + template<int N> + struct value + { + static constexpr int get() + { + return 1 + value<N - 1>::get(); + } + }; + + template<> + struct value<0> + { + static constexpr int get() + { + int n = 0; + for (int i = 0; i < 20000; ++i) { + constexpr double x = 42; + n += (int)x; + } + return 0; + } + }; + + template<int X, int Y> + struct add + { + static constexpr int get() + { + return 1 + add<X, Y - 1>::get(); + } + }; + + template <int X> + struct add<X, 0> + { + static constexpr int get() + { + return value<X>::get(); + } + }; + + template<int X, int Y> + struct mult + { + static constexpr int get() + { + return add<value<X>::get(), mult<X, Y - 1>::get()>::get(); + } + }; + + template <int X> + struct mult<X, 0> + { + static constexpr int get() + { + return value<0>::get(); + } + }; + + template<int X, int Y> + struct pow + { + static constexpr int get() + { + return mult<value<X>::get(), pow<X, Y - 1>::get()>::get(); + } + }; + + template <int X> + struct pow<X, 0> + { + static constexpr int get() + { + return value<1>::get(); + } + }; +} + +BigClassNNN::BigClassNNN() +{ +} + +int BigClassNNN::BigMethod() +{ + return biglib::pow<2, 3>::get(); +} diff --git a/Tests/BigSolution/template/BigProjectNNN/BigClassNNN.h b/Tests/BigSolution/template/BigProjectNNN/BigClassNNN.h new file mode 100644 index 0000000..a53cb79 --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigClassNNN.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include "bigprojectnnn_global.h" + +class BIGPROJECTNNN_EXPORT BigClassNNN +{ +public: + BigClassNNN(); + int BigMethod(); +}; diff --git a/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.qrc b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.qrc new file mode 100644 index 0000000..882a3e6 --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.qrc @@ -0,0 +1,4 @@ +<RCC> + <qresource prefix="BigProjectNNN"> + </qresource> +</RCC> diff --git a/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.ui b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.ui new file mode 100644 index 0000000..881b507 --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.ui @@ -0,0 +1,28 @@ +<UI version="4.0" > + <class>BigProjectNNNUi</class> + <widget class="QMainWindow" name="BigProjectNNNUi" > + <property name="objectName" > + <string notr="true">BigProjectNNNUi</string> + </property> + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle" > + <string>BigProject</string> + </property> <widget class="QMenuBar" name="menuBar" /> + <widget class="QToolBar" name="mainToolBar" /> + <widget class="QWidget" name="centralWidget" /> + <widget class="QStatusBar" name="statusBar" /> + </widget> + <layoutDefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <resources> + <include location="BigProjectNNN.qrc"/> + </resources> + <connections/> +</UI> diff --git a/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj new file mode 100644 index 0000000..2ec9d1f --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$(VisualStudioVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{C032BE4B-48F7-465F-BA2A-44962223E000}</ProjectGuid> + <Keyword>QtVS_v304</Keyword> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0.19041.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0.19041.0</WindowsTargetPlatformVersion> + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;gui;widgets</QtModules> + <QtBuildConfig>debug</QtBuildConfig> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;gui;widgets</QtModules> + <QtBuildConfig>release</QtBuildConfig> + </PropertyGroup> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <Optimization>Disabled</Optimization> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <PreprocessorDefinitions>BIGPROJECTNNN_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>None</DebugInformationFormat> + <Optimization>MaxSpeed</Optimization> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <PreprocessorDefinitions>BIGPROJECTNNN_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="bigprojectnnn_global.h" /> + <ClInclude Include="BigClassNNN.h" /> + <ClCompile Include="BigClassNNN.cpp" /> + <QtMoc Include="BigProjectQtClassNNN.h" /> + <ClCompile Include="BigProjectQtClassNNN.cpp" /> + <QtUic Include="BigProjectNNN.ui" /> + <QtRcc Include="BigProjectNNN.qrc" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj.filters b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj.filters new file mode 100644 index 0000000..98e64ca --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigProjectNNN.vcxproj.filters @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="bigprojectnnn_global.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="BigClassNNN.h"> + <Filter>Header Files</Filter> + </ClInclude> + <QtMoc Include="BigProjectQtClassNNN.h"> + <Filter>Header Files</Filter> + </QtMoc> + </ItemGroup> + <ItemGroup> + <ClCompile Include="BigClassNNN.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="BigProjectQtClassNNN.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <QtUic Include="BigProjectNNN.ui"> + <Filter>Source Files</Filter> + </QtUic> + </ItemGroup> + <ItemGroup> + <QtRcc Include="BigProjectNNN.qrc"> + <Filter>Resource Files</Filter> + </QtRcc> + </ItemGroup> +</Project> diff --git a/Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.cpp b/Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.cpp new file mode 100644 index 0000000..8e02739 --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "BigProjectQtClassNNN.h" + +BigProjectQtClassNNN::BigProjectQtClassNNN(QWidget *parent) + : QMainWindow(parent) +{ + ui.setupUi(this); +} diff --git a/Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.h b/Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.h new file mode 100644 index 0000000..66561fb --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/BigProjectQtClassNNN.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <QtWidgets/QMainWindow> +#include "ui_BigProjectNNN.h" + +class BigProjectQtClassNNN : public QMainWindow +{ + Q_OBJECT + +public: + BigProjectQtClassNNN(QWidget *parent = Q_NULLPTR); + +private: + Ui::BigProjectNNNUi ui; +}; diff --git a/Tests/BigSolution/template/BigProjectNNN/bigprojectnnn_global.h b/Tests/BigSolution/template/BigProjectNNN/bigprojectnnn_global.h new file mode 100644 index 0000000..2f7e637 --- /dev/null +++ b/Tests/BigSolution/template/BigProjectNNN/bigprojectnnn_global.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <QtCore/qglobal.h> + +#ifndef BUILD_STATIC +# if defined(BIGPROJECTNNN_LIB) +# define BIGPROJECTNNN_EXPORT Q_DECL_EXPORT +# else +# define BIGPROJECTNNN_EXPORT Q_DECL_IMPORT +# endif +#else +# define BIGPROJECTNNN_EXPORT +#endif diff --git a/Tests/BigSolution/template/BigSolution.sln b/Tests/BigSolution/template/BigSolution.sln new file mode 100644 index 0000000..66a56f1 --- /dev/null +++ b/Tests/BigSolution/template/BigSolution.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.387 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BigProjectNNN", "BigProjectNNN\BigProjectNNN.vcxproj", "{C032BE4B-48F7-465F-BA2A-44962223E000}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C032BE4B-48F7-465F-BA2A-44962223E000}.Debug|x64.ActiveCfg = Debug|x64 + {C032BE4B-48F7-465F-BA2A-44962223E000}.Debug|x64.Build.0 = Debug|x64 + {C032BE4B-48F7-465F-BA2A-44962223E000}.Release|x64.ActiveCfg = Release|x64 + {C032BE4B-48F7-465F-BA2A-44962223E000}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F1E7C58A-3AB2-4EF2-8E8C-C5FA96FCD5F3} + EndGlobalSection +EndGlobal diff --git a/Tests/SampleTest/Macros.qrc b/Tests/SampleTest/Macros.qrc new file mode 100644 index 0000000..3eb08e4 --- /dev/null +++ b/Tests/SampleTest/Macros.qrc @@ -0,0 +1,9 @@ +<RCC> + <qresource prefix="/"> + <file alias="QtVsToolsLoaded">Test_QtVsToolsLoaded.csmacro</file> + <file alias="CreateGuiApp" >Test_CreateGuiApp.csmacro</file> + <file alias="RebuildSolution">Test_RebuildSolution.csmacro</file> + <file alias="DebugGuiApp" >Test_DebugGuiApp.csmacro</file> + <file alias="ImportProFile" >Test_ImportProFile.csmacro</file> + </qresource> +</RCC> diff --git a/Tests/SampleTest/SampleTest.pro b/Tests/SampleTest/SampleTest.pro new file mode 100644 index 0000000..6d5964d --- /dev/null +++ b/Tests/SampleTest/SampleTest.pro @@ -0,0 +1,10 @@ +QT += network testlib + +INCLUDEPATH += $(LOCALAPPDATA)\qtvstest +DEFINES += "\"QT_CONF_PATH=\\\"$$QMAKESPEC/qt.conf\\\"\"" + +RESOURCES += \ + Macros.qrc + +SOURCES += \ + main.cpp diff --git a/Tests/SampleTest/SampleTest.sln b/Tests/SampleTest/SampleTest.sln new file mode 100644 index 0000000..ebeebef --- /dev/null +++ b/Tests/SampleTest/SampleTest.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31229.387 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleTest", "SampleTest.vcxproj", "{12857847-9877-466C-B056-DD286A219093}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {12857847-9877-466C-B056-DD286A219093}.Debug|x64.ActiveCfg = Debug|x64 + {12857847-9877-466C-B056-DD286A219093}.Debug|x64.Build.0 = Debug|x64 + {12857847-9877-466C-B056-DD286A219093}.Release|x64.ActiveCfg = Release|x64 + {12857847-9877-466C-B056-DD286A219093}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0994645B-44BE-41F8-BE79-993936518575} + EndGlobalSection +EndGlobal diff --git a/Tests/SampleTest/SampleTest.vcxproj b/Tests/SampleTest/SampleTest.vcxproj new file mode 100644 index 0000000..b7f418c --- /dev/null +++ b/Tests/SampleTest/SampleTest.vcxproj @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$(VisualStudioVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{12857847-9877-466C-B056-DD286A219093}</ProjectGuid> + <Keyword>QtVS_v303</Keyword> + <ProjectName>SampleTest</ProjectName> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='15.0'">10.0.17763.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='16.0'">10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='17.0'">10.0</WindowsTargetPlatformVersion> + <QtMsBuild Condition="Exists('$(SolutionDir)\qtmsbuild\QtMsBuild')">$(SolutionDir)\qtmsbuild\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='15.0'">v141</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='16.0'">v142</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='17.0'">v143</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir> + <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir> + <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <IncludePath>$(LOCALAPPDATA)\qtvstest;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <IncludePath>$(LOCALAPPDATA)\qtvstest;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;network;testlib</QtModules> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;network;testlib</QtModules> + </PropertyGroup> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"> + <Import Project="$(QtMsBuild)\qt.props" /> + </ImportGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <PreprocessorDefinitions>UNICODE;_UNICODE;WIN32;WIN64;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <AdditionalIncludeDirectories>.\GeneratedFiles\$(ConfigurationName);$(SolutionDir)qtvstest;.;$(QTDIR)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile> + <AdditionalLibraryDirectories>$(QTDIR)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <QtMoc> + </QtMoc> + <QtRcc> + </QtRcc> + <QtMoc> + <AdditionalIncludeDirectories>$(IntDir)\moc\%(RelativeDir)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>QT_CONF_PATH=R"__($(QtToolsPath)\qt.conf)__"</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(IntDir)\moc\%(RelativeDir)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>QT_CONF_PATH="$(QMake_QT_INSTALL_PREFIX_)/mkspecs/$(QMake_QMAKE_SPEC_)/qt.conf"</PreprocessorDefinitions> + <OverrideClCompile>extend</OverrideClCompile> + <AdditionalCppOptions>/Zc:referenceBinding</AdditionalCppOptions> + </QtMoc> + <QtRcc> + <OverrideClCompile>extend</OverrideClCompile> + </QtRcc> + <QtRcc> + <AdditionalCppOptions>/Zc:referenceBinding</AdditionalCppOptions> + </QtRcc> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat /> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile> + <AdditionalLibraryDirectories>$(QTDIR)\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <GenerateDebugInformation>false</GenerateDebugInformation> + </Link> + <QtMoc> + </QtMoc> + <QtRcc> + </QtRcc> + <QtMoc> + <AdditionalIncludeDirectories>$(IntDir)\moc\%(RelativeDir)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>QT_CONF_PATH=R"__($(QtToolsPath)\qt.conf)__"</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(IntDir)\moc\%(RelativeDir)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>QT_CONF_PATH="$(QMake_QT_INSTALL_PREFIX_)/mkspecs/$(QMake_QMAKE_SPEC_)/qt.conf"</PreprocessorDefinitions> + <OverrideClCompile>extend</OverrideClCompile> + <AdditionalCppOptions>/Zc:referenceBinding</AdditionalCppOptions> + </QtMoc> + <QtRcc> + <OverrideClCompile>extend</OverrideClCompile> + </QtRcc> + <QtRcc> + <AdditionalCppOptions>/Zc:referenceBinding</AdditionalCppOptions> + </QtRcc> + </ItemDefinitionGroup> + <ItemGroup> + <QtMoc Include="main.cpp"> + <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource> + <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource> + <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).moc</QtMocFileName> + <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName> + </QtMoc> + </ItemGroup> + <ItemGroup> + <ClInclude Include="$(LocalAppData)\qtvstest\MacroClient.h" /> + </ItemGroup> + <ItemGroup> + <QtRcc Include="Macros.qrc"> + <SubType>Designer</SubType> + </QtRcc> + </ItemGroup> + <ItemGroup> + <None Include="Test_CreateGuiApp.csmacro" /> + <None Include="Test_DebugGuiApp.csmacro" /> + <None Include="Test_QtVsToolsLoaded.csmacro" /> + <None Include="Test_RebuildSolution.csmacro" /> + <None Include="Test_ImportProFile.csmacro" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> + <ProjectExtensions> + <VisualStudio> + <UserProperties /> + </VisualStudio> + </ProjectExtensions> +</Project> \ No newline at end of file diff --git a/Tests/SampleTest/SampleTest.vcxproj.filters b/Tests/SampleTest/SampleTest.vcxproj.filters new file mode 100644 index 0000000..2daaed2 --- /dev/null +++ b/Tests/SampleTest/SampleTest.vcxproj.filters @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier> + <Extensions>qrc;*</Extensions> + <ParseFiles>false</ParseFiles> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier> + <Extensions>qrc;*</Extensions> + <ParseFiles>false</ParseFiles> + </Filter> + <Filter Include="Generated Files"> + <UniqueIdentifier>{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}</UniqueIdentifier> + <Extensions>moc;h;cpp</Extensions> + <SourceControlFiles>False</SourceControlFiles> + </Filter> + </ItemGroup> + <ItemGroup> + <QtMoc Include="main.cpp"> + <Filter>Source Files</Filter> + </QtMoc> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\qtvstest\MacroClient.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <None Include="Test_QtVsToolsLoaded.csmacro"> + <Filter>Resource Files</Filter> + </None> + <None Include="Test_CreateGuiApp.csmacro"> + <Filter>Resource Files</Filter> + </None> + <None Include="Test_RebuildSolution.csmacro"> + <Filter>Resource Files</Filter> + </None> + <None Include="Test_DebugGuiApp.csmacro"> + <Filter>Resource Files</Filter> + </None> + <None Include="Test_ImportProFile.csmacro"> + <Filter>Resource Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <QtRcc Include="Macros.qrc"> + <Filter>Resource Files</Filter> + </QtRcc> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Tests/SampleTest/Test_CreateGuiApp.csmacro b/Tests/SampleTest/Test_CreateGuiApp.csmacro new file mode 100644 index 0000000..2cad562 --- /dev/null +++ b/Tests/SampleTest/Test_CreateGuiApp.csmacro @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +//# using System.IO +//# using System.Threading.Tasks +var solutionName = "QtVsTest"; +var projectName = "MyGuiApp"; +var tempDir = Environment.GetEnvironmentVariable("TEMP"); +var solutionFileName = solutionName + ".sln"; +var solutionDir = Path.Combine(tempDir, solutionName); +var projectDir = Path.Combine(solutionDir, projectName); +if (Directory.Exists(solutionDir)) + Directory.Delete(solutionDir, recursive: true); +Directory.CreateDirectory(projectDir); +var solution = Dte.Solution as Solution2; +solution.Create(tempDir, solutionName); +solution.SaveAs(Path.Combine(solutionDir, solutionFileName)); +var templateGuiApp = solution.GetProjectTemplate("Qt Widgets Application", "VC"); +var taskAddProj = Task.Run(() => solution.AddFromTemplate(templateGuiApp, projectDir, "MyGuiApp")); +//# ui context VSROOT 15000 => "Qt Widgets Application Wizard" +//# ui pattern Invoke => "Next >" +//# ui pattern Invoke => "Next >" +//# ui pattern Invoke => "Finish" +if (!taskAddProj.Wait(15000)) + throw new Exception("Timeout: Solution2.AddFromTemplate"); diff --git a/Tests/SampleTest/Test_DebugGuiApp.csmacro b/Tests/SampleTest/Test_DebugGuiApp.csmacro new file mode 100644 index 0000000..9267c70 --- /dev/null +++ b/Tests/SampleTest/Test_DebugGuiApp.csmacro @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +var solution = Dte.Solution as Solution2; +var solutionBuild = solution.SolutionBuild as SolutionBuild2; +var project = solution.Projects.Cast<Project>().First(); +solutionBuild.Debug(); +//# wait 15000 => Dte.Debugger.CurrentMode == dbgDebugMode.dbgRunMode +//# ui context DESKTOP 15000 => project.Name +//# ui pattern Window mainWindow +mainWindow.Close(); +//# wait 15000 => Dte.Debugger.CurrentMode == dbgDebugMode.dbgDesignMode diff --git a/Tests/SampleTest/Test_ImportProFile.csmacro b/Tests/SampleTest/Test_ImportProFile.csmacro new file mode 100644 index 0000000..18f83ee --- /dev/null +++ b/Tests/SampleTest/Test_ImportProFile.csmacro @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//# using System.IO + +//# var string QtConfPath + +var qtConf = File.ReadAllLines(QtConfPath); +var prefix = qtConf + .Where(x => x.StartsWith("Prefix=")).First() + .Substring("Prefix=".Length); + +var qtDir = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(QtConfPath), prefix)); + +var examplesRelativePath = qtConf + .Where(x => x.StartsWith("Examples=")).First() + .Substring("Examples=".Length); +var examplesPath = Path.GetFullPath(Path.Combine(qtDir, examplesRelativePath)); +var wigglyPath = Path.GetFullPath(Path.Combine(examplesPath, "widgets", "widgets", "wiggly")); + +var projectName = "QtVsTest_Import"; +var tempDir = Environment.GetEnvironmentVariable("LOCALAPPDATA"); +var projectDir = Path.Combine(tempDir, projectName); +var projectFile = Path.Combine(projectDir, "wiggly.pro"); + +if (Directory.Exists(projectDir)) + Directory.Delete(projectDir, recursive: true); +Directory.CreateDirectory(projectDir); + +Directory.EnumerateFiles(wigglyPath) + .Where(x => new[] { ".pro", ".h", ".cpp" }.Contains(Path.GetExtension(x))) + .ToList().ForEach(x => File.Copy(x, Path.Combine(projectDir, Path.GetFileName(x)))); + +var taskOpenPro = Task.Run(() => Dte.ExecuteCommand("QtVSTools.OpenQtProjectFile.pro")); +taskOpenPro.Wait(0); + +//# ui context VSROOT 3000 => "Select a Qt Project to Add to the Solution" +//# ui pattern Value proFilePath => new[] { "File name:", "combo box" }, new[] { "File name:", "edit" } +proFilePath.SetValue(projectFile); +//# ui pattern Invoke => new[] { "Open", "button", "1" } + +if (!taskOpenPro.Wait(15000)) + throw new Exception("Timeout: QtVSTools.OpenQtProjectFile.pro"); diff --git a/Tests/SampleTest/Test_QtVsToolsLoaded.csmacro b/Tests/SampleTest/Test_QtVsToolsLoaded.csmacro new file mode 100644 index 0000000..f1e335c --- /dev/null +++ b/Tests/SampleTest/Test_QtVsToolsLoaded.csmacro @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +/* + * - Ensure Qt VS Tools extension is loaded + * - Wait for Qt VS Tools to finish initialization + */ +//# wait 15000 Assembly QtVsTools => GetAssembly("QtVsTools") +var VsixType = QtVsTools.GetType("QtVsTools.Vsix"); +var VsixInstance = VsixType.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static); +//# wait 15000 => VsixInstance.GetValue(null) diff --git a/Tests/SampleTest/Test_RebuildSolution.csmacro b/Tests/SampleTest/Test_RebuildSolution.csmacro new file mode 100644 index 0000000..7b5a28a --- /dev/null +++ b/Tests/SampleTest/Test_RebuildSolution.csmacro @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//# using System.Threading.Tasks + +var solution = Dte.Solution as Solution2; +var solutionBuild = solution.SolutionBuild as SolutionBuild2; + +var taskRebuild = Task.Run(() => +{ + solutionBuild.Clean(WaitForCleanToFinish: true); + solutionBuild.Build(WaitForBuildToFinish: true); +}); + +bool buildOk = (taskRebuild.Wait(15000) && solutionBuild.LastBuildInfo == 0); +Result = buildOk ? MACRO_OK : MACRO_ERROR_MSG("Build failed."); diff --git a/Tests/SampleTest/main.cpp b/Tests/SampleTest/main.cpp new file mode 100644 index 0000000..400c477 --- /dev/null +++ b/Tests/SampleTest/main.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest> +#include <MacroClient.h> + +class TestSample : public QObject +{ + Q_OBJECT + +private: + MacroClient client; + +private slots: + void initTestCase() + { + qint64 pid = 0; + QVERIFY(client.connect(&pid)); + + client.runMacro(QString() % "//# var QtConfPath => @\"" % QT_CONF_PATH % "\""); + + QFile macroQtVsToolsLoaded(":/QtVsToolsLoaded"); + QCOMPARE(client.runMacro(macroQtVsToolsLoaded), MACRO_OK); + } + + void tutorial01TestCase() + { + QSKIP("tutorial"); + QCOMPARE(client.runMacro(QString() + % "//# using System.Windows.Forms\r\n" + % "MessageBox.Show(\"Hello from Visual Studio!!\");"), + MACRO_OK); + } + + void tutorial02TestCase() + { + QSKIP("tutorial"); + QCOMPARE(client.runMacro(QString() + % "//# using System.Windows.Forms\r\n" + % "var task = Task.Run(() => MessageBox.Show(\"Hello, close this in 15 secs!!\"));\r\n" + % "//# wait 15000 => task.IsCompleted"), + MACRO_OK); + } + + void tutorial03TestCase() + { + QSKIP("tutorial"); + QCOMPARE(client.runMacro( + "Result = Environment.CurrentDirectory.Replace(\"\\\\\", \"/\");"), + QDir::currentPath()); + } + + void tutorial04TestCase() + { + QSKIP("tutorial"); + QCOMPARE(client.runMacro("//# var InitTime => DateTime.Now"), + MACRO_OK); + + QCOMPARE(client.runMacro(QString() + % "//# using System.Windows.Forms\r\n" + % "//# var InitTime\r\n" + % "MessageBox.Show(\"Test started at \" + InitTime);"), + MACRO_OK); + } + + void tutorial05TestCase() + { + QSKIP("tutorial"); + QCOMPARE(client.runMacro(QString() + % "//# using System.Windows.Forms\r\n" + % "Task.Run(() => MessageBox.Show(\"Press OK to close.\", \"Hello\"));\r\n" + % "//# ui context DESKTOP => \"Hello\", \"OK\"\r\n" + % "UiContext.SetFocus();\r\n"), + MACRO_OK); + } + + void guiAppCreate_Rebuild_Debug() + { + client.runMacro("//# wait 5000 => !Dte.Solution.IsOpen"); + QFile macroCreateGuiApp(":/CreateGuiApp"), + macroRebuildSolution(":/RebuildSolution"), + macroDebugGuiApp(":/DebugGuiApp"); + QCOMPARE(client.runMacro(macroCreateGuiApp), MACRO_OK); + QCOMPARE(client.runMacro(macroRebuildSolution), MACRO_OK); + QCOMPARE(client.runMacro(macroDebugGuiApp), MACRO_OK); + client.runMacro( + "Dte.Solution.Close(false);" "\r\n" + "//# wait 15000 => !Dte.Solution.IsOpen" "\r\n"); + } + + void importProFile_Rebuild_Debug() + { + QSKIP("foo"); + QFile macroImportProFile(":/ImportProFile"), + macroRebuildSolution(":/RebuildSolution"), + macroDebugGuiApp(":/DebugGuiApp"); + QCOMPARE(client.runMacro(macroImportProFile), MACRO_OK); + QCOMPARE(client.runMacro(macroRebuildSolution), MACRO_OK); + QCOMPARE(client.runMacro(macroDebugGuiApp), MACRO_OK); + client.runMacro( + "Dte.Solution.Close(false);" "\r\n" + "//# wait 15000 => !Dte.Solution.IsOpen" "\r\n"); + } + + void cleanupTestCase() + { +#ifdef QT_NO_DEBUG + client.runMacro("//#quit"); + client.disconnect(true); +#endif + } +}; + +QTEST_MAIN(TestSample) +#include "main.moc" diff --git a/Tests/Test_QtMsBuild.Tasks/Properties/AssemblyInfo.cs b/Tests/Test_QtMsBuild.Tasks/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d2a7f58 --- /dev/null +++ b/Tests/Test_QtMsBuild.Tasks/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Test_QtMsBuild.Tasks")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Test_QtMsBuild.Tasks")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("e809dde3-ae76-4f7a-8dc5-775ac4900138")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/Test_QtMsBuild.Tasks/TestTaskLoggingHelper.cs b/Tests/Test_QtMsBuild.Tasks/TestTaskLoggingHelper.cs new file mode 100644 index 0000000..b4c1272 --- /dev/null +++ b/Tests/Test_QtMsBuild.Tasks/TestTaskLoggingHelper.cs @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.Build.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Diagnostics; + +namespace QtVsTools.Test.QtMsBuild.Tasks +{ + internal class TestTaskLoggingHelper : QtMSBuild.ITaskLoggingHelper + { + public bool HasLoggedErrors { get; set; } = false; + + public void LogMessage( + string message, + params object[] messageArgs) + { + Debug.WriteLine(message, messageArgs); + } + + public void LogMessage( + MessageImportance importance, + string message, + params object[] messageArgs) + { + Debug.WriteLine(message, messageArgs); + } + + public void LogWarning( + string message, + params object[] messageArgs) + { + Debug.WriteLine("Warning: " + message, messageArgs); + } + + public void LogError( + string message, + params object[] messageArgs) + { + Debug.WriteLine("ERROR: " + message, messageArgs); + } + + public void LogCommandLine(string commandLine) + { + throw new NotImplementedException(); + } + + public void LogCommandLine( + MessageImportance importance, + string commandLine) + { + throw new NotImplementedException(); + } + + public void LogCriticalMessage( + string subcategory, + string code, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogError( + string subcategory, + string errorCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogError( + string subcategory, + string errorCode, + string helpKeyword, + string helpLink, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogErrorFromException(Exception exception) + { + throw new NotImplementedException(); + } + + public void LogErrorFromException( + Exception exception, + bool showStackTrace) + { + throw new NotImplementedException(); + } + + public void LogErrorFromException( + Exception exception, + bool showStackTrace, + bool showDetail, + string file) + { + throw new NotImplementedException(); + } + + public void LogErrorFromResources( + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogErrorFromResources( + string subcategoryResourceName, + string errorCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogErrorWithCodeFromResources( + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogErrorWithCodeFromResources( + string subcategoryResourceName, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogExternalProjectFinished( + string message, + string helpKeyword, + string projectFile, + bool succeeded) + { + throw new NotImplementedException(); + } + + public void LogExternalProjectStarted( + string message, + string helpKeyword, + string projectFile, + string targetNames) + { + throw new NotImplementedException(); + } + + public void LogMessage( + string subcategory, + string code, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + MessageImportance importance, + string message, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogMessageFromResources( + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogMessageFromResources( + MessageImportance importance, + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public bool LogMessageFromText( + string lineOfText, + MessageImportance messageImportance) + { + throw new NotImplementedException(); + } + + public bool LogMessagesFromFile(string fileName) + { + throw new NotImplementedException(); + } + + public bool LogMessagesFromFile( + string fileName, + MessageImportance messageImportance) + { + throw new NotImplementedException(); + } + + public bool LogMessagesFromStream( + TextReader stream, + MessageImportance messageImportance) + { + throw new NotImplementedException(); + } + + public bool LogsMessagesOfImportance(MessageImportance importance) + { + throw new NotImplementedException(); + } + + public void LogTelemetry( + string eventName, + IDictionary<string, + string> properties) + { + throw new NotImplementedException(); + } + + public void LogWarning( + string subcategory, + string warningCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogWarning( + string subcategory, + string warningCode, + string helpKeyword, + string helpLink, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string message, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogWarningFromException(Exception exception) + { + throw new NotImplementedException(); + } + + public void LogWarningFromException( + Exception exception, + bool showStackTrace) + { + throw new NotImplementedException(); + } + + public void LogWarningFromResources( + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogWarningFromResources( + string subcategoryResourceName, + string warningCode, + string helpKeyword, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogWarningWithCodeFromResources( + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + + public void LogWarningWithCodeFromResources( + string subcategoryResourceName, + string file, + int lineNumber, + int columnNumber, + int endLineNumber, + int endColumnNumber, + string messageResourceName, + params object[] messageArgs) + { + throw new NotImplementedException(); + } + } +} diff --git a/Tests/Test_QtMsBuild.Tasks/Test_Join.cs b/Tests/Test_QtMsBuild.Tasks/Test_Join.cs new file mode 100644 index 0000000..0ae7973 --- /dev/null +++ b/Tests/Test_QtMsBuild.Tasks/Test_Join.cs @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; +using System.Collections.Generic; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using QtVsTools.QtMsBuild.Tasks; + +namespace QtVsTools.Test.QtMsBuild.Tasks +{ + [TestClass] + public class Test_Join + { + ITaskItem[] LeftItems = new TaskItem[] + { + new TaskItem("A", new Dictionary<string, string> { + { "X", "foo" }, + { "Y", "42" }, + }), + new TaskItem("B", new Dictionary<string, string> { + { "X", "sna" }, + { "Y", "99" }, + }), + new TaskItem("C", new Dictionary<string, string> { + { "X", "bar" }, + { "Y", "3.14159" }, + }), + }; + + ITaskItem[] RightItems = new TaskItem[] + { + new TaskItem("A", new Dictionary<string, string> { + { "Z", "foo" }, + { "Y", "99" }, + }), + new TaskItem("B", new Dictionary<string, string> { + { "Z", "sna" }, + { "Y", "2.71828" }, + }), + new TaskItem("B", new Dictionary<string, string> { + { "Z", "bar" }, + { "Y", "42" }, + }), + new TaskItem("A", new Dictionary<string, string> { + { "Z", "bar" }, + { "Y", "99" }, + }), + }; + + [TestMethod] + public void Basic() + { + // JOIN ON 'Y' + // + // Left Right --> Result + // --------------- --------------- --------------- + // X | Y Z | Y X | Y | Z + // --------------- --------------- --------------- + // foo | 42 foo | 99 foo | 42 | bar + // sna | 99 sna | 2.71828 sna | 99 | foo + // bar | 3.14159 bar | 42 sna | 99 | bar + // --------------- bar | 99 --------------- + // --------------- + + var criteria = new string[] { "Y" }; + ITaskItem[] result; + Assert.IsTrue( + Join.Execute(LeftItems, RightItems, out result, criteria)); + Assert.IsTrue(result != null && result.Length == 3); + + Assert.IsTrue(result[0].GetMetadata("X") == "foo"); + Assert.IsTrue(result[0].GetMetadata("Y") == "42"); + Assert.IsTrue(result[0].GetMetadata("Z") == "bar"); + + Assert.IsTrue(result[1].GetMetadata("X") == "sna"); + Assert.IsTrue(result[1].GetMetadata("Y") == "99"); + Assert.IsTrue(result[1].GetMetadata("Z") == "foo"); + + Assert.IsTrue(result[2].GetMetadata("X") == "sna"); + Assert.IsTrue(result[2].GetMetadata("Y") == "99"); + Assert.IsTrue(result[2].GetMetadata("Z") == "bar"); + } + + [TestMethod] + public void RowNumber() + { + // JOIN ON 'ROW_NUMBER' + // + // Left Right --> Result + // ------------------- ------------------- ------------------------ + // # | X | Y # | Z | Y # | X | Y | Z + // ------------------- ------------------- ------------------------ + // 0 | foo | 42 0 | foo | 99 0 | foo | 42 | foo + // 1 | sna | 99 1 | sna | 2.71828 1 | sna | 99 | sna + // 2 | bar | 3.14159 2 | bar | 42 2 | bar | 3.14159 | bar + // ------------------- 3 | bar | 99 ------------------------ + // ------------------- + + var criteria = new string[] { "ROW_NUMBER" }; + ITaskItem[] result; + Assert.IsTrue( + Join.Execute(LeftItems, RightItems, out result, criteria)); + Assert.IsTrue(result != null && result.Length == 3); + + Assert.IsTrue(result[0].GetMetadata("X") == "foo"); + Assert.IsTrue(result[0].GetMetadata("Y") == "42"); + Assert.IsTrue(result[0].GetMetadata("Z") == "foo"); + + Assert.IsTrue(result[1].GetMetadata("X") == "sna"); + Assert.IsTrue(result[1].GetMetadata("Y") == "99"); + Assert.IsTrue(result[1].GetMetadata("Z") == "sna"); + + Assert.IsTrue(result[2].GetMetadata("X") == "bar"); + Assert.IsTrue(result[2].GetMetadata("Y") == "3.14159"); + Assert.IsTrue(result[2].GetMetadata("Z") == "bar"); + } + + [TestMethod] + public void Empty() + { + // JOIN ON 'ROW_NUMBER, Y' + // + // Left Right --> Result + // ------------------- ------------------- ----------- + // # | X | Y # | Z | Y (empty) + // ------------------- ------------------- ----------- + // 0 | foo | 42 0 | foo | 99 + // 1 | sna | 99 1 | sna | 2.71828 + // 2 | bar | 3.14159 2 | bar | 42 + // ------------------- 3 | bar | 99 + // ------------------- + + var criteria = new string[] { "ROW_NUMBER", "Y" }; + ITaskItem[] result; + Assert.IsTrue( + Join.Execute(LeftItems, RightItems, out result, criteria)); + Assert.IsTrue(result != null && result.Length == 0); + } + + [TestMethod] + public void MultipleCriteria() + { + // JOIN ON 'ROW_NUMBER, Y' + // + // Left Right --> Result + // ------------------- ------------------- ---------------- + // # | X | Y # | Z | Y X | Y | Z + // ------------------- ------------------- ---------------- + // 0 | foo | 42 0 | foo | 99 zzz | 99 | bar + // 1 | sna | 99 1 | sna | 2.71828 + // 2 | bar | 3.14159 2 | bar | 42 + // 3 | zzz | 99 3 | bar | 99 + // ------------------- ------------------- + + var newLeftItems = LeftItems + .Append(new TaskItem("D", new Dictionary<string, string> { + { "X", "zzz" }, + { "Y", "99" }, + })) + .ToArray(); + + var criteria = new string[] { "ROW_NUMBER", "Y" }; + ITaskItem[] result; + Assert.IsTrue( + Join.Execute(newLeftItems, RightItems, out result, criteria)); + Assert.IsTrue(result != null && result.Length == 1); + + Assert.IsTrue(result[0].GetMetadata("X") == "zzz"); + Assert.IsTrue(result[0].GetMetadata("Y") == "99"); + Assert.IsTrue(result[0].GetMetadata("Z") == "bar"); + } + + [TestMethod] + public void Default() + { + // JOIN ON '' <=> JOIN ON 'Identity' + // + // Left Right --> Result + // --------------------- --------------------- ---------------------- + // Id. | X | Y Id. | Z | Y Id. | X | Y | Z + // --------------------- --------------------- ---------------------- + // A | foo | 42 A | foo | 99 A | foo | 42 | foo + // B | sna | 99 B | sna | 2.71828 A | foo | 42 | bar + // C | bar | 3.14159 B | bar | 42 B | sna | 99 | sna + // --------------------- A | bar | 99 B | bar | 99 | bar + // --------------------- ---------------------- + + ITaskItem[] result; + Assert.IsTrue( + Join.Execute(LeftItems, RightItems, out result)); + Assert.IsTrue(result != null && result.Length == 4); + + Assert.IsTrue(result[0].GetMetadata("X") == "foo"); + Assert.IsTrue(result[0].GetMetadata("Y") == "42"); + Assert.IsTrue(result[0].GetMetadata("Z") == "foo"); + + Assert.IsTrue(result[1].GetMetadata("X") == "foo"); + Assert.IsTrue(result[1].GetMetadata("Y") == "42"); + Assert.IsTrue(result[1].GetMetadata("Z") == "bar"); + + Assert.IsTrue(result[2].GetMetadata("X") == "sna"); + Assert.IsTrue(result[2].GetMetadata("Y") == "99"); + Assert.IsTrue(result[2].GetMetadata("Z") == "sna"); + + Assert.IsTrue(result[3].GetMetadata("X") == "sna"); + Assert.IsTrue(result[3].GetMetadata("Y") == "99"); + Assert.IsTrue(result[3].GetMetadata("Z") == "bar"); + } + } +} diff --git a/Tests/Test_QtMsBuild.Tasks/Test_QtMsBuild.Tasks.csproj b/Tests/Test_QtMsBuild.Tasks/Test_QtMsBuild.Tasks.csproj new file mode 100644 index 0000000..77bbb75 --- /dev/null +++ b/Tests/Test_QtMsBuild.Tasks/Test_QtMsBuild.Tasks.csproj @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ***************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{E809DDE3-AE76-4F7A-8DC5-775AC4900138}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Test_QtMsBuild.Tasks</RootNamespace> + <AssemblyName>Test_QtMsBuild.Tasks</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> + <IsCodedUITest>False</IsCodedUITest> + <TestProjectType>UnitTest</TestProjectType> + <NuGetPackageImportStamp> + </NuGetPackageImportStamp> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="Microsoft.Build" Version="$(Version_Microsoft_Build)" /> + <PackageReference Include="Microsoft.Build.Framework" Version="$(Version_Microsoft_Build_Framework)" /> + <PackageReference Include="Microsoft.Build.Tasks.Core" Version="$(Version_Microsoft_Build_Tasks_Core)" /> + <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="$(Version_Microsoft_Bcl_AsyncInterfaces)" /> + <PackageReference Include="MSTest.TestAdapter" Version="$(Version_MSTest_TestAdapter)" /> + <PackageReference Include="MSTest.TestFramework" Version="$(Version_MSTest_TestFramework)" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="$(SolutionDir)\QtMSBuild\QtMSBuild.csproj"> + <Project>{a618d28b-9352-44f4-aa71-609bf68bf871}</Project> + <Name>QtMSBuild</Name> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="TestTaskLoggingHelper.cs" /> + <Compile Include="Test_Join.cs" /> + </ItemGroup> + <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> \ No newline at end of file diff --git a/Tests/Test_QtVsTools.PriorityQueue/Properties/AssemblyInfo.cs b/Tests/Test_QtVsTools.PriorityQueue/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8561057 --- /dev/null +++ b/Tests/Test_QtVsTools.PriorityQueue/Properties/AssemblyInfo.cs @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Test_QtVsTools.PriorityQueue")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Test_QtVsTools.PriorityQueue")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("a5320606-37b8-4f15-97e2-16314109caf9")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/Test_QtVsTools.PriorityQueue/Test_PriorityQueue.cs b/Tests/Test_QtVsTools.PriorityQueue/Test_PriorityQueue.cs new file mode 100644 index 0000000..b5a2a4b --- /dev/null +++ b/Tests/Test_QtVsTools.PriorityQueue/Test_PriorityQueue.cs @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace QtVsTools.Test.PriorityQueue +{ + [TestClass] + public class Test_PriorityQueue + { + [TestMethod] + public void TestEnqueueWithPriority() + { + var q = new PriorityQueue<string, int>(); + q.Enqueue("c", 30); + q.Enqueue("a", 13); + q.Enqueue("d", 47); + q.Enqueue("b", 28); + Assert.IsTrue(string.Join("", q) == "abcd"); + } + + [TestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public void TestEnqueueWithSamePriority() + { + var q = new PriorityQueue<string, int>(); + q.Enqueue("a", 1); + q.Enqueue("a", 1); + Assert.IsTrue(string.Join("", q) == "a"); + q.Enqueue("b", 1); + } + + [TestMethod] + public void TestEnqueueContains() + { + var q = new PunisherQueue<string>(); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + Assert.IsTrue(q.Contains("a")); + Assert.IsTrue(q.Contains("b")); + Assert.IsTrue(q.Contains("c")); + Assert.IsTrue(string.Join("", q) == "abc"); + } + + [TestMethod] + public void TestEnqueueTwice() + { + var q = new PunisherQueue<string>(); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("a"); + q.Enqueue("c"); + q.Enqueue("b"); + Assert.IsTrue(string.Join("", q) == "acb"); + } + + [TestMethod] + public void TestTryPeek() + { + var q = new PunisherQueue<string>(); + string s; + Assert.IsTrue(!q.TryPeek(out s)); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + Assert.IsTrue(q.TryPeek(out s) && s == "a"); + Assert.IsTrue(string.Join("", q) == "abc"); + } + + [TestMethod] + public void TestPeek() + { + var q = new PunisherQueue<string>(); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + Assert.IsTrue(q.Peek() == "a"); + Assert.IsTrue(string.Join("", q) == "abc"); + } + + [TestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public void TestPeekEmpty() + { + var q = new PunisherQueue<string>(); + q.Peek(); + } + + [TestMethod] + public void TestTryDequeue() + { + var q = new PunisherQueue<string>(); + string s; + Assert.IsTrue(!q.TryDequeue(out s)); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + Assert.IsTrue(q.TryDequeue(out s) && s == "a"); + Assert.IsTrue(string.Join("", q) == "bc"); + } + + [TestMethod] + public void TestDequeue() + { + var q = new PunisherQueue<string>(); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + Assert.IsTrue(q.Dequeue() == "a"); + Assert.IsTrue(string.Join("", q) == "bc"); + } + + [TestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public void TestDequeueEmpty() + { + var q = new PunisherQueue<string>(); + q.Dequeue(); + } + + [TestMethod] + public void TestClear() + { + var q = new PunisherQueue<string>(); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + q.Clear(); + q.Enqueue("x"); + q.Enqueue("y"); + q.Enqueue("z"); + Assert.IsTrue(string.Join("", q) == "xyz"); + } + + [TestMethod] + public void TestConcurrency() + { + var q = new PunisherQueue<string>(); + int n = 0; + Task.Run(() => + { + for (int i = 0; i < 10000; ++i) { + q.Enqueue(Path.GetRandomFileName()); + ++n; + Thread.Yield(); + } + }); + for (int i = 0; i < 10000; ++i) { + string s; + if (!q.TryDequeue(out s)) + --i; + --n; + Thread.Yield(); + } + if (n == 0) + Assert.Inconclusive(); + Assert.IsTrue(q.Count() == 0); + } + + [TestMethod] + public void TestGetItemKey() + { + var q = new PunisherQueue<string>(item => + { + switch (item) { + case "a": + case "x": + return "ax"; + case "b": + case "y": + return "by"; + case "c": + case "z": + return "cz"; + default: + return item; + } + }); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + q.Enqueue("x"); + q.Enqueue("z"); + q.Enqueue("w"); + Assert.IsTrue(string.Join("", q) == "bxzw"); + } + + [TestMethod] + public void TestRemove() + { + var q = new PunisherQueue<string>(); + q.Enqueue("a"); + q.Enqueue("b"); + q.Enqueue("c"); + q.Remove("b"); + Assert.IsTrue(string.Join("", q) == "ac"); + } + } +} diff --git a/Tests/Test_QtVsTools.PriorityQueue/Test_QtVsTools.PriorityQueue.csproj b/Tests/Test_QtVsTools.PriorityQueue/Test_QtVsTools.PriorityQueue.csproj new file mode 100644 index 0000000..7cc709b --- /dev/null +++ b/Tests/Test_QtVsTools.PriorityQueue/Test_QtVsTools.PriorityQueue.csproj @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ***************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{A5320606-37B8-4F15-97E2-16314109CAF9}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Test_QtVsTools.PriorityQueue</RootNamespace> + <AssemblyName>Test_QtVsTools.PriorityQueue</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> + <IsCodedUITest>False</IsCodedUITest> + <TestProjectType>UnitTest</TestProjectType> + <NuGetPackageImportStamp> + </NuGetPackageImportStamp> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="MSTest.TestAdapter" Version="$(Version_MSTest_TestAdapter)" /> + <PackageReference Include="MSTest.TestFramework" Version="$(Version_MSTest_TestFramework)" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="$(SolutionDir)\QtVsTools.Package\QtVsTools.Package.csproj"> + <Project>{671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}</Project> + <Name>QtVsTools.Package</Name> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <Compile Include="Test_PriorityQueue.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> \ No newline at end of file diff --git a/Tests/Test_QtVsTools.RegExpr/Properties/AssemblyInfo.cs b/Tests/Test_QtVsTools.RegExpr/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..68ec694 --- /dev/null +++ b/Tests/Test_QtVsTools.RegExpr/Properties/AssemblyInfo.cs @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Test_QtVsTools.RegExpr")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Test_QtVsTools.RegExpr")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("d574efed-5e19-45be-9b05-310f65065303")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/Test_QtVsTools.RegExpr/Test_MacroParser.cs b/Tests/Test_QtVsTools.RegExpr/Test_MacroParser.cs new file mode 100644 index 0000000..5f29dc4 --- /dev/null +++ b/Tests/Test_QtVsTools.RegExpr/Test_MacroParser.cs @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace QtVsTools.Test.RegExpr +{ + using QtVsTest.Macros; + + [TestClass] + public class Test_MacroParser + { + MacroParser Parser = MacroParser.Get(); + + [TestMethod] + public void TestMacro() + { + string testInput = + "//# wait 15000 Assembly QtVsTools => GetAssembly(\"QtVsTools\")" + "\r\n" + + "var VsixType = QtVsTools.GetType(\"QtVsTools.Vsix\");" + "\r\n" + + "var VsixInstance = VsixType.GetProperty(\"Instance\"," + "\r\n" + + " BindingFlags.Public | BindingFlags.Static);" + "\r\n" + + "//# wait 15000 object Vsix => VsixInstance.GetValue(null)" + "\r\n" + + "Result = \"(ok)\";"; + + var macroLines = Parser.Parse(testInput); + + Debug.Assert(macroLines != null); + Debug.Assert(macroLines.Count() == 6); + Debug.Assert(macroLines.Skip(0).First() is Statement); + Debug.Assert(macroLines.Skip(1).First() is CodeLine); + Debug.Assert(macroLines.Skip(2).First() is CodeLine); + Debug.Assert(macroLines.Skip(3).First() is CodeLine); + Debug.Assert(macroLines.Skip(4).First() is Statement); + Debug.Assert(macroLines.Skip(5).First() is CodeLine); + } + } +} diff --git a/Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj b/Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj new file mode 100644 index 0000000..5622e2d --- /dev/null +++ b/Tests/Test_QtVsTools.RegExpr/Test_QtVsTools.RegExpr.csproj @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="$(VisualStudioVersion)" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ***************************************************************************** + ** + ** Copyright (C) 2019 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the Qt VS Tools. + ** + ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ + ** 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. + ** + ** $QT_END_LICENSE$ + ** + ***************************************************************************** +--> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{D574EFED-5E19-45BE-9B05-310F65065303}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Test_QtVsTools.RegExpr</RootNamespace> + <AssemblyName>Test_QtVsTools.RegExpr</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion> + <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> + <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> + <IsCodedUITest>False</IsCodedUITest> + <TestProjectType>UnitTest</TestProjectType> + <NuGetPackageImportStamp> + </NuGetPackageImportStamp> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Global references + // --> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Version specific references + // --> + <Import Project="$(SolutionDir)\references.props" /> + <ItemGroup> + <PackageReference Include="MSTest.TestAdapter" Version="$(Version_MSTest_TestAdapter)" /> + <PackageReference Include="MSTest.TestFramework" Version="$(Version_MSTest_TestFramework)" /> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Solution project references + // --> + <ItemGroup> + <ProjectReference Include="$(SolutionDir)\QtVsTools.RegExpr\QtVsTools.RegExpr.csproj"> + <Project>{a2831b9b-4d3b-46cb-85df-1b5c277c17db}</Project> + <Name>QtVsTools.RegExpr</Name> + </ProjectReference> + </ItemGroup> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Project items + // --> + <ItemGroup> + <Compile Include="$(SolutionDir)\QtVsTest\MacroParser.cs"> + <Link>MacroParser.cs</Link> + </Compile> + <Compile Include="Test_MacroParser.cs" /> + <Compile Include="Test_XmlIntParser.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Test_SubTokens.cs" /> + </ItemGroup> + <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\transform.targets" /> +</Project> \ No newline at end of file diff --git a/Tests/Test_QtVsTools.RegExpr/Test_SubTokens.cs b/Tests/Test_QtVsTools.RegExpr/Test_SubTokens.cs new file mode 100644 index 0000000..8118ea7 --- /dev/null +++ b/Tests/Test_QtVsTools.RegExpr/Test_SubTokens.cs @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace QtVsTools.Test.RegExpr +{ + using static SyntaxAnalysis.RegExpr; + + [TestClass] + public class Test_SubTokens + { + [TestMethod] + public void TestManyToMany() + { + var tokenA = new Token("A", "a"); + var tokenB = new Token("B", "b" & tokenA); + var tokenC = new Token("C", "c" & tokenB); + var tokenX = new Token("X", (tokenA | tokenB | tokenC).Repeat()); + var parser = tokenX.Render(); + parser.Parse("abacba"); + } + + [TestMethod] + public void TestLookAhead() + { + var tokenA = new Token("A", "a"); + var tokenB = new Token("B", "b" & !LookAhead[tokenA] & AnyChar); + var tokenX = new Token("X", (tokenA | tokenB).Repeat()); + var parser = tokenX.Render(); + parser.Parse("abc"); + } + } +} diff --git a/Tests/Test_QtVsTools.RegExpr/Test_XmlIntParser.cs b/Tests/Test_QtVsTools.RegExpr/Test_XmlIntParser.cs new file mode 100644 index 0000000..bca023e --- /dev/null +++ b/Tests/Test_QtVsTools.RegExpr/Test_XmlIntParser.cs @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +using System; +using System.Diagnostics; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace QtVsTools.Test.RegExpr +{ + using static SyntaxAnalysis.RegExpr; + + [TestClass] + public class Test_XmlIntParser + { + public const string IdNum = "NUM"; + public const string IdExpr = "EXPR"; + public const string IdExprLPar = "EXPR_LPAR"; + public const string IdTagValue = "TAG_VALUE"; + public const string IdTagBegin = "TAG_BEGIN"; + public const string IdTagName = "TAG_NAME"; + public const string IdTag = "TAG"; + + public static Parser GetParser() + { + // XML chars + var charLt = Char['<']; + var charGt = Char['>']; + var charSlash = Char['/']; + + // int expr chars + var charPlus = Char['+']; + var charMinus = Char['-']; + var charMult = Char['*']; + var charDiv = Char['/']; + var charLPar = Char['(']; + var charRPar = Char[')']; + var charDigit = Char['0', '9']; + var charAddtOper = CharSet[charPlus + charMinus]; + var charMultOper = CharSet[charMult + charDiv]; + var charOper = CharSet[charAddtOper + charMultOper]; + + // int operator priorities + int priorityInfixAddt = 10; + int priorityInfixMult = 20; + int priorityPrefixAddt = 30; + + // token: number + var exprNum = new Token(IdNum, + charDigit.Repeat() & !LookAhead[SkipWs & (charDigit | charLPar)]) + { + new Rule<int> + { + Capture(value => int.Parse(value)) + } + }; + + // token: plus (infix or prefix) + var exprPlus = new Token(IdExpr, + charPlus & !LookAhead[SkipWs & (charOper | charRPar | charLt)]) + { + new PrefixRule<int, int>( + priority: priorityPrefixAddt, + select: t => (t.IsFirst || t.LookBehind().First().Is(IdExprLPar)) + && t.LookAhead().First().Is(IdNum, IdExprLPar)) + { + Create((int x) => +x) + }, + + new InfixRule<int, int, int>(priority: priorityInfixAddt) + { + Create((int x, int y) => x + y) + } + }; + + // token: minus (infix or prefix) + var exprMinus = new Token(IdExpr, + charMinus & !LookAhead[SkipWs & (charOper | charRPar | charLt)]) + { + new PrefixRule<int, int>( + priority: priorityPrefixAddt, + select: t => (t.IsFirst || t.LookBehind().First().Is(IdExprLPar)) + && t.LookAhead().First().Is(IdNum, IdExprLPar)) + { + Create((int x) => -x) + }, + + new InfixRule<int, int, int>(priority: priorityInfixAddt) + { + Create((int x, int y) => x - y) + } + }; + + // token: multiplication + var exprMult = new Token(IdExpr, + charMult & !LookAhead[SkipWs & (charOper | charRPar | charLt)]) + { + new InfixRule<int, int, int>(priority: priorityInfixMult) + { + Create((int x, int y) => x * y) + } + }; + + // token: division + var exprDiv = new Token(IdExpr, + charDiv & !LookAhead[SkipWs & (charOper | charRPar | charLt)]) + { + new InfixRule<int, int, int>(priority: priorityInfixMult) + { + Create((int x, int y) => x / y) + } + }; + + // token: left parenthesis + var exprLPar = new Token(IdExprLPar, + charLPar & !LookAhead[SkipWs & (charRPar | charLt)]) + { + new LeftDelimiterRule<string>() + { + Capture(value => value) + } + }; + + // token: right parenthesis + var exprRPar = new Token(IdExpr, + charRPar & !LookAhead[SkipWs & (charDigit | charLPar)]) + { + new RightDelimiterRule<string, int, int> + { + Create((string lPar, int n) => n) + } + }; + + // int expression + var numExpr = (exprNum + | exprPlus + | exprMinus + | exprMult + | exprDiv + | exprLPar + | exprRPar).Repeat(); + + // token: tag value containing int expression + var tagValue = new Token(IdTagValue, SkipWs_Disable, + LookAhead[SkipWs & CharSet[~(CharSpace + charLt)]] & numExpr & LookAhead[charLt]) + { + new Rule<string> + { + Create(IdNum, (int expr) => "=" + expr.ToString()), + Create(IdExpr, (int expr) => "=" + expr.ToString()) + } + }; + + // token: tag open (only tag name, no attribs) + var tagBegin = new Token(IdTagBegin, + charLt & new Token(IdTagName, CharWord.Repeat()) & charGt) + { + new LeftDelimiterRule<string> + { + Create(IdTagName, (string tagName) => tagName) + } + }; + + // token: tag close + var tagEnd = new Token(IdTag, + charLt & charSlash & new Token(IdTagName, CharWord.Repeat()) & LookAhead[charGt]) + { + new RightDelimiterRule<string, string, string> + { + Create(IdTagName, (string name) => name), + Error( + (string tag, string tagName) => tagName != tag, + (tag, tagName) => string.Format("Expected {0}, found {1}", tagName, tag)), + Create( + (string tag, string value) => value.StartsWith("="), + (tag, value) => tag + value), + Create( + (string tag, string value) => !value.StartsWith("="), + (tag, value) => tag + ":{" + value + "}") + } + }; + + // token: tag sequence + var tagConcat = new Token(IdTag, charGt & LookAhead[SkipWs & charLt & ~charSlash]) + { + new InfixRule<string, string, string>( + pre: t => t.LeftOperand.Is(IdTag) && t.RightOperand.Is(IdTag)) + { + Create((string leftTag, string rightTag) => leftTag + "," + rightTag) + } + }; + + // XML containing int expressions + var xmlInt = StartOfLine + & (tagBegin | tagValue | tagEnd & (tagConcat | charGt)).Repeat() + & SkipWs & EndOfFile; + + // generate RegExpr parser + return xmlInt.Render(CharSpace.Repeat()); + } + + Parser Parser = GetParser(); + + [TestMethod] + public void TestConst() + { + string testInput = "<x>42</x>"; + string testOutput = "x=42"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + + [TestMethod] + [ExpectedException(typeof(ParseErrorException))] + public void TestConstError() + { + string testInput = "<x>foo</x>"; + Parser.Parse(testInput); + } + + [TestMethod] + public void TestInfix() + { + string testInput = "<x>2 - 1</x>"; + string testOutput = "x=1"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + + [TestMethod] + [ExpectedException(typeof(ParseErrorException))] + public void TestInfixError() + { + string testInput = "<x>2 - </x>"; + Parser.Parse(testInput); + } + + [TestMethod] + public void TestPrefix() + { + string testInput = "<x>-2 + 1</x>"; + string testOutput = "x=-1"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + [TestMethod] + [ExpectedException(typeof(ParseErrorException))] + public void TestPrefixError() + { + string testInput = "<x>- + 1</x>"; + Parser.Parse(testInput); + } + + [TestMethod] + public void TestPrecedence() + { + string testInput = "<x>2 + 3 * 4</x>"; + string testOutput = "x=14"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + + [TestMethod] + public void TestParentheses() + { + string testInput = "<x>(2 + 3) * 4</x>"; + string testOutput = "x=20"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + + [TestMethod] + [ExpectedException(typeof(ParseErrorException))] + public void TestParenthesesLeftError() + { + string testInput = "<x>2 + 3) * 4</x>"; + Parser.Parse(testInput); + } + + [TestMethod] + [ExpectedException(typeof(ParseErrorException))] + public void TestParenthesesRightError() + { + string testInput = "<x>(2 + 3 * 4</x>"; + Parser.Parse(testInput); + } + + [TestMethod] + public void TestParenthesesNested() + { + string testInput = "<x>(-((2 + 3) * 4) / 5) * 3</x>"; + string testOutput = "x=-12"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + + [TestMethod] + public void TestNestedTags() + { + string testInput = "<a><x>(-((2 + 3) * 4) / 5) * 3</x><y>(2 + 3) * 4</y></a>"; + string testOutput = "a:{x=-12,y=20}"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + + [TestMethod] + [ExpectedException(typeof(ParseErrorException))] + public void TestNestedTagsError() + { + string testInput = "<a><x>1</x><y>2<z><w>"; + Parser.Parse(testInput); + } + + [TestMethod] + public void TestMultiLines() + { + string testInput = + "<a>" + "\r\n" + + " <x>2 + 3 * 4</x>" + "\r\n" + + " <y>(2 + 3) * 4</y>" + "\r\n" + + "</a>"; + string testOutput = "a:{x=14,y=20}"; + Debug.Assert(Parser.Parse(testInput).GetValues<string>(IdTag).First() == testOutput); + } + } +} diff --git a/Tests/concurrency/Solution1/MyPropertySheet.props b/Tests/concurrency/Solution1/MyPropertySheet.props new file mode 100644 index 0000000..c25c952 --- /dev/null +++ b/Tests/concurrency/Solution1/MyPropertySheet.props @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ImportGroup Label="PropertySheets" /> + <PropertyGroup Label="UserMacros"> + </PropertyGroup> + <PropertyGroup /> + <ItemDefinitionGroup /> + <ItemGroup> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/QtClassLibrary1/QtClass.cpp b/Tests/concurrency/Solution1/QtClassLibrary1/QtClass.cpp new file mode 100644 index 0000000..bc362ce --- /dev/null +++ b/Tests/concurrency/Solution1/QtClassLibrary1/QtClass.cpp @@ -0,0 +1,10 @@ +#include "QtClass.h" + +QtClass::QtClass(QObject *parent) + : QObject(parent) +{ +} + +QtClass::~QtClass() +{ +} diff --git a/Tests/concurrency/Solution1/QtClassLibrary1/QtClass.h b/Tests/concurrency/Solution1/QtClassLibrary1/QtClass.h new file mode 100644 index 0000000..9ee643f --- /dev/null +++ b/Tests/concurrency/Solution1/QtClassLibrary1/QtClass.h @@ -0,0 +1,13 @@ +#pragma once +#include "qtclasslibrary1_global.h" + +#include <QObject> + +class QTCLASSLIBRARY1_EXPORT QtClass : public QObject +{ + Q_OBJECT + +public: + QtClass(QObject *parent); + ~QtClass(); +}; diff --git a/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.cpp b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.cpp new file mode 100644 index 0000000..f661126 --- /dev/null +++ b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.cpp @@ -0,0 +1,5 @@ +#include "QtClassLibrary1.h" + +QtClassLibrary1::QtClassLibrary1() +{ +} diff --git a/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.h b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.h new file mode 100644 index 0000000..8fb5dc9 --- /dev/null +++ b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.h @@ -0,0 +1,9 @@ +#pragma once + +#include "qtclasslibrary1_global.h" + +class QTCLASSLIBRARY1_EXPORT QtClassLibrary1 +{ +public: + QtClassLibrary1(); +}; diff --git a/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj new file mode 100644 index 0000000..4ec4a0c --- /dev/null +++ b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$(VisualStudioVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{CE78EC51-C4A0-465B-A161-21C257BD057B}</ProjectGuid> + <Keyword>QtVS_v304</Keyword> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0</WindowsTargetPlatformVersion> + <QtMsBuild Condition="Exists('$(MSBuildProjectDirectory)\..\..\..\..\qtmsbuild')" + >$([System.IO.Path]::GetFullPath( + $(MSBuildProjectDirectory)\..\..\..\..\qtmsbuild\QtMsBuild))</QtMsBuild> + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core</QtModules> + <QtBuildConfig>debug</QtBuildConfig> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core</QtModules> + <QtBuildConfig>release</QtBuildConfig> + </PropertyGroup> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\MyPropertySheet.props" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\MyPropertySheet.props" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <AllProjectIncludesArePublic>true</AllProjectIncludesArePublic> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <AllProjectIncludesArePublic>true</AllProjectIncludesArePublic> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ProjectReference> + <UseLibraryDependencyInputs>true</UseLibraryDependencyInputs> + </ProjectReference> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ProjectReference> + <UseLibraryDependencyInputs>true</UseLibraryDependencyInputs> + </ProjectReference> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <Optimization>Disabled</Optimization> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <PreprocessorDefinitions>QTCLASSLIBRARY1_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>None</DebugInformationFormat> + <Optimization>MaxSpeed</Optimization> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <PreprocessorDefinitions>QTCLASSLIBRARY1_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <QtMoc Include="QtClass.h" /> + <ClInclude Include="qtclasslibrary1_global.h" /> + <ClInclude Include="QtClassLibrary1.h" /> + <ClCompile Include="QtClass.cpp" /> + <ClCompile Include="QtClassLibrary1.cpp" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj.filters b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj.filters new file mode 100644 index 0000000..da71435 --- /dev/null +++ b/Tests/concurrency/Solution1/QtClassLibrary1/QtClassLibrary1.vcxproj.filters @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClInclude Include="qtclasslibrary1_global.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClCompile Include="QtClassLibrary1.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClInclude Include="QtClassLibrary1.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <QtMoc Include="QtClass.h"> + <Filter>Header Files</Filter> + </QtMoc> + </ItemGroup> + <ItemGroup> + <ClCompile Include="QtClass.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/QtClassLibrary1/qtclasslibrary1_global.h b/Tests/concurrency/Solution1/QtClassLibrary1/qtclasslibrary1_global.h new file mode 100644 index 0000000..4a2e97c --- /dev/null +++ b/Tests/concurrency/Solution1/QtClassLibrary1/qtclasslibrary1_global.h @@ -0,0 +1,13 @@ +#pragma once + +#include <QtCore/qglobal.h> + +#ifndef BUILD_STATIC +# if defined(QTCLASSLIBRARY1_LIB) +# define QTCLASSLIBRARY1_EXPORT Q_DECL_EXPORT +# else +# define QTCLASSLIBRARY1_EXPORT Q_DECL_IMPORT +# endif +#else +# define QTCLASSLIBRARY1_EXPORT +#endif diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.cpp b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.cpp new file mode 100644 index 0000000..40899a6 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.cpp @@ -0,0 +1,11 @@ +#include "QtWidgetsApplication1.h" +#include "QtClass.h" +#include "Header.h" + +QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent) + : QMainWindow(parent) +{ + ui.setupUi(this); + qtObject = new QtClass(this); + foobar(); +} diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.h b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.h new file mode 100644 index 0000000..537966d --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.h @@ -0,0 +1,18 @@ +#pragma once + +#include <QtWidgets/QMainWindow> +#include "ui_QtWidgetsApplication1.h" + +class QtClass; + +class QtWidgetsApplication1 : public QMainWindow +{ + Q_OBJECT + +public: + QtWidgetsApplication1(QWidget *parent = Q_NULLPTR); + +private: + Ui::QtWidgetsApplication1Class ui; + QtClass *qtObject; +}; diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.qrc b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.qrc new file mode 100644 index 0000000..3fde6c3 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.qrc @@ -0,0 +1,4 @@ +<RCC> + <qresource prefix="QtWidgetsApplication1"> + </qresource> +</RCC> diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.ui b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.ui new file mode 100644 index 0000000..03a2f0f --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.ui @@ -0,0 +1,28 @@ +<UI version="4.0" > + <class>QtWidgetsApplication1Class</class> + <widget class="QMainWindow" name="QtWidgetsApplication1Class" > + <property name="objectName" > + <string notr="true">QtWidgetsApplication1Class</string> + </property> + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle" > + <string>QtWidgetsApplication1</string> + </property> <widget class="QMenuBar" name="menuBar" /> + <widget class="QToolBar" name="mainToolBar" /> + <widget class="QWidget" name="centralWidget" /> + <widget class="QStatusBar" name="statusBar" /> + </widget> + <layoutDefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <resources> + <include location="QtWidgetsApplication1.qrc"/> + </resources> + <connections/> +</UI> diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj new file mode 100644 index 0000000..b00db6e --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$(VisualStudioVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{806AF248-25F9-4300-9FB3-100E5F79C026}</ProjectGuid> + <Keyword>QtVS_v304</Keyword> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0</WindowsTargetPlatformVersion> + <QtMsBuild Condition="Exists('$(MSBuildProjectDirectory)\..\..\..\..\qtmsbuild')" + >$([System.IO.Path]::GetFullPath( + $(MSBuildProjectDirectory)\..\..\..\..\qtmsbuild\QtMsBuild))</QtMsBuild> + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;gui;widgets</QtModules> + <QtBuildConfig>debug</QtBuildConfig> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;gui;widgets</QtModules> + <QtBuildConfig>release</QtBuildConfig> + </PropertyGroup> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\MyPropertySheet.props" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\MyPropertySheet.props" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <CopyLocalProjectReference>true</CopyLocalProjectReference> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <CopyLocalProjectReference>true</CopyLocalProjectReference> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <Optimization>Disabled</Optimization> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>None</DebugInformationFormat> + <Optimization>MaxSpeed</Optimization> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <QtRcc Include="QtWidgetsApplication1.qrc" /> + <QtUic Include="QtWidgetsApplication1.ui" /> + <QtMoc Include="QtWidgetsApplication1.h" /> + <ClCompile Include="QtWidgetsApplication1.cpp" /> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\QtClassLibrary1\QtClassLibrary1.vcxproj"> + <Project>{ce78ec51-c4a0-465b-a161-21c257bd057b}</Project> + </ProjectReference> + <ProjectReference Include="..\StaticLib1\StaticLib1.vcxproj"> + <Project>{8463051a-b32c-43f0-9a77-9f223598aac9}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj.filters b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj.filters new file mode 100644 index 0000000..3f13844 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication1/QtWidgetsApplication1.vcxproj.filters @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <QtRcc Include="QtWidgetsApplication1.qrc"> + <Filter>Resource Files</Filter> + </QtRcc> + <QtUic Include="QtWidgetsApplication1.ui"> + <Filter>Form Files</Filter> + </QtUic> + <QtMoc Include="QtWidgetsApplication1.h"> + <Filter>Header Files</Filter> + </QtMoc> + <ClCompile Include="QtWidgetsApplication1.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication1/main.cpp b/Tests/concurrency/Solution1/QtWidgetsApplication1/main.cpp new file mode 100644 index 0000000..e1a4cb3 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication1/main.cpp @@ -0,0 +1,10 @@ +#include "QtWidgetsApplication1.h" +#include <QtWidgets/QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QtWidgetsApplication1 w; + w.show(); + return a.exec(); +} diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.cpp b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.cpp new file mode 100644 index 0000000..1b5c4de --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.cpp @@ -0,0 +1,9 @@ +#include "QtWidgetsApplication2.h" +#include "QtClass.h" + +QtWidgetsApplication2::QtWidgetsApplication2(QWidget *parent) + : QMainWindow(parent) +{ + ui.setupUi(this); + qtObject = new QtClass(this); +} diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.h b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.h new file mode 100644 index 0000000..dad1a28 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.h @@ -0,0 +1,18 @@ +#pragma once + +#include <QtWidgets/QMainWindow> +#include "ui_QtWidgetsApplication2.h" + +class QtClass; + +class QtWidgetsApplication2 : public QMainWindow +{ + Q_OBJECT + +public: + QtWidgetsApplication2(QWidget *parent = Q_NULLPTR); + +private: + Ui::QtWidgetsApplication2Class ui; + QtClass* qtObject; +}; diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.qrc b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.qrc new file mode 100644 index 0000000..83bec21 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.qrc @@ -0,0 +1,4 @@ +<RCC> + <qresource prefix="QtWidgetsApplication2"> + </qresource> +</RCC> diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.ui b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.ui new file mode 100644 index 0000000..543b877 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.ui @@ -0,0 +1,28 @@ +<UI version="4.0" > + <class>QtWidgetsApplication2Class</class> + <widget class="QMainWindow" name="QtWidgetsApplication2Class" > + <property name="objectName" > + <string notr="true">QtWidgetsApplication2Class</string> + </property> + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle" > + <string>QtWidgetsApplication2</string> + </property> <widget class="QMenuBar" name="menuBar" /> + <widget class="QToolBar" name="mainToolBar" /> + <widget class="QWidget" name="centralWidget" /> + <widget class="QStatusBar" name="statusBar" /> + </widget> + <layoutDefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <resources> + <include location="QtWidgetsApplication2.qrc"/> + </resources> + <connections/> +</UI> diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj new file mode 100644 index 0000000..c642173 --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$(VisualStudioVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B74596C2-46DC-4615-A6EF-0CB66643BB2B}</ProjectGuid> + <Keyword>QtVS_v304</Keyword> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">10.0</WindowsTargetPlatformVersion> + <QtMsBuild Condition="Exists('$(MSBuildProjectDirectory)\..\..\..\..\qtmsbuild')" + >$([System.IO.Path]::GetFullPath( + $(MSBuildProjectDirectory)\..\..\..\..\qtmsbuild\QtMsBuild))</QtMsBuild> + <QtMsBuild Condition="'$(QtMsBuild)'=='' OR !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;gui;widgets</QtModules> + <QtBuildConfig>debug</QtBuildConfig> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="QtSettings"> + <QtInstall>$(DefaultQtVersion)</QtInstall> + <QtModules>core;gui;widgets</QtModules> + <QtBuildConfig>release</QtBuildConfig> + </PropertyGroup> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\MyPropertySheet.props" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\MyPropertySheet.props" /> + <Import Project="$(QtMsBuild)\Qt.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <CopyLocalProjectReference>true</CopyLocalProjectReference> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <CopyLocalProjectReference>true</CopyLocalProjectReference> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <Optimization>Disabled</Optimization> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'" Label="Configuration"> + <ClCompile> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat>None</DebugInformationFormat> + <Optimization>MaxSpeed</Optimization> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <QtRcc Include="QtWidgetsApplication2.qrc" /> + <QtUic Include="QtWidgetsApplication2.ui" /> + <QtMoc Include="QtWidgetsApplication2.h" /> + <ClCompile Include="QtWidgetsApplication2.cpp" /> + <ClCompile Include="main.cpp" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\QtClassLibrary1\QtClassLibrary1.vcxproj"> + <Project>{ce78ec51-c4a0-465b-a161-21c257bd057b}</Project> + </ProjectReference> + <ProjectReference Include="..\StaticLib1\StaticLib1.vcxproj"> + <Project>{8463051a-b32c-43f0-9a77-9f223598aac9}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj.filters b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj.filters new file mode 100644 index 0000000..e00a30a --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication2/QtWidgetsApplication2.vcxproj.filters @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + </Filter> + <Filter Include="Translation Files"> + <UniqueIdentifier>{639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}</UniqueIdentifier> + <Extensions>ts</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <QtRcc Include="QtWidgetsApplication2.qrc"> + <Filter>Resource Files</Filter> + </QtRcc> + <QtUic Include="QtWidgetsApplication2.ui"> + <Filter>Form Files</Filter> + </QtUic> + <QtMoc Include="QtWidgetsApplication2.h"> + <Filter>Header Files</Filter> + </QtMoc> + <ClCompile Include="QtWidgetsApplication2.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/QtWidgetsApplication2/main.cpp b/Tests/concurrency/Solution1/QtWidgetsApplication2/main.cpp new file mode 100644 index 0000000..c081a1d --- /dev/null +++ b/Tests/concurrency/Solution1/QtWidgetsApplication2/main.cpp @@ -0,0 +1,10 @@ +#include "QtWidgetsApplication2.h" +#include <QtWidgets/QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QtWidgetsApplication2 w; + w.show(); + return a.exec(); +} diff --git a/Tests/concurrency/Solution1/Solution1.sln b/Tests/concurrency/Solution1/Solution1.sln new file mode 100644 index 0000000..853142e --- /dev/null +++ b/Tests/concurrency/Solution1/Solution1.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QtClassLibrary1", "QtClassLibrary1\QtClassLibrary1.vcxproj", "{CE78EC51-C4A0-465B-A161-21C257BD057B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QtWidgetsApplication1", "QtWidgetsApplication1\QtWidgetsApplication1.vcxproj", "{806AF248-25F9-4300-9FB3-100E5F79C026}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QtWidgetsApplication2", "QtWidgetsApplication2\QtWidgetsApplication2.vcxproj", "{B74596C2-46DC-4615-A6EF-0CB66643BB2B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StaticLib1", "StaticLib1\StaticLib1.vcxproj", "{8463051A-B32C-43F0-9A77-9F223598AAC9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CE78EC51-C4A0-465B-A161-21C257BD057B}.Debug|x64.ActiveCfg = Debug|x64 + {CE78EC51-C4A0-465B-A161-21C257BD057B}.Debug|x64.Build.0 = Debug|x64 + {CE78EC51-C4A0-465B-A161-21C257BD057B}.Release|x64.ActiveCfg = Release|x64 + {CE78EC51-C4A0-465B-A161-21C257BD057B}.Release|x64.Build.0 = Release|x64 + {806AF248-25F9-4300-9FB3-100E5F79C026}.Debug|x64.ActiveCfg = Debug|x64 + {806AF248-25F9-4300-9FB3-100E5F79C026}.Debug|x64.Build.0 = Debug|x64 + {806AF248-25F9-4300-9FB3-100E5F79C026}.Release|x64.ActiveCfg = Release|x64 + {806AF248-25F9-4300-9FB3-100E5F79C026}.Release|x64.Build.0 = Release|x64 + {B74596C2-46DC-4615-A6EF-0CB66643BB2B}.Debug|x64.ActiveCfg = Debug|x64 + {B74596C2-46DC-4615-A6EF-0CB66643BB2B}.Debug|x64.Build.0 = Debug|x64 + {B74596C2-46DC-4615-A6EF-0CB66643BB2B}.Release|x64.ActiveCfg = Release|x64 + {B74596C2-46DC-4615-A6EF-0CB66643BB2B}.Release|x64.Build.0 = Release|x64 + {8463051A-B32C-43F0-9A77-9F223598AAC9}.Debug|x64.ActiveCfg = Debug|x64 + {8463051A-B32C-43F0-9A77-9F223598AAC9}.Debug|x64.Build.0 = Debug|x64 + {8463051A-B32C-43F0-9A77-9F223598AAC9}.Release|x64.ActiveCfg = Release|x64 + {8463051A-B32C-43F0-9A77-9F223598AAC9}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {76259E50-26CE-4EC6-99CE-B1245F270DD3} + EndGlobalSection +EndGlobal diff --git a/Tests/concurrency/Solution1/StaticLib1/Header.h b/Tests/concurrency/Solution1/StaticLib1/Header.h new file mode 100644 index 0000000..69b048e --- /dev/null +++ b/Tests/concurrency/Solution1/StaticLib1/Header.h @@ -0,0 +1,4 @@ +#pragma once + +void foobar(); + diff --git a/Tests/concurrency/Solution1/StaticLib1/StaticLib1.cpp b/Tests/concurrency/Solution1/StaticLib1/StaticLib1.cpp new file mode 100644 index 0000000..e0fd07b --- /dev/null +++ b/Tests/concurrency/Solution1/StaticLib1/StaticLib1.cpp @@ -0,0 +1,3 @@ +void foobar() +{ +} diff --git a/Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj b/Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj new file mode 100644 index 0000000..d1d0abd --- /dev/null +++ b/Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>16.0</VCProjectVersion> + <Keyword>Win32Proj</Keyword> + <ProjectGuid>{8463051a-b32c-43f0-9a77-9f223598aac9}</ProjectGuid> + <RootNamespace>StaticLib1</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <AllProjectIncludesArePublic>true</AllProjectIncludesArePublic> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <AllProjectIncludesArePublic>true</AllProjectIncludesArePublic> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem> + </SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ConformanceMode>true</ConformanceMode> + </ClCompile> + <Link> + <SubSystem> + </SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="StaticLib1.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="Header.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj.filters b/Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj.filters new file mode 100644 index 0000000..a876f24 --- /dev/null +++ b/Tests/concurrency/Solution1/StaticLib1/StaticLib1.vcxproj.filters @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="StaticLib1.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="Header.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/Tests/concurrency/loop_msbuild.bat b/Tests/concurrency/loop_msbuild.bat new file mode 100644 index 0000000..24854c0 --- /dev/null +++ b/Tests/concurrency/loop_msbuild.bat @@ -0,0 +1,36 @@ +@ECHO OFF + +SET TOTAL=0 +SET FAIL=0 +SET RATE=0 +SET USER_QUIT=0 +SET PAD=..00 + +:loop + CLS + SET RATE=%PAD%%RATE% + ECHO ################################################################################ + ECHO # Total: %TOTAL%, Failed: %FAIL%...%RATE:~-4,-2%,%RATE:~-2%%% + ECHO ################################################################################ + IF %USER_QUIT% EQU 1 GOTO quit + SET /A "TOTAL+=1" + msbuild %* ^ + /m /bl /v:m /nologo ^ + && ( + DEL last_build_ok.binlog 2> NUL + COPY msbuild.binlog last_build_ok.binlog > NUL + ) || ( + SET /A "FAIL+=1" + COPY msbuild.binlog error_build_%TOTAL%.binlog > NUL + ) + SET /A "RATE=(FAIL*100*100)/(TOTAL)" + ECHO ################################################################################ + CHOICE /C QNOP /N /T 1 /D N /M "# [N]ext / [O]pen Log / [P]ause / [Q]uit ?" + IF %ERRORLEVEL% EQU 3 ( + START "" msbuild.binlog + PAUSE + ) + IF %ERRORLEVEL% EQU 4 PAUSE + SET USER_QUIT=%ERRORLEVEL% + GOTO :loop +:quit diff --git a/VsQml/README b/VsQml/README new file mode 100644 index 0000000..c4adc11 --- /dev/null +++ b/VsQml/README @@ -0,0 +1,35 @@ + +== Static build of Qt + +This project requires an x86 static build of Qt that includes at least qtbase and qtdeclarative. + +By default, it will look for the Qt build three levels higher in the file system hierarchy, in +"build\qt5-x86-<VS Version>-static", where <VS Version> is one of "vs2017", "vs2019" or "vs2022". + +For example, when building in Visual Studio 2017, if the Qt VS Tools solution is located in +X:\dev\vstools (and therefore vsqml is located in X:\dev\vstools\src\vsqml), the project will look +for the static build of Qt in X:\dev\build\qt5-x86-vs2017-static. + +It is also possible to override the default Qt build location by setting the following environment +variables: + QTBUILD_STATIC_VS2017 = <path to x86 static build of Qt for Visual Studio 2017> + QTBUILD_STATIC_VS2019 = <path to x86 static build of Qt for Visual Studio 2019> + QTBUILD_STATIC_VS2022 = <path to x64 static build of Qt for Visual Studio 2022> + +The following configure options can be used to obtain an optimized build configuration: + + -opensource -static -nomake examples -nomake tests -opengl desktop -skip qt3d -skip qtactiveqt + -skip qtcanvas3d -skip qtwebchannel -skip qtwebglplugin -skip qtwebsockets -skip qtwebview + -skip qtpurchasing -skip qtandroidextras -skip qtconnectivity -skip qtdatavis3d -skip qtfeedback + -skip qtgamepad -skip qtgraphicaleffects -skip qtimageformats -skip qtmultimedia + -skip qtnetworkauth -skip qtpim -skip qtremoteobjects -skip qtrepotools -skip qtsensors + -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qttranslations + -skip qtvirtualkeyboard -skip qtwayland -skip qtcharts -skip qtlocation -skip qtmacextras + -skip qtqa -skip qtquickcontrols -skip qtscript -skip qtscxml -skip qttools -skip qtwinextras + -skip qtx11extras -skip qtxmlpatterns + + +== Project output + +The output DLL is copied to a bin sub-folder inside the project directory +(e.g. X:\dev\vstools\vsqml\bin). This is the location where the VS Tools project will look for it. diff --git a/VsQml/astvisitor.cpp b/VsQml/astvisitor.cpp new file mode 100644 index 0000000..9fa1f10 --- /dev/null +++ b/VsQml/astvisitor.cpp @@ -0,0 +1,563 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "astvisitor.h" + +using namespace QQmlJS::AST; + +class AstVisitorPrivate : public QQmlJS::AST::Visitor +{ +private: + Callback callbackUnfiltered; + QMap<int, Callback> callbackFiltered; + + Callback getCallback(Node *node) { + auto callback = callbackUnfiltered; + auto itFilter = callbackFiltered.find(node->kind); + if (itFilter != callbackFiltered.end()) + callback = itFilter.value(); + return callback; + } + + bool invokeCallback(Callback callback, QVector<int> &nodeData, Node *node, bool beginVisit) + { + if (!callback) + return true; + bool result = callback(this, node->kind, node, + beginVisit, nodeData.data(), nodeData.count() * sizeof(int)); + + nodeData.clear(); + return result; + } + + void marshalInt(QVector<int> &nodeData, int n) + { + nodeData.append(n); + } + + void marshalLocation(QVector<int> &nodeData, SourceLocation &location) + { + nodeData.append(location.offset); + nodeData.append(location.length); + } + + void marshalPointer(QVector<int> &nodeData, void *ptr) + { + auto ptrRef = reinterpret_cast<long long>(ptr); + auto ptrHi = ptrRef >> 32; + auto ptrLo = ptrRef & 0xFFFFFFFFLL; + nodeData.append(int(ptrHi)); + nodeData.append(int(ptrLo)); + } + + void marshalNode(QVector<int> &nodeData, Node *node) + { + auto firstLocation = node->firstSourceLocation(); + auto lastLocation = node->lastSourceLocation(); + marshalLocation(nodeData, firstLocation); + marshalLocation(nodeData, lastLocation); + } + + bool visitCallback(UiImport *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + marshalLocation(nodeData, node->importToken); + marshalLocation(nodeData, node->fileNameToken); + marshalLocation(nodeData, node->versionToken); + marshalLocation(nodeData, node->asToken); + marshalLocation(nodeData, node->importIdToken); + marshalLocation(nodeData, node->semicolonToken); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(UiQualifiedId *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + marshalPointer(nodeData, node->next); + marshalLocation(nodeData, node->identifierToken); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(UiObjectDefinition *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + marshalPointer(nodeData, node->qualifiedTypeNameId); + marshalPointer(nodeData, node->initializer); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(UiObjectBinding *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + marshalPointer(nodeData, node->qualifiedId); + marshalPointer(nodeData, node->qualifiedTypeNameId); + marshalPointer(nodeData, node->initializer); + marshalLocation(nodeData, node->colonToken); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(UiObjectInitializer *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(UiScriptBinding *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + marshalPointer(nodeData, node->qualifiedId); + marshalPointer(nodeData, node->statement); + marshalLocation(nodeData, node->colonToken); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(UiArrayBinding *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + marshalPointer(nodeData, node->qualifiedId); + marshalPointer(nodeData, node->members); + marshalLocation(nodeData, node->colonToken); + marshalLocation(nodeData, node->lbracketToken); + marshalLocation(nodeData, node->rbracketToken); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(UiPublicMember *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + marshalInt(nodeData, node->type); + marshalPointer(nodeData, node->memberType); + marshalPointer(nodeData, node->statement); + marshalPointer(nodeData, node->binding); + marshalInt(nodeData, node->isDefaultMember); + marshalInt(nodeData, node->isReadonlyMember); + marshalPointer(nodeData, node->parameters); + marshalLocation(nodeData, node->defaultToken); + marshalLocation(nodeData, node->readonlyToken); + marshalLocation(nodeData, node->propertyToken); + marshalLocation(nodeData, node->typeModifierToken); + marshalLocation(nodeData, node->typeToken); + marshalLocation(nodeData, node->identifierToken); + marshalLocation(nodeData, node->colonToken); + marshalLocation(nodeData, node->semicolonToken); + return invokeCallback(callback, nodeData, node, beginVisit); + } + + bool visitCallback(Node *node, bool beginVisit) + { + auto callback = getCallback(node); + if (!callback) + return true; + QVector<int> nodeData; + marshalNode(nodeData, node); + return invokeCallback(callback, nodeData, node, beginVisit); + } + +public: + AstVisitorPrivate() + { + callbackUnfiltered = 0; + } + + ~AstVisitorPrivate() + { + } + + void setCallback(Callback visitCallback) + { + callbackUnfiltered = visitCallback; + } + + void setCallback(int nodeKindFilter, Callback visitCallback) + { + callbackFiltered[nodeKindFilter] = visitCallback; + } + + // Copied from qqmljsastvisitor_p.h + + virtual bool visit(UiProgram *node) { return visitCallback(node, true); } + virtual bool visit(UiHeaderItemList *node) { return visitCallback(node, true); } + virtual bool visit(UiPragma *node) { return visitCallback(node, true); } + virtual bool visit(UiImport *node) { return visitCallback(node, true); } + virtual bool visit(UiPublicMember *node) { return visitCallback(node, true); } + virtual bool visit(UiSourceElement *node) { return visitCallback(node, true); } + virtual bool visit(UiObjectDefinition *node) { return visitCallback(node, true); } + virtual bool visit(UiObjectInitializer *node) { return visitCallback(node, true); } + virtual bool visit(UiObjectBinding *node) { return visitCallback(node, true); } + virtual bool visit(UiScriptBinding *node) { return visitCallback(node, true); } + virtual bool visit(UiArrayBinding *node) { return visitCallback(node, true); } + virtual bool visit(UiParameterList *node) { return visitCallback(node, true); } + virtual bool visit(UiObjectMemberList *node) { return visitCallback(node, true); } + virtual bool visit(UiArrayMemberList *node) { return visitCallback(node, true); } + virtual bool visit(UiQualifiedId *node) { return visitCallback(node, true); } + virtual bool visit(UiEnumDeclaration *node) { return visitCallback(node, true); } + virtual bool visit(UiEnumMemberList *node) { return visitCallback(node, true); } + + virtual void endVisit(UiProgram *node) { visitCallback(node, false); } + virtual void endVisit(UiImport *node) { visitCallback(node, false); } + virtual void endVisit(UiHeaderItemList *node) { visitCallback(node, false); } + virtual void endVisit(UiPragma *node) { visitCallback(node, false); } + virtual void endVisit(UiPublicMember *node) { visitCallback(node, false); } + virtual void endVisit(UiSourceElement *node) { visitCallback(node, false); } + virtual void endVisit(UiObjectDefinition *node) { visitCallback(node, false); } + virtual void endVisit(UiObjectInitializer *node) { visitCallback(node, false); } + virtual void endVisit(UiObjectBinding *node) { visitCallback(node, false); } + virtual void endVisit(UiScriptBinding *node) { visitCallback(node, false); } + virtual void endVisit(UiArrayBinding *node) { visitCallback(node, false); } + virtual void endVisit(UiParameterList *node) { visitCallback(node, false); } + virtual void endVisit(UiObjectMemberList *node) { visitCallback(node, false); } + virtual void endVisit(UiArrayMemberList *node) { visitCallback(node, false); } + virtual void endVisit(UiQualifiedId *node) { visitCallback(node, false); } + virtual void endVisit(UiEnumDeclaration *node) { visitCallback(node, false); } + virtual void endVisit(UiEnumMemberList *node) { visitCallback(node, false); } + + // QQmlJS + virtual bool visit(ThisExpression *node) { return visitCallback(node, true); } + virtual void endVisit(ThisExpression *node) { visitCallback(node, false); } + + virtual bool visit(IdentifierExpression *node) { return visitCallback(node, true); } + virtual void endVisit(IdentifierExpression *node) { visitCallback(node, false); } + + virtual bool visit(NullExpression *node) { return visitCallback(node, true); } + virtual void endVisit(NullExpression *node) { visitCallback(node, false); } + + virtual bool visit(TrueLiteral *node) { return visitCallback(node, true); } + virtual void endVisit(TrueLiteral *node) { visitCallback(node, false); } + + virtual bool visit(FalseLiteral *node) { return visitCallback(node, true); } + virtual void endVisit(FalseLiteral *node) { visitCallback(node, false); } + + virtual bool visit(SuperLiteral *node) { return visitCallback(node, true); } + virtual void endVisit(SuperLiteral *node) { visitCallback(node, false); } + + virtual bool visit(StringLiteral *node) { return visitCallback(node, true); } + virtual void endVisit(StringLiteral *node) { visitCallback(node, false); } + + virtual bool visit(TemplateLiteral *node) { return visitCallback(node, true); } + virtual void endVisit(TemplateLiteral *node) { visitCallback(node, false); } + + virtual bool visit(NumericLiteral *node) { return visitCallback(node, true); } + virtual void endVisit(NumericLiteral *node) { visitCallback(node, false); } + + virtual bool visit(RegExpLiteral *node) { return visitCallback(node, true); } + virtual void endVisit(RegExpLiteral *node) { visitCallback(node, false); } + + virtual bool visit(ArrayPattern *node) { return visitCallback(node, true); } + virtual void endVisit(ArrayPattern *node) { visitCallback(node, false); } + + virtual bool visit(ObjectPattern *node) { return visitCallback(node, true); } + virtual void endVisit(ObjectPattern *node) { visitCallback(node, false); } + + virtual bool visit(PatternElementList *node) { return visitCallback(node, true); } + virtual void endVisit(PatternElementList *node) { visitCallback(node, false); } + + virtual bool visit(PatternPropertyList *node) { return visitCallback(node, true); } + virtual void endVisit(PatternPropertyList *node) { visitCallback(node, false); } + + virtual bool visit(PatternElement *node) { return visitCallback(node, true); } + virtual void endVisit(PatternElement *node) { visitCallback(node, false); } + + virtual bool visit(PatternProperty *node) { return visitCallback(node, true); } + virtual void endVisit(PatternProperty *node) { visitCallback(node, false); } + + virtual bool visit(Elision *node) { return visitCallback(node, true); } + virtual void endVisit(Elision *node) { visitCallback(node, false); } + + virtual bool visit(NestedExpression *node) { return visitCallback(node, true); } + virtual void endVisit(NestedExpression *node) { visitCallback(node, false); } + + virtual bool visit(IdentifierPropertyName *node) { return visitCallback(node, true); } + virtual void endVisit(IdentifierPropertyName *node) { visitCallback(node, false); } + + virtual bool visit(StringLiteralPropertyName *node) { return visitCallback(node, true); } + virtual void endVisit(StringLiteralPropertyName *node) { visitCallback(node, false); } + + virtual bool visit(NumericLiteralPropertyName *node) { return visitCallback(node, true); } + virtual void endVisit(NumericLiteralPropertyName *node) { visitCallback(node, false); } + + virtual bool visit(ComputedPropertyName *node) { return visitCallback(node, true); } + virtual void endVisit(ComputedPropertyName *node) { visitCallback(node, false); } + + virtual bool visit(ArrayMemberExpression *node) { return visitCallback(node, true); } + virtual void endVisit(ArrayMemberExpression *node) { visitCallback(node, false); } + + virtual bool visit(FieldMemberExpression *node) { return visitCallback(node, true); } + virtual void endVisit(FieldMemberExpression *node) { visitCallback(node, false); } + + virtual bool visit(TaggedTemplate *node) { return visitCallback(node, true); } + virtual void endVisit(TaggedTemplate *node) { visitCallback(node, false); } + + virtual bool visit(NewMemberExpression *node) { return visitCallback(node, true); } + virtual void endVisit(NewMemberExpression *node) { visitCallback(node, false); } + + virtual bool visit(NewExpression *node) { return visitCallback(node, true); } + virtual void endVisit(NewExpression *node) { visitCallback(node, false); } + + virtual bool visit(CallExpression *node) { return visitCallback(node, true); } + virtual void endVisit(CallExpression *node) { visitCallback(node, false); } + + virtual bool visit(ArgumentList *node) { return visitCallback(node, true); } + virtual void endVisit(ArgumentList *node) { visitCallback(node, false); } + + virtual bool visit(PostIncrementExpression *node) { return visitCallback(node, true); } + virtual void endVisit(PostIncrementExpression *node) { visitCallback(node, false); } + + virtual bool visit(PostDecrementExpression *node) { return visitCallback(node, true); } + virtual void endVisit(PostDecrementExpression *node) { visitCallback(node, false); } + + virtual bool visit(DeleteExpression *node) { return visitCallback(node, true); } + virtual void endVisit(DeleteExpression *node) { visitCallback(node, false); } + + virtual bool visit(VoidExpression *node) { return visitCallback(node, true); } + virtual void endVisit(VoidExpression *node) { visitCallback(node, false); } + + virtual bool visit(TypeOfExpression *node) { return visitCallback(node, true); } + virtual void endVisit(TypeOfExpression *node) { visitCallback(node, false); } + + virtual bool visit(PreIncrementExpression *node) { return visitCallback(node, true); } + virtual void endVisit(PreIncrementExpression *node) { visitCallback(node, false); } + + virtual bool visit(PreDecrementExpression *node) { return visitCallback(node, true); } + virtual void endVisit(PreDecrementExpression *node) { visitCallback(node, false); } + + virtual bool visit(UnaryPlusExpression *node) { return visitCallback(node, true); } + virtual void endVisit(UnaryPlusExpression *node) { visitCallback(node, false); } + + virtual bool visit(UnaryMinusExpression *node) { return visitCallback(node, true); } + virtual void endVisit(UnaryMinusExpression *node) { visitCallback(node, false); } + + virtual bool visit(TildeExpression *node) { return visitCallback(node, true); } + virtual void endVisit(TildeExpression *node) { visitCallback(node, false); } + + virtual bool visit(NotExpression *node) { return visitCallback(node, true); } + virtual void endVisit(NotExpression *node) { visitCallback(node, false); } + + virtual bool visit(BinaryExpression *node) { return visitCallback(node, true); } + virtual void endVisit(BinaryExpression *node) { visitCallback(node, false); } + + virtual bool visit(ConditionalExpression *node) { return visitCallback(node, true); } + virtual void endVisit(ConditionalExpression *node) { visitCallback(node, false); } + + virtual bool visit(Expression *node) { return visitCallback(node, true); } + virtual void endVisit(Expression *node) { visitCallback(node, false); } + + virtual bool visit(Block *node) { return visitCallback(node, true); } + virtual void endVisit(Block *node) { visitCallback(node, false); } + + virtual bool visit(StatementList *node) { return visitCallback(node, true); } + virtual void endVisit(StatementList *node) { visitCallback(node, false); } + + virtual bool visit(VariableStatement *node) { return visitCallback(node, true); } + virtual void endVisit(VariableStatement *node) { visitCallback(node, false); } + + virtual bool visit(VariableDeclarationList *node) { return visitCallback(node, true); } + virtual void endVisit(VariableDeclarationList *node) { visitCallback(node, false); } + + virtual bool visit(EmptyStatement *node) { return visitCallback(node, true); } + virtual void endVisit(EmptyStatement *node) { visitCallback(node, false); } + + virtual bool visit(ExpressionStatement *node) { return visitCallback(node, true); } + virtual void endVisit(ExpressionStatement *node) { visitCallback(node, false); } + + virtual bool visit(IfStatement *node) { return visitCallback(node, true); } + virtual void endVisit(IfStatement *node) { visitCallback(node, false); } + + virtual bool visit(DoWhileStatement *node) { return visitCallback(node, true); } + virtual void endVisit(DoWhileStatement *node) { visitCallback(node, false); } + + virtual bool visit(WhileStatement *node) { return visitCallback(node, true); } + virtual void endVisit(WhileStatement *node) { visitCallback(node, false); } + + virtual bool visit(ForStatement *node) { return visitCallback(node, true); } + virtual void endVisit(ForStatement *node) { visitCallback(node, false); } + + virtual bool visit(ForEachStatement *node) { return visitCallback(node, true); } + virtual void endVisit(ForEachStatement *node) { visitCallback(node, false); } + + virtual bool visit(ContinueStatement *node) { return visitCallback(node, true); } + virtual void endVisit(ContinueStatement *node) { visitCallback(node, false); } + + virtual bool visit(BreakStatement *node) { return visitCallback(node, true); } + virtual void endVisit(BreakStatement *node) { visitCallback(node, false); } + + virtual bool visit(ReturnStatement *node) { return visitCallback(node, true); } + virtual void endVisit(ReturnStatement *node) { visitCallback(node, false); } + + virtual bool visit(YieldExpression *node) { return visitCallback(node, true); } + virtual void endVisit(YieldExpression *node) { visitCallback(node, false); } + + virtual bool visit(WithStatement *node) { return visitCallback(node, true); } + virtual void endVisit(WithStatement *node) { visitCallback(node, false); } + + virtual bool visit(SwitchStatement *node) { return visitCallback(node, true); } + virtual void endVisit(SwitchStatement *node) { visitCallback(node, false); } + + virtual bool visit(CaseBlock *node) { return visitCallback(node, true); } + virtual void endVisit(CaseBlock *node) { visitCallback(node, false); } + + virtual bool visit(CaseClauses *node) { return visitCallback(node, true); } + virtual void endVisit(CaseClauses *node) { visitCallback(node, false); } + + virtual bool visit(CaseClause *node) { return visitCallback(node, true); } + virtual void endVisit(CaseClause *node) { visitCallback(node, false); } + + virtual bool visit(DefaultClause *node) { return visitCallback(node, true); } + virtual void endVisit(DefaultClause *node) { visitCallback(node, false); } + + virtual bool visit(LabelledStatement *node) { return visitCallback(node, true); } + virtual void endVisit(LabelledStatement *node) { visitCallback(node, false); } + + virtual bool visit(ThrowStatement *node) { return visitCallback(node, true); } + virtual void endVisit(ThrowStatement *node) { visitCallback(node, false); } + + virtual bool visit(TryStatement *node) { return visitCallback(node, true); } + virtual void endVisit(TryStatement *node) { visitCallback(node, false); } + + virtual bool visit(Catch *node) { return visitCallback(node, true); } + virtual void endVisit(Catch *node) { visitCallback(node, false); } + + virtual bool visit(Finally *node) { return visitCallback(node, true); } + virtual void endVisit(Finally *node) { visitCallback(node, false); } + + virtual bool visit(FunctionDeclaration *node) { return visitCallback(node, true); } + virtual void endVisit(FunctionDeclaration *node) { visitCallback(node, false); } + + virtual bool visit(FunctionExpression *node) { return visitCallback(node, true); } + virtual void endVisit(FunctionExpression *node) { visitCallback(node, false); } + + virtual bool visit(FormalParameterList *node) { return visitCallback(node, true); } + virtual void endVisit(FormalParameterList *node) { visitCallback(node, false); } + + virtual bool visit(ClassExpression *node) { return visitCallback(node, true); } + virtual void endVisit(ClassExpression *node) { visitCallback(node, false); } + + virtual bool visit(ClassDeclaration *node) { return visitCallback(node, true); } + virtual void endVisit(ClassDeclaration *node) { visitCallback(node, false); } + + virtual bool visit(ClassElementList *node) { return visitCallback(node, true); } + virtual void endVisit(ClassElementList *node) { visitCallback(node, false); } + + virtual bool visit(Program *node) { return visitCallback(node, true); } + virtual void endVisit(Program *node) { visitCallback(node, false); } + + virtual bool visit(NameSpaceImport *node) { return visitCallback(node, true); } + virtual void endVisit(NameSpaceImport *node) { visitCallback(node, false); } + + virtual bool visit(ImportSpecifier *node) { return visitCallback(node, true); } + virtual void endVisit(ImportSpecifier *node) { visitCallback(node, false); } + + virtual bool visit(ImportsList *node) { return visitCallback(node, true); } + virtual void endVisit(ImportsList *node) { visitCallback(node, false); } + + virtual bool visit(NamedImports *node) { return visitCallback(node, true); } + virtual void endVisit(NamedImports *node) { visitCallback(node, false); } + + virtual bool visit(FromClause *node) { return visitCallback(node, true); } + virtual void endVisit(FromClause *node) { visitCallback(node, false); } + + virtual bool visit(ImportClause *node) { return visitCallback(node, true); } + virtual void endVisit(ImportClause *node) { visitCallback(node, false); } + + virtual bool visit(ImportDeclaration *node) { return visitCallback(node, true); } + virtual void endVisit(ImportDeclaration *node) { visitCallback(node, false); } + + virtual bool visit(ExportSpecifier *node) { return visitCallback(node, true); } + virtual void endVisit(ExportSpecifier *node) { visitCallback(node, false); } + + virtual bool visit(ExportsList *node) { return visitCallback(node, true); } + virtual void endVisit(ExportsList *node) { visitCallback(node, false); } + + virtual bool visit(ExportClause *node) { return visitCallback(node, true); } + virtual void endVisit(ExportClause *node) { visitCallback(node, false); } + + virtual bool visit(ExportDeclaration *node) { return visitCallback(node, true); } + virtual void endVisit(ExportDeclaration *node) { visitCallback(node, false); } + + virtual bool visit(ESModule *node) { return visitCallback(node, true); } + virtual void endVisit(ESModule *node) { visitCallback(node, false); } + + virtual bool visit(DebuggerStatement *node) { return visitCallback(node, true); } + virtual void endVisit(DebuggerStatement *node) { visitCallback(node, false); } + + virtual void throwRecursionDepthError() { /* TODO: Anything we should do here? */ } +}; + +AstVisitor::AstVisitor() : d_ptr(new AstVisitorPrivate) +{ +} + +AstVisitor::~AstVisitor() +{ + delete d_ptr; +} + +void AstVisitor::setCallback(Callback visitCallback) +{ + d_ptr->setCallback(visitCallback); +} + +void AstVisitor::setCallback(int nodeKindFilter, Callback visitCallback) +{ + d_ptr->setCallback(nodeKindFilter, visitCallback); +} + +Visitor *AstVisitor::GetVisitor() +{ + return d_ptr; +} diff --git a/VsQml/astvisitor.h b/VsQml/astvisitor.h new file mode 100644 index 0000000..99c2e58 --- /dev/null +++ b/VsQml/astvisitor.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once + +#include "vsqml.h" + +#include <QtQml/private/qqmljslexer_p.h> +#include <QtQml/private/qqmljsparser_p.h> + +class AstVisitorPrivate; + +class AstVisitor { +public: + AstVisitor(); + ~AstVisitor(); + void setCallback(Callback visitCallback); + void setCallback(int nodeKindFilter, Callback visitCallback); + QQmlJS::AST::Visitor *GetVisitor(); + +private: + AstVisitorPrivate *d_ptr; +}; diff --git a/VsQml/vsqml.cpp b/VsQml/vsqml.cpp new file mode 100644 index 0000000..4283917 --- /dev/null +++ b/VsQml/vsqml.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "vsqml.h" +#include "astvisitor.h" +#include "vsqmldebugclient.h" + +#include <QtQml/private/qqmljslexer_p.h> +#include <QtQml/private/qqmljsparser_p.h> +#include <QtQml/private/qqmljsglobal_p.h> +#include <QtQml/private/qqmljsgrammar_p.h> + +#include <QCoreApplication> + +#include <Windows.h> + +using namespace QQmlJS; +using namespace QQmlJS::AST; + +QCoreApplication *app = nullptr; + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) +{ + switch (reason) { + case DLL_PROCESS_DETACH: + if (app) { + delete app; + app = nullptr; + } + break; + default: + break; + } + return TRUE; +} + +QCoreApplication *GetQtApplication() +{ + if (app == nullptr) { + static int argc = 1; + static const char *argv[] = { "vsqml", nullptr }; + app = new QCoreApplication(argc, const_cast<char **>(argv)); + } + + return app; +} + +struct State +{ + Engine *engine; + Lexer *lexer; + Parser *parser; +}; + +bool qmlGetTokens(const char *qmlText, int qmlTextLength, int **tokens, int *tokensLength) +{ + if (!qmlText || !tokens || !tokensLength) + return false; + + Engine engine; + Lexer lexer(&engine); + Parser parser(&engine); + + lexer.setCode(QString::fromUtf8(qmlText, qmlTextLength), 1, true); + + QVector<int> tokenValues; + for (lexer.lex(); lexer.tokenKind(); lexer.lex()) { + tokenValues.append(lexer.tokenKind()); + tokenValues.append(lexer.tokenOffset()); + tokenValues.append(lexer.tokenLength()); + } + *tokensLength = tokenValues.count() * sizeof(int); + *tokens = new int[tokenValues.count()]; + memcpy(*tokens, tokenValues.data(), *tokensLength); + + return true; +} + +bool qmlFreeTokens(int *tokens) +{ + if (!tokens) + return false; + + delete[] tokens; + return true; +} + +bool qmlParse( + const char *qmlText, + int qmlTextLength, + void **parser, + bool *parsedCorrectly, + int **diagnosticMessages, + int *diagnosticMessagesLength, + int **comments, + int *commentsLength) +{ + if (!qmlText || !parser) + return false; + + *parser = 0; + + if (parsedCorrectly) + *parsedCorrectly = false; + if (diagnosticMessages) + *diagnosticMessages = 0; + if (diagnosticMessagesLength) + *diagnosticMessagesLength = 0; + if (comments) + *comments = 0; + if (commentsLength) + *commentsLength = 0; + + auto s = new State; + s->engine = new Engine(); + s->lexer = new Lexer(s->engine); + s->parser = new Parser(s->engine); + *parser = s; + + s->lexer->setCode(QString::fromUtf8(qmlText, qmlTextLength), 1, true); + bool ok = s->parser->parse(); + if (parsedCorrectly) + *parsedCorrectly = ok; + + if (diagnosticMessages && diagnosticMessagesLength) { + QVector<int> diagValues; + for (auto diag : s->parser->diagnosticMessages()) { + diagValues.append(diag.kind); + diagValues.append(diag.loc.offset); + diagValues.append(diag.loc.length); + } + *diagnosticMessagesLength = diagValues.count() * sizeof(int); + *diagnosticMessages = new int[diagValues.count()]; + memcpy(*diagnosticMessages, diagValues.data(), *diagnosticMessagesLength); + } + + if (comments && commentsLength) { + QVector<int> commentValues; + for (auto comment : s->engine->comments()) { + commentValues.append(comment.offset); + commentValues.append(comment.length); + } + *commentsLength = commentValues.count() * sizeof(int); + *comments = new int[commentValues.count()]; + memcpy(*comments, commentValues.data(), *commentsLength); + } + + return true; +} + +bool qmlFreeParser(void *parser) +{ + if (!parser) + return false; + + auto s = reinterpret_cast<State*>(parser); + delete s->parser; + delete s->lexer; + delete s->engine; + delete s; + + return true; +} + +bool qmlFreeDiagnosticMessages(int *diagnosticMessages) +{ + if (!diagnosticMessages) + return false; + + delete[] diagnosticMessages; + return true; +} + +bool qmlFreeComments(int *comments) +{ + if (!comments) + return false; + + delete[] comments; + return true; +} + +void *qmlGetAstVisitor() +{ + return new AstVisitor(); +} + +bool qmlFreeAstVisitor(void *astVisitor) +{ + if (!astVisitor) + return false; + + delete reinterpret_cast<AstVisitor*>(astVisitor); + return true; +} + +bool qmlSetAstVisitorCallback(void *astVisitor, int nodeKindFilter, Callback visitCallback) +{ + if (!astVisitor) + return false; + + auto visitor = reinterpret_cast<AstVisitor*>(astVisitor); + if (nodeKindFilter <= Node::Kind_Undefined) + visitor->setCallback(visitCallback); + else + visitor->setCallback(nodeKindFilter, visitCallback); + + return true; +} + +bool qmlAcceptAstVisitor(void *parser, void *node, void *astVisitor) +{ + if (!parser || !astVisitor) + return false; + + auto s = reinterpret_cast<State*>(parser); + auto visitor = reinterpret_cast<AstVisitor*>(astVisitor); + + Node *visitNode = 0; + if (node) + visitNode = reinterpret_cast<Node*>(node); + else + visitNode = s->parser->rootNode(); + + if (!visitNode) + return false; + + visitNode->accept(visitor->GetVisitor()); + + return true; +} + +bool qmlDebugClientThread( + QmlDebugClientCreated clientCreated, + QmlDebugClientDestroyed clientDestroyed, + QmlDebugClientConnected clientConnected, + QmlDebugClientDisconnected clientDisconnected, + QmlDebugClientMessageReceived clientMessageReceived) +{ + GetQtApplication(); + + QEventLoop eventLoop; + VsQmlDebugClient client(&eventLoop); + + if (clientCreated) + clientCreated(&client); + + QObject::connect(&client, &VsQmlDebugClient::connected, [&client, clientConnected]() + { + if (clientConnected) + clientConnected(&client); + }); + + QObject::connect(&client, &VsQmlDebugClient::disconnected, [&client, clientDisconnected]() + { + if (clientDisconnected) + clientDisconnected(&client); + }); + + QObject::connect(&client, &VsQmlDebugClient::messageReceived, + [&client, clientMessageReceived]( + const QByteArray &messageType, + const QByteArray &messageParams) + { + if (clientMessageReceived) + clientMessageReceived(&client, + messageType.constData(), messageType.size(), + messageParams.constData(), messageParams.size()); + }); + + int exitCode = eventLoop.exec(); + + if (clientDestroyed) + clientDestroyed(&client); + + return (exitCode == 0); +} + +bool qmlDebugClientConnect( + void *qmlDebugClient, + const char *hostNameData, + int hostNameLength, + unsigned short hostPort) +{ + if (!qmlDebugClient) + return false; + + auto client = reinterpret_cast<VsQmlDebugClient*>(qmlDebugClient); + QString hostName = QString::fromUtf8(hostNameData, hostNameLength); + + return QMetaObject::invokeMethod(client, "connectToHost", Qt::QueuedConnection, + Q_ARG(QString, hostName), Q_ARG(quint16, hostPort)); +} + +bool qmlDebugClientStartLocalServer( + void *qmlDebugClient, + const char *fileNameData, + int fileNameLength) +{ + if (!qmlDebugClient) + return false; + + auto client = reinterpret_cast<VsQmlDebugClient *>(qmlDebugClient); + QString fileName = QString::fromUtf8(fileNameData, fileNameLength); + + return QMetaObject::invokeMethod(client, "startLocalServer", Qt::QueuedConnection, + Q_ARG(QString, fileName)); +} + +bool qmlDebugClientDisconnect(void *qmlDebugClient) +{ + if (!qmlDebugClient) + return false; + + auto client = reinterpret_cast<VsQmlDebugClient*>(qmlDebugClient); + + return QMetaObject::invokeMethod(client, "disconnectFromHost", Qt::QueuedConnection); +} + +bool qmlDebugClientSendMessage( + void *qmlDebugClient, + const char *messageTypeData, + int messageTypeLength, + const char *messageParamsData, + int messageParamsLength) +{ + if (!qmlDebugClient) + return false; + + auto client = reinterpret_cast<VsQmlDebugClient*>(qmlDebugClient); + QByteArray messageType = QByteArray::fromRawData(messageTypeData, messageTypeLength); + QByteArray messageParams = QByteArray::fromRawData(messageParamsData, messageParamsLength); + + return QMetaObject::invokeMethod(client, "sendMessage", Qt::QueuedConnection, + Q_ARG(QByteArray, messageType), Q_ARG(QByteArray, messageParams)); +} + +bool qmlDebugClientShutdown(void *qmlDebugClient) +{ + if (!qmlDebugClient) + return false; + + auto client = reinterpret_cast<VsQmlDebugClient*>(qmlDebugClient); + return QMetaObject::invokeMethod(client->parent(), "quit", Qt::QueuedConnection); +} diff --git a/VsQml/vsqml.h b/VsQml/vsqml.h new file mode 100644 index 0000000..52cb16d --- /dev/null +++ b/VsQml/vsqml.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once + +#include "vsqml_global.h" + +extern "C" VSQML_EXPORT bool qmlGetTokens( + const char *qmlText, + int qmlTextLength, + int **tokens, + int *tokensLength); + +extern "C" VSQML_EXPORT bool qmlParse( + const char *qmlText, + int qmlTextLength, + void **parser, + bool *parsedCorrectly, + int **diagnosticMessages, + int *diagnosticMessagesLength, + int **comments, + int *commentsLength); + +extern "C" VSQML_EXPORT void *qmlGetAstVisitor(); + +typedef bool(__stdcall *Callback)( + void *astVisitor, + int nodeKind, + void *node, + bool beginVisit, + int *nodeData, + int nodeDataLength); + +extern "C" VSQML_EXPORT bool qmlSetAstVisitorCallback( + void *astVisitor, + int nodeKindFilter, + Callback visitCallback); + +extern "C" VSQML_EXPORT bool qmlAcceptAstVisitor( + void *parser, + void *node, + void *astVisitor); + +extern "C" VSQML_EXPORT bool qmlFreeTokens(int *tokenData); + +extern "C" VSQML_EXPORT bool qmlFreeParser(void *parser); + +extern "C" VSQML_EXPORT bool qmlFreeDiagnosticMessages(int *diagnosticMessages); + +extern "C" VSQML_EXPORT bool qmlFreeComments(int *commentData); + +extern "C" VSQML_EXPORT bool qmlFreeAstVisitor(void *astVisitor); + +typedef void(__stdcall *QmlDebugClientCreated)(void *qmlDebugClient); + +typedef void(__stdcall *QmlDebugClientDestroyed)(void *qmlDebugClient); + +typedef void(__stdcall *QmlDebugClientConnected)(void *qmlDebugClient); + +typedef void(__stdcall *QmlDebugClientDisconnected)(void *qmlDebugClient); + +typedef void(__stdcall *QmlDebugClientMessageReceived)( + void *qmlDebugClient, + const char *messageTypeData, + int messageTypeLength, + const char *messageParamsData, + int messageParamsLength); + +extern "C" VSQML_EXPORT bool qmlDebugClientThread( + QmlDebugClientCreated clientCreated, + QmlDebugClientDestroyed clientDestroyed, + QmlDebugClientConnected clientConnected, + QmlDebugClientDisconnected clientDisconnected, + QmlDebugClientMessageReceived clientMessageReceived); + +extern "C" VSQML_EXPORT bool qmlDebugClientConnect( + void *qmlDebugClient, + const char *hostNameData, + int hostNameLength, + unsigned short hostPort); + +extern "C" VSQML_EXPORT bool qmlDebugClientStartLocalServer( + void *qmlDebugClient, + const char *fileNameData, + int fileNameLength); + +extern "C" VSQML_EXPORT bool qmlDebugClientDisconnect(void *qmlDebugClient); + +extern "C" VSQML_EXPORT bool qmlDebugClientSendMessage( + void *qmlDebugClient, + const char *messageTypeData, + int messageTypeLength, + const char *messageParamsData, + int messageParamsLength); + +extern "C" VSQML_EXPORT bool qmlDebugClientShutdown(void *qmlDebugClient); diff --git a/VsQml/vsqml.vcxproj b/VsQml/vsqml.vcxproj new file mode 100644 index 0000000..02c5578 --- /dev/null +++ b/VsQml/vsqml.vcxproj @@ -0,0 +1,196 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="$(VisualStudioVersion)" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B12702AD-ABFB-343A-A199-8E24837244A3}</ProjectGuid> + <Keyword>QtVS_v303</Keyword> + <ProjectName>VsQml</ProjectName> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='15.0'">10.0.17763.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='16.0'">10.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion Condition="'$(VisualStudioVersion)'=='17.0'">10.0</WindowsTargetPlatformVersion> + <QtMsBuild>$(SolutionDir)\qtmsbuild\QtMsBuild</QtMsBuild> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='15.0'">v141</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='16.0'">v142</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)'=='17.0'">v143</PlatformToolset> + </PropertyGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <OutDir>bin\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</OutDir> + <IntDir>obj\$(VisualStudioVersion)\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + <Import Project="..\QtCppConfig.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"> + <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /> + </Target> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <QtModules>core;core-private;qml;qml-private;qmldebug-private</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <QtModules>core;core-private;qml;qml-private;qmldebug-private</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <QtModules>core;core-private;qml;qml-private;qmldebug-private</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <QtModules>core;core-private;qml;qml-private;qmldebug-private</QtModules> + <QtInstall>$(QtBuild)</QtInstall> + </PropertyGroup> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"> + <Import Project="$(QtMsBuild)\qt.props" /> + </ImportGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>VSQML_LIB;$(Qt_DEFINES_);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).dll</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>VSQML_LIB;$(Qt_DEFINES_);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).dll</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>VSQML_LIB;$(Qt_DEFINES_);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).dll</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PreprocessorDefinitions>VSQML_LIB;$(Qt_DEFINES_);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).dll</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + </Link> + <PostBuildEvent> + <Command>copy $(TargetPath) bin</Command> + </PostBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="astvisitor.cpp" /> + <ClCompile Include="vsqml.cpp" /> + <QtMoc Include="vsqmldebugclient.cpp"> + <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">input</DynamicSource> + <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">input</DynamicSource> + <OutputFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)moc\%(RecursiveDir)%(Filename).moc</OutputFile> + <OutputFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)moc\%(RecursiveDir)%(Filename).moc</OutputFile> + <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(Filename).moc</QtMocFileName> + <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(Filename).moc</QtMocFileName> + <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource> + <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource> + <OutputFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)moc\%(RecursiveDir)%(Filename).moc</OutputFile> + <OutputFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)moc\%(RecursiveDir)%(Filename).moc</OutputFile> + <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).moc</QtMocFileName> + <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName> + </QtMoc> + </ItemGroup> + <ItemGroup> + <ClInclude Include="astvisitor.h" /> + <ClInclude Include="vsqml.h" /> + <QtMoc Include="vsqmldebugclient.h" /> + <ClInclude Include="vsqml_global.h" /> + </ItemGroup> + <ItemGroup> + <None Include="README" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <Import Project="..\transform.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets" /> + <ItemGroup> + <ProjectReference Include="..\qtmsbuild\QtMSBuild.csproj"> + <Project>{A618D28B-9352-44F4-AA71-609BF68BF871}</Project> + </ProjectReference> + </ItemGroup> + <Target Name="VsQml_PreBuild"> + <ItemGroup> + <ProjectReference Remove="..\qtmsbuild\QtMSBuild.csproj"/> + </ItemGroup> + </Target> + <PropertyGroup> + <ResolveReferencesDependsOn>VsQml_PreBuild;$(ResolveReferencesDependsOn)</ResolveReferencesDependsOn> + </PropertyGroup> +</Project> \ No newline at end of file diff --git a/VsQml/vsqml.vcxproj.filters b/VsQml/vsqml.vcxproj.filters new file mode 100644 index 0000000..7fc74d0 --- /dev/null +++ b/VsQml/vsqml.vcxproj.filters @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="vsqml.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="astvisitor.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="vsqml.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="vsqml_global.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="astvisitor.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <QtMoc Include="vsqmldebugclient.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="vsqmldebugclient.cpp"> + <Filter>Source Files</Filter> + </QtMoc> + </ItemGroup> + <ItemGroup> + <None Include="README" /> + </ItemGroup> +</Project> diff --git a/VsQml/vsqml_global.h b/VsQml/vsqml_global.h new file mode 100644 index 0000000..5f724c1 --- /dev/null +++ b/VsQml/vsqml_global.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once + +#ifndef BUILD_STATIC +# if defined(VSQML_LIB) +# define VSQML_EXPORT __declspec(dllexport) +# else +# define VSQML_EXPORT __declspec(dllimport) +# endif +#else +# define VSQML_EXPORT +#endif diff --git a/VsQml/vsqmldebugclient.cpp b/VsQml/vsqmldebugclient.cpp new file mode 100644 index 0000000..eaf71ea --- /dev/null +++ b/VsQml/vsqmldebugclient.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "vsqmldebugclient.h" + +#include <QMutex> +#include <QMutexLocker> +#include <QTimer> + +#include <QtQmlDebug/private/qqmldebugclient_p.h> +#include <QtQmlDebug/private/qqmldebugconnection_p.h> +#include <QtPacketProtocol/private/qpacket_p.h> + +class VsQmlDebugClientPrivate : public QQmlDebugClient +{ + Q_OBJECT + +public: + VsQmlDebugClient *q; + QString hostName; + quint16 hostPort; + QTimer checkConnection; + + VsQmlDebugClientPrivate(VsQmlDebugClient *debugClient, QQmlDebugConnection *debugConnection) + : q(debugClient), + QQmlDebugClient("V8Debugger", debugConnection) + { + connect(this, &QQmlDebugClient::stateChanged, this, &VsQmlDebugClientPrivate::stateChanged); + + checkConnection.setInterval(1000); + checkConnection.setSingleShot(false); + connect(&checkConnection, &QTimer::timeout, + this, &VsQmlDebugClientPrivate::checkConnectionState); + } + + void messageReceived(const QByteArray &data) override; + +public slots: + void stateChanged(State state); + void checkConnectionState(); +}; + +VsQmlDebugClient::VsQmlDebugClient(QObject *parent) + : d(new VsQmlDebugClientPrivate(this, new QQmlDebugConnection(this))), + QObject(parent) +{ } + +VsQmlDebugClient::~VsQmlDebugClient() +{ + delete d; +} + +void VsQmlDebugClient::connectToHost(const QString &hostName, quint16 port) +{ + if (!d->connection()->isConnected()) { + d->hostName = hostName; + d->hostPort = port; + d->connection()->connectToHost(hostName, port); + } +} + +void VsQmlDebugClient::startLocalServer(const QString &fileName) +{ + if (!d->connection()->isConnected()) + d->connection()->startLocalServer(fileName); +} + +void VsQmlDebugClient::disconnectFromHost() +{ + d->connection()->close(); + emit disconnected(); +} + +void VsQmlDebugClient::sendMessage( + const QByteArray &messageType, + const QByteArray &messageParams) +{ + QByteArray packetType = "V8DEBUG"; + QPacket messageEnvelope(d->connection()->currentDataStreamVersion()); + messageEnvelope << packetType << messageType << messageParams; + + d->sendMessage(messageEnvelope.data()); +} + +void VsQmlDebugClientPrivate::messageReceived(const QByteArray &data) +{ + QPacket messageEnvelope(connection()->currentDataStreamVersion(), data); + QByteArray packetType; + messageEnvelope >> packetType; + if (packetType == "V8DEBUG") { + QByteArray messageType; + QByteArray messageParams; + messageEnvelope >> messageType >> messageParams; + emit q->messageReceived(messageType, messageParams); + } +} + +void VsQmlDebugClientPrivate::stateChanged(State state) +{ + switch (state) + { + case QQmlDebugClient::Unavailable: + case QQmlDebugClient::NotConnected: + emit q->disconnected(); + break; + case QQmlDebugClient::Enabled: + checkConnection.start(); + emit q->connected(); + break; + default: + break; + } +} + +void VsQmlDebugClientPrivate::checkConnectionState() +{ + if (!connection()->isConnected()) { + checkConnection.stop(); + emit q->disconnected(); + } +} + + +#include "vsqmldebugclient.moc" diff --git a/VsQml/vsqmldebugclient.h b/VsQml/vsqmldebugclient.h new file mode 100644 index 0000000..a04b149 --- /dev/null +++ b/VsQml/vsqmldebugclient.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#pragma once + +#include <QObject> + +class VsQmlDebugClientPrivate; + +class VsQmlDebugClient : public QObject +{ + Q_OBJECT + +public: + VsQmlDebugClient(QObject *parent = nullptr); + ~VsQmlDebugClient() override; + +public slots: + void connectToHost(const QString &hostName, quint16 port); + void startLocalServer(const QString &fileName); + void disconnectFromHost(); + void sendMessage(const QByteArray &messageType, const QByteArray &messageParams); + +signals: + void connected(); + void disconnected(); + void messageReceived(const QByteArray &messageType, const QByteArray &messageParams); + +private: + VsQmlDebugClientPrivate *d; + friend class VsQmlDebugClientPrivate; +}; diff --git a/common.tt b/common.tt new file mode 100644 index 0000000..7b47d86 --- /dev/null +++ b/common.tt @@ -0,0 +1,158 @@ +<#@template hostspecific="true" language="C#" #> +<#@assembly Name="System.Core" #> +<#@assembly Name="System.Xml" #> +<#@assembly Name="System.Xml.Linq" #> +<#@import namespace="System" #> +<#@import namespace="System.Collections.Generic" #> +<#@import namespace="System.IO" #> +<#@import namespace="System.Linq" #> +<#@import namespace="System.Text" #> +<#@import namespace="System.Xml" #> +<#@import namespace="System.Xml.Linq" #> +<#@assembly name="$(AssemblyRef_Microsoft_VisualStudio_Interop)" #> +<#@assembly name="$(AssemblyRef_EnvDTE)" #> +<#@import namespace="EnvDTE" #> +<#@assembly name="$(VsSdk_Shell_Interop)" #> +<#@import namespace="Microsoft.VisualStudio.Shell.Interop" #> +<# +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + DTE Dte = null; + try { + Dte = ((IServiceProvider)Host)?.GetService(typeof(DTE)) as DTE; + } catch { + } + + IVsShell VsShell = null; + try { + VsShell = ((IServiceProvider)Host)?.GetService(typeof(SVsShell)) as IVsShell; + } catch { + } + + string SolutionDir = Host?.ResolveParameterValue("-", "-", "SolutionDir"); + if (string.IsNullOrEmpty(SolutionDir)) { + if (Dte != null) + SolutionDir = Path.GetDirectoryName(Dte.Solution.FullName); + else + throw new Exception("Unable to obtain 'SolutionDir' parameter."); + } + + string DevEnvDir = Host?.ResolveParameterValue("-", "-", "DevEnvDir"); + if (string.IsNullOrEmpty(DevEnvDir)) { + if (VsShell != null) { + object vsShellPropValue = null; + VsShell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory, out vsShellPropValue); + DevEnvDir = vsShellPropValue as string; + if (string.IsNullOrEmpty(DevEnvDir)) + throw new Exception("Unable to obtain 'DevEnvDir' parameter."); + } else { + throw new Exception("Unable to obtain 'DevEnvDir' parameter."); + } + } + + string VS_VERSION = Host?.ResolveParameterValue("-", "-", "VisualStudioVersion"); + if (string.IsNullOrEmpty(VS_VERSION)) { + if (Dte != null) + VS_VERSION = Dte.Version; + else + throw new Exception("Unable to obtain 'VisualStudioVersion' parameter."); + } + + string VS_NAME = VS_VERSION.StartsWith("17.") ? "2022" : + VS_VERSION.StartsWith("16.") ? "2019" : + VS_VERSION.StartsWith("15.") ? "2017" : "????"; + + string VC_TARGETS_PATH = Host?.ResolveParameterValue("-", "-", "VCTargetsPath"); + + if (string.IsNullOrEmpty(VC_TARGETS_PATH) && !string.IsNullOrEmpty(VS_VERSION)) { + var proc = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles(x86)"), + @"Microsoft Visual Studio\Installer\vswhere.exe"), + Arguments = string.Format(@"-property installationPath -latest -Version [{0})", + VS_VERSION.StartsWith("17.") ? "17.0,18.0" : + VS_VERSION.StartsWith("16.") ? "16.0,17.0" : + VS_VERSION.StartsWith("15.") ? "15.0,16.0" : string.Empty), + WorkingDirectory = Directory.GetParent(SolutionDir).FullName, + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + }); + if (proc.WaitForExit(3000) && proc.ExitCode == 0) + VC_TARGETS_PATH = proc.StandardOutput.ReadLine(); + } + + if (string.IsNullOrEmpty(VC_TARGETS_PATH)) { + if (Dte != null && !string.IsNullOrEmpty(VS_VERSION)) { + Path.Combine(Environment.GetEnvironmentVariable( + VS_VERSION.StartsWith("17.") ? "ProgramFiles" : "ProgramFiles(x86)"), + $"Microsoft Visual Studio\\{VS_NAME}\\{Dte.Edition}", + VS_VERSION.StartsWith("17.") ? @"MSBuild\Microsoft\VC\v170" + : VS_VERSION.StartsWith("16.") ? @"MSBuild\Microsoft\VC\v160" + : VS_VERSION.StartsWith("15.") ? @"Common7\IDE\VC\VCTargets" : null); + } else { + throw new Exception("Unable to obtain 'VCTargetsPath' parameter."); + } + } + +/***************************************************************************/ + string WARNING_GENERATED_FILE = "This file was generated automatically."; + string XML_COMMENT_BEGIN = "<!--"; + string XML_COMMENT_END = "-->"; + + XML_COMMENT_BEGIN = XML_COMMENT_BEGIN.ToString(); + XML_COMMENT_END = XML_COMMENT_END.ToString(); + + var XmlLoad = new Func<string, XElement>(xmlText => + { + XDocument xmlDoc = null; + using (var reader = XmlReader.Create(new StringReader(xmlText))) + xmlDoc = XDocument.Load(reader); + var xmlRoot = xmlDoc.Root; + xmlRoot.Descendants().ToList().ForEach(x => x.Name = x.Name.LocalName); + return xmlRoot; + }); + + var XmlPrint = new Func<IEnumerable<XElement>, string>(x => + { + var xmlOut = new StringBuilder(); + var xmlOutSettings = new XmlWriterSettings() + { + ConformanceLevel = ConformanceLevel.Fragment, + OmitXmlDeclaration = true, + Indent = true, + }; + using (var xmlOutWriter = XmlWriter.Create(xmlOut, xmlOutSettings)) { + foreach (var y in x) + y.WriteTo(xmlOutWriter); + } + return xmlOut.ToString(); + }); + +#> \ No newline at end of file diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf new file mode 100644 index 0000000..1355559 --- /dev/null +++ b/doc/config/macros.qdocconf @@ -0,0 +1,4 @@ +macro.gui = "\\b" +macro.key = "\\b" +macro.QD = "\\e{Qt Designer}" +macro.QL = "\\e{Qt Linguist}" diff --git a/doc/config/qtvstools-project.qdocconf b/doc/config/qtvstools-project.qdocconf new file mode 100644 index 0000000..845c88c --- /dev/null +++ b/doc/config/qtvstools-project.qdocconf @@ -0,0 +1,45 @@ +include(macros.qdocconf) +include($QT_INSTALL_DOCS/global/qt-cpp-defines.qdocconf) +include($QT_INSTALL_DOCS/global/compat.qdocconf) +include($QT_INSTALL_DOCS/global/fileextensions.qdocconf) + +project = "QtVSTools" +description = "Qt VS Tools Manual" + +sourcedirs += .. +imagedirs += ../images +outputdir = $OUTDIR +exampledirs = ../tutorial + +qhp.projects = QtVSTools +qhp.QtVSTools.file = qtvstools.qhp +qhp.QtVSTools.namespace = io.qt.qtvstools +qhp.QtVSTools.virtualFolder = qtvstools +qhp.QtVSTools.indexTitle = Qt VS Tools Manual +qhp.QtVSTools.filterAttributes = qtvstools +qhp.QtVSTools.customFilters.QtVSTools.name = Qt VS Tools $QTVSTOOLS_VERSION +qhp.QtVSTools.customFilters.QtVSTools.filterAttributes = qtvstools $QTVSTOOLS_VERSION +qhp.QtVSTools.indexRoot = + +qhp.QtVSTools.subprojects = manual +qhp.QtVSTools.subprojects.manual.indexTitle = Qt VS Tools Manual +qhp.QtVSTools.subprojects.manual.title = Qt VS Tools Manual +qhp.QtVSTools.subprojects.manual.type = manual + +indexes += $QDOC_INDEX_DIR/qmake/qmake.index \ + $QDOC_INDEX_DIR/qtcore/qtcore.index \ + $QDOC_INDEX_DIR/qtdesigner/qtdesigner.index \ + $QDOC_INDEX_DIR/qtdoc/qtdoc.index \ + $QDOC_INDEX_DIR/qtgui/qtgui.index \ + $QDOC_INDEX_DIR/qthelp/qthelp.index \ + $QDOC_INDEX_DIR/qtlinguist/qtlinguist.index \ + $QDOC_INDEX_DIR/qtquick/qtquick.index \ + $QDOC_INDEX_DIR/qtwidgets/qtwidgets.index + +# Doxygen compatibility commands + +macro.see = "\\sa" +macro.function = "\\fn" + +navigation.homepage = "Qt VS Tools Manual" +buildversion = "Qt VS Tools $QTVSTOOLS_VERSION" diff --git a/doc/config/style/qt5-sidebar.html b/doc/config/style/qt5-sidebar.html new file mode 100644 index 0000000..60760ac --- /dev/null +++ b/doc/config/style/qt5-sidebar.html @@ -0,0 +1,15 @@ +<div class="sectionlist normallist"> + <div class="heading"> + <a name="reference"></a> + <h2 id="reference">Qt VS Tools Manual</h2> + </div> + <div class="indexboxcont indexboxbar"> + <ul> + <li><a href="qtvstools-getting-started.html">Getting Started</a></li> + <li><a href="qtvstools-managing-projects.html">Managing Projects</a></li> + <li><a href="qtvstools-form-files.html">Adding Form Files to Projects</a></li> + <li><a href="qtvstools-managing-resources.html">Managing Resources</a></li> + <li><a href="qtvstools-translation-files.html">Creating Qt Translation Files for Projects</a></li> + <li><a href="qtvstools-faq.html">FAQ</a></li> + </ul> + </div> diff --git a/doc/doc.pri b/doc/doc.pri new file mode 100644 index 0000000..8266367 --- /dev/null +++ b/doc/doc.pri @@ -0,0 +1,42 @@ +HELPGENERATOR = $$shell_path($$[QT_INSTALL_BINS]/qhelpgenerator) -platform minimal +QDOC_BIN = $$shell_path($$[QT_INSTALL_BINS]/qdoc) +QDOC_GLOBAL = QT_INSTALL_DOCS=$$[QT_INSTALL_DOCS/src] QDOC_INDEX_DIR=$$[QT_INSTALL_DOCS] +COMPAT = + +# unset the installdir for qdoc, so we force generation +# of URLs for the links to the Qt documentation +QMAKE_DOCS_INSTALLDIR = + +defineReplace(cmdEnv) { + !equals(QMAKE_DIR_SEP, /): 1 ~= s,^(.*)$,(set \\1) &&,g + return("$$1") +} + +defineReplace(qdoc) { + return("$$cmdEnv(OUTDIR=$$1 QTVSTOOLS_VERSION=$$QTVSTOOLS_VERSION QTVSTOOLS_VERSION_TAG=$$QTVSTOOLS_VERSION_TAG $$QDOC_GLOBAL) $$QDOC_BIN") +} + +QHP_FILE = $$OUT_PWD/doc/html/qtvstools.qhp +QCH_FILE = $$OUT_PWD/doc/qtvstools.qch + +HELP_DEP_FILES = $$PWD/src/qtvstools.qdoc $$PWD/src/externallinks.qdoc + +html_docs.commands = $$qdoc($$OUT_PWD/doc/html) $$PWD/qtvstools.qdocconf +html_docs.depends += $$HELP_DEP_FILES +html_docs.files = $$QHP_FILE + +html_docs_online.commands = $$qdoc($$OUT_PWD/doc/html) $$PWD/qtvstools-online.qdocconf +html_docs_online.depends += $$HELP_DEP_FILES + +qch_docs.commands = $$HELPGENERATOR -o \"$$QCH_FILE\" $$QHP_FILE +qch_docs.depends += html_docs + +docs_online.depends = html_docs_online +docs.depends = qch_docs +QMAKE_EXTRA_TARGETS += qch_docs html_docs html_docs_online docs docs_online + +DISTFILES += \ + $$HELP_DEP_FILES \ + $$PWD/config/qtvstools-project.qdocconf \ + $$PWD/qtvstools.qdocconf \ + $$PWD/qtvstools-online.qdocconf \ diff --git a/doc/images/qtvstools-addressbook-adddialog.png b/doc/images/qtvstools-addressbook-adddialog.png new file mode 100644 index 0000000..2237bba --- /dev/null +++ b/doc/images/qtvstools-addressbook-adddialog.png Binary files differ diff --git a/doc/images/qtvstools-addressbook-mainwindow.png b/doc/images/qtvstools-addressbook-mainwindow.png new file mode 100644 index 0000000..6b174be --- /dev/null +++ b/doc/images/qtvstools-addressbook-mainwindow.png Binary files differ diff --git a/doc/images/qtvstools-export-project.png b/doc/images/qtvstools-export-project.png new file mode 100644 index 0000000..dbbb440 --- /dev/null +++ b/doc/images/qtvstools-export-project.png Binary files differ diff --git a/doc/images/qtvstools-minus.png b/doc/images/qtvstools-minus.png new file mode 100644 index 0000000..33d8a05 --- /dev/null +++ b/doc/images/qtvstools-minus.png Binary files differ diff --git a/doc/images/qtvstools-plus.png b/doc/images/qtvstools-plus.png new file mode 100644 index 0000000..3c341d1 --- /dev/null +++ b/doc/images/qtvstools-plus.png Binary files differ diff --git a/doc/images/qtvstools-qrc-editor.png b/doc/images/qtvstools-qrc-editor.png new file mode 100644 index 0000000..2e5b201 --- /dev/null +++ b/doc/images/qtvstools-qrc-editor.png Binary files differ diff --git a/doc/images/qtvstools-qt-project-settings.png b/doc/images/qtvstools-qt-project-settings.png new file mode 100644 index 0000000..c7f7bc2 --- /dev/null +++ b/doc/images/qtvstools-qt-project-settings.png Binary files differ diff --git a/doc/images/qtvstools-qt-versions.png b/doc/images/qtvstools-qt-versions.png new file mode 100644 index 0000000..4adb108 --- /dev/null +++ b/doc/images/qtvstools-qt-versions.png Binary files differ diff --git a/doc/images/qtvstools-qt-widget-class-wizard.png b/doc/images/qtvstools-qt-widget-class-wizard.png new file mode 100644 index 0000000..e6a30dd --- /dev/null +++ b/doc/images/qtvstools-qt-widget-class-wizard.png Binary files differ diff --git a/doc/images/qtvstools-widgets-app-class.png b/doc/images/qtvstools-widgets-app-class.png new file mode 100644 index 0000000..84fb881 --- /dev/null +++ b/doc/images/qtvstools-widgets-app-class.png Binary files differ diff --git a/doc/images/qtvstools-widgets-app-modules.png b/doc/images/qtvstools-widgets-app-modules.png new file mode 100644 index 0000000..a3c2200 --- /dev/null +++ b/doc/images/qtvstools-widgets-app-modules.png Binary files differ diff --git a/doc/qtvstools-online.qdocconf b/doc/qtvstools-online.qdocconf new file mode 100644 index 0000000..95d3f7c --- /dev/null +++ b/doc/qtvstools-online.qdocconf @@ -0,0 +1,19 @@ +include(config/qtvstools-project.qdocconf) + +HTML.footer = \ + " </div>\n" \ + " <p class=\"copy-notice\">\n" \ + " <acronym title=\"Copyright\">©</acronym> 2016 The Qt Company Ltd.\n" \ + " Documentation contributions included herein are the copyrights of\n" \ + " their respective owners. " \ + " The documentation provided herein is licensed under the terms of the" \ + " <a href=\"http://www.gnu.org/licenses/fdl.html\">GNU Free Documentation" \ + " License version 1.3</a> as published by the Free Software Foundation. " \ + " Qt and respective logos are trademarks of The Qt Company Ltd " \ + " in Finland and/or other countries worldwide. All other trademarks are property\n" \ + " of their respective owners. </p>\n" + +include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) + +# Add an .html file with sidebar content, used in the online style +HTML.stylesheets += config/style/qt5-sidebar.html diff --git a/doc/qtvstools.qdocconf b/doc/qtvstools.qdocconf new file mode 100644 index 0000000..6c9a149 --- /dev/null +++ b/doc/qtvstools.qdocconf @@ -0,0 +1,2 @@ +include(config/qtvstools-project.qdocconf) +include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) diff --git a/doc/src/externallinks.qdoc b/doc/src/externallinks.qdoc new file mode 100644 index 0000000..12c0bf5 --- /dev/null +++ b/doc/src/externallinks.qdoc @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\externalpage http://download.qt.io/development_releases/vsaddin/ +\title Qt Downloads +*/ + +/*! +\externalpage https://account.qt.io/ +\title Qt Account +*/ + diff --git a/doc/src/qtvstools.qdoc b/doc/src/qtvstools.qdoc new file mode 100644 index 0000000..1b0eda9 --- /dev/null +++ b/doc/src/qtvstools.qdoc @@ -0,0 +1,827 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page index.html + \nextpage Getting Started + \title Qt VS Tools Manual + + Qt VS Tools integrate the Qt development tools into Microsoft Visual Studio + 2017, and later. This enables developers to use the standard Windows + development environment without having to worry about Qt-related build steps + or tools. You can install and update Qt VS Tools directly from Microsoft + Visual Studio. + + The main features of Qt VS Tools are: + + \list + \li Wizards for creating new Qt projects and classes. + \li Automated build setup for the \l {Using the Meta-Object Compiler + (moc)}{Meta-Object Compiler (moc)}, \l {User Interface Compiler + (uic)}, and \l {Resource Compiler (rcc)}. + \li Import and export of Qt project files (\c .pro) and project include + files (\c .pri). + \li Automated conversion of a Qt VS Tools project to a \l {qmake Manual} + {qmake} project, or the other way around. + \li Integrated Qt resource management. + \li Creating a Qt translation source (TS) file and starting Qt Linguist + to translate the strings. + \li Starting Qt Designer to design widget-based UIs for applications. + \li Integrated Qt documentation. + \li Debugging extensions for Qt data types. + \endlist + + \section1 Table of Contents + + \list + \li \l {Getting Started} + \li \l {Managing Qt Versions} + \li \l {Building} + \li \l {Cross-Compiling} + \li \l {Creating Projects} + \li \l {Importing and Exporting Projects} + \li \l {Adding Form Files to Projects} + \li \l {Managing Resources} + \li \l {Creating Qt Translation Files for Projects} + \li \l {Updating IntelliSense Info} + \li \l {Getting Help} + \endlist +*/ + +/*! + \page qtvstools-getting-started.html + \previouspage Qt VS Tools Manual + \nextpage Managing Qt Versions + \title Getting Started + + This tutorial illustrates how to use Qt VS Tools to create a \l {Qt Widgets} + application. You will create a project using a project wizard and design a + widget-based UI using \QD. In addition, you will learn how to convert a + Microsoft Visual Studio project file into a qmake compatible \c .pro file. + + You can use Qt VS Tools to develop also \l{Qt Quick} applications. + + \section1 Install Qt VS Tools + + In Microsoft Visual Studio, select \uicontrol Extensions > + \uicontrol {Manage Extensions} > \uicontrol Online, + and search for \uicontrol {Qt Visual Studio Tools} + to install or update Qt VS Tools. + + \section1 Add a Qt Version + + To create a Qt VS Tools project, you must add at least one + \l{Managing Qt Versions}{Qt version}. + + \section1 Create a Qt Widgets Application Project + + To create a Qt Widgets application project in Visual Studio: + + \list 1 + \li Select \uicontrol File > \uicontrol New > \uicontrol Project, and + search for \uicontrol {Qt Widgets Application}. + \li In the \uicontrol Name field, enter \e AddressBook, and then select + \uicontrol OK. + \li To acknowledge the \uicontrol Welcome dialog, select + \uicontrol Next. + \li Set up the \uicontrol Debug build configuration and select the + modules to include in the project: + \image qtvstools-widgets-app-modules.png "Selecting Qt modules in Qt Widgets Application Wizard" + The modules that are typically needed in widget application projects + are selected by default. + \li Select \uicontrol Next to continue to the class creation page: + \image qtvstools-widgets-app-class.png "Creating a class in Qt Widgets Application Wizard" + \li In the \uicontrol {Base class} field, enter \uicontrol QWidget as + the base class type. + \li Select the \uicontrol {Lower case filenames} check box to + only use lower case characters in the names of the generated files. + \li Select the \uicontrol {Precompiled header} check box to use a + precompiled header file. + \li Select the \uicontrol {Add default application icon} check box to + use a default application icon for the application. + \li Select \uicontrol Finish to create the project. + \endlist + + You now have a small working Qt application. Select \uicontrol Build > + \uicontrol {Build Solution} to build it, and then select \uicontrol Debug > + \uicontrol {Start Debugging} to run it. For now, the result is an empty + window. + + \section1 Design the Main Window + + You can use \QD to design the application's main window, which contains + some widgets placed in layouts: + + \image qtvstools-addressbook-mainwindow.png AddressBook's main dialog + + By default, \QD opens in Visual Studio. To open it as a stand-alone + application, select \uicontrol Detach. + + To run \QD in a separate window by default, select \uicontrol Extensions > + \uicontrol {Qt VS Tools} > \uicontrol Options > \uicontrol {\QD}, and set + \uicontrol {Run in detached window} to \uicontrol True. + + For more information about using \QD, see the \l{Qt Designer Manual}. + + \section2 Add Widgets + + To add widgets to the UI and to set properties for them: + + \list 1 + \li In Visual Studio's Solution Explorer, double-click the + \c addressbook.ui file to open it in \QD. + \li In \QD's \uicontrol Widgetbox, select \uicontrol {List Widget} and + drag and drop it to the form to add a \l QListWidget. + \li In the \uicontrol {Property Editor}, set the \c ObjectName property + to \e addressList. + \li Drag and drop two \uicontrol {Push Button} widgets to the top-right + corner of the form to add \l QPushButton objects for the + \uicontrol Add and \uicontrol Delete buttons. + \li Set the button names to \e addButton and \e deleteButton and + \uicontrol text property values to \e Add and \e Delete. + \li Drag and drop two \uicontrol {Label} widgets to the form to add + \l QLabel objects for displaying the selected item in the list. + \li Rename the first label to \e nameLabel and change its + \uicontrol text property to \e {<No item selected>}. + \li Rename the second label to \e emailLabel and leave its + \uicontrol text property empty. + \endlist + + Position the widgets approximately as they appear in the screenshot above. + In order to properly position the widgets and to ensure that they are + resized correctly when the form is resized, you need to add layouts to the + form. + + \section2 Add Widgets to Layouts + + You will need a vertical layout for the buttons as well as a spacer to push + the buttons to the top of the layout. In addition, you will need a second + layout to manage the positioning of the other widgets as well as the button + layout. + + To add wigdets to layouts: + + \list 1 + \li Drag a \uicontrol {Vertical Spacer} item to the form to add a + spacer. + \li Select the buttons and the spacer, and then select \uicontrol Form > + \uicontrol {Lay Out Vertically} to add a vertical layout + (\l QVBoxLayout). + \li Select the list widgets, the two labels, and the button layout, + and then select \uicontrol Form > \uicontrol {Lay Out in a Grid} to + add a grid layout (\l QGridLayout). + + \note Make sure that the labels are almost as wide as the form. + Otherwise, the grid layout will make them only as wide as the + address list. + \li Select \uicontrol Form > \uicontrol Preview to preview your form + without compiling it. + \li Select \uicontrol File > \uicontrol Save to save the form. + \endlist + + Build and run the application to check the main window. + + \section1 Add a Dialog + + Now that the main window is ready, you can move on to add functionality to + the application. To have the application open a dialog when the user clicks + the \uicontrol Add button, you must create an \uicontrol {Add Address} + dialog and invoke the dialog from a slot connected to the \uicontrol Add + button. + + You can use a Qt file wizard in Visual Studio to create a UI form that + contains the \uicontrol OK and \uicontrol Cancel buttons connected to the + QDialog::accept() and QDialog::reject() slots, respectively. You can use \QD + to add other widgets to the form. + + \section2 Create the Dialog + + To add a dialog to a project: + + \list 1 + \li In Visual Studio, select \uicontrol Project > + \uicontrol {Add Qt Class} > \uicontrol Installed > + \uicontrol {Visual C++} > \uicontrol Qt > + \uicontrol {Qt Widgets Class}. + \li To acknowledge the \uicontrol Welcome dialog, select + \uicontrol Next. + \li In the \uicontrol Name field, enter \e AddDialog. + \image qtvstools-qt-widget-class-wizard.png "Creating a class in Qt Widgets Class Wizard" + \li In the \uicontrol {Base class} field, enter \uicontrol QDialog as + the base class type. + \li Select the \uicontrol {Multiple inheritance} radio button. + \li Select the \uicontrol {Lower case filenames} check box to + only use lower case characters in the names of the generated files. + \li Select \uicontrol Finish to create source, header, and UI files for + the dialog. + \endlist + + \section2 Design the Dialog + + \image qtvstools-addressbook-adddialog.png Add Address Dialog + + To design the dialog: + + \list 1 + \li In Visual Studio's Solution Explorer, double-click the + \c adddialog.ui file to open it in \QD. + \li In \QD, set \e {Add Address} as the \uicontrol windowTitle. + \li Add a \uicontrol Label to the form and set its \uicontrol objectName + property to \e nameText and \uicontrol text property to \e {Name:}. + \li Add another \uicontrol Label and set its \uicontrol objectName + property to \e emailText and \uicontrol text property to + \e {Email:}. + \li Add a \uicontrol {Line Edit} (\l QLineEdit) and set its + \uicontrol objectName property to \e nameEdit. Leave the + \uicontrol text property empty. + \li Add another \uicontrol {Line Edit} and set its \uicontrol objectName + property to \e emailEdit. Leave the \uicontrol text property empty. + \li Select the labels and line edits, and then select \uicontrol Form > + \uicontrol {Lay Out in a Grid} to add a grid layout. + \li Add a \uicontrol {Push Button} and set its \uicontrol objectName + property to \e okButton and \uicontrol text property to \e OK. + \li Add a horizontal spacer to the left of the button. + \li Add a horizontal layout for the spacer and the button. + \li Add a vertical spacer between the labels and the button. + \li Add a vertical layout for the labels and the spacer. + \li Add a grid layout for both layouts. + \li Select \uicontrol Form > \uicontrol Preview to preview your form + without compiling it. + \li Select \uicontrol File > \uicontrol Save to save the form. + \endlist + + \section1 Connect to the Dialog's OK Button + + To have the \uicontrol OK button invoke the QDialog::accept() slot, click + the \uicontrol {Edit Signals/Slots} toolbar button to enter + \l {Qt Designer's Signals and Slots Editing Mode}. + + Click the \uicontrol OK button, drag the mouse cursor to an empty area of + the form, and release the mouse button. In the + \uicontrol {Configure Connection} dialog, connect the button's + QPushButton::clicked() signal to the form's QDialog::accept() slot. + + \section1 Open Dialogs from the Main Window + + To invoke the dialog when the user selects \uicontrol Add in the main + window, you must add a slot to the \c AddressBook class and invoke + \c AddDialog from this slot. + + Forms that are created using \QD call QMetaObject::connectSlotsByName() to + establish connections between signals emitted by the form's child widgets + and slots that follow the naming convention \c {on_<sender>_<signal>()}. + For the application to react appropriately when the \uicontrol Add button is + clicked, you must implement a slot called \c{on_addButton_clicked()}. + + To implement the slot, open the \c addressbook.h file in Visual Studio and + add a declaration for the slot: + + \badcode + private slots: + void on_addButton_clicked(); + \endcode + + Then open \c addressbook.cpp and add the slot definition: + + \badcode + void AddressBook::on_addButton_clicked() + { + AddDialog dialog(this); + dialog.exec(); + } + \endcode + + To connect to some other signal, you must add the signal to the + \c AddressBook class. This requires editing both the header file, + \c addressbook.h, and the implementation file, \c addressbook.cpp. + + Include \c adddialog.h to \c addressbook.cpp: + + \quotefromfile AddressBook/addressbook.cpp + \skipto adddialog + \printuntil adddialog.h + + To test your changes, build and run the application. Select the + \uicontrol Add button to open the \uicontrol {Add Address} dialog, and then + select \uicontrol OK to close it. + + \section1 Add Items to the List Widget + + When the user selects \uicontrol OK, an item should be added to the + QListWidget. To implement this function, modify the code in the + \c {on_addButton_clicked()} slot, as follows: + + \skipto AddDialog dialog(this); + \printuntil } + \printuntil } + + The dialog is executed. If the user accepts it by selecting \uicontrol OK, + the \uicontrol Name and \uicontrol Email fields are extracted and a + QListWidgetItem that contains the specified information is created. + + \section1 Display the Selected Item + + When the user selects an item in the list widget, the \c nameLabel and + \c emailLabel at the bottom of the form should be updated. This behavior + requires another slot to be added to the \c AddressBook class. + + In the \c addressbook.h file, add the following code in the + \c{private slots} section of the class: + + \quotefromfile AddressBook/addressbook.h + \skipto on_addressList_currentItemChanged() + \printuntil ; + + Then, add the block of code below to \c addressbook.cpp: + + \quotefromfile AddressBook/addressbook.cpp + \skipto on_addressList_currentItemChanged() + \printuntil /^\}/ + + Thanks to the naming convention, this slot will automatically be connected + to the QListWidget::currentItemChanged() signal of \c{addressList} and + invoked whenever the selected item in the list changes. + + \section1 Add Functionality for the Delete Button + + To implement a slot for the \uicontrol Delete button, open the + \c addressbook.h file in Visual Studio and add a declaration for the + \c on_deleteButton_clicked() slot. Then open \c addressbook.cpp and add the + slot definition for \c on_deleteButton_clicked(). + + Type the following code in the slot's body: + + \skipto on_deleteButton_clicked() + \printuntil /^\}/ + + Your application is now complete. + + \section1 Create Qt Project Files + + To build the application on other platforms, you need to create a \c .pro + file for the project. + + To let Qt VS Tools create a basic \c .pro file for you: + + \list 1 + \li Select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Create Basic .pro File}. + \li In the \uicontrol {Export Project} dialog, make sure that the + \uicontrol {Create .pri file} check box is selected, and then select + \uicontrol OK. + \li Select \uicontrol Save to use the default location and name for + saving the \c .pri file. + \endlist + + For more information about \c .pro files and their associated \c .pri files, + see \l {Creating Projects}. + + You should now have a working \c .pro file and \c .pri file for your + project. For more complex projects, manually editing the \c .pro file + is required to make it work on all platforms. However, for the example + project, the generated \c .pro file is sufficient. +*/ + +/*! + \page qtvstools-creating-projects.html + \previouspage Cross-Compiling + \nextpage Importing and Exporting Projects + \title Creating Projects + + Once you have installed Qt VS Tools, you can search for and then select + the following project templates in Visual Studio in \uicontrol File > + \uicontrol New \uicontrol Project: + + \list + \li \uicontrol {Qt ActiveQt Server} creates a simple ActiveX server. + \li \uicontrol {Qt Class Library} creates a basic Qt-based dynamic + library. + \li \uicontrol {Qt Console Application} creates a basic Qt console + application. + \li \uicontrol {Qt Designer Custom Widget} creates a \QD custom widget + plugin. + \li \uicontrol {Qt Empty Application} creates a an empty Qt application + project. + \li \uicontrol {Qt Quick Application} creates a Qt Quick application + project. + \li \uicontrol {Qt Widgets Application} creates a simple Qt widgets + application with one form. + \endlist + + Each Qt project provides a wizard that allows you to specify the Qt modules + required for your project. For some projects, the wizard will also generate + a skeleton class or QML type instance that you can use to get started. + + To start writing Qt code and building your projects, you must tell Visual + Studio where to find the \l{Managing Qt Versions}{Qt version} that you want + to use. +*/ + +/*! + \page qtvstools-managing-qt-versions.html + \previouspage Getting Started + \nextpage Building + \title Managing Qt Versions + + Qt VS Tools provide you with basic Qt version management that enables you to + use multiple Qt versions in parallel. For example, Qt 5.15.4 and 6.2.0. + You have to add at least one Qt version to be able to create Qt projects. + + \image qtvstools-qt-versions.png Qt Versions + + To manage Qt versions: + + \list 1 + \li Select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Qt Versions} > \inlineimage qtvstools-plus.png + . + \li In the \uicontrol Version field, enter a name for the Qt version. + If you add the path to Qt first, this field is filled automatically. + \li In the \uicontrol Host field, select \uicontrol Windows, unless you + want to cross-compile the application for an embedded Linux device. + \li In the \uicontrol Path field, enter the path to the qmake.exe for + the Qt version. + For example: \c {C:\Qt\6.2.0\msvc2019_64\bin\qmake.exe}. + \li In the \uicontrol Default field, select the Qt version to be used + by default when creating new projects or importing a project from a + \c {.pro} file. + \li Select \uicontrol OK. + \endlist + + To remove a Qt version, select \inlineimage qtvstools-minus.png + . + + To specify Qt versions for a project, right-click the project in Solution + Explorer and select \uicontrol Properties in the context menu. Select + \uicontrol {Qt Project Settings}, and set the Qt version as the value of + the \uicontrol {Qt Installation} option. +*/ + +/*! + \page qtvstools-building.html + \previouspage Managing Qt Versions + \nextpage Cross-Compiling + \title Building + + Qt Visual Studio Tools includes MSBuild rules and targets specific to + \l {Using the Meta-Object Compiler (moc)}{Meta-Object Compiler (moc)}, + \l {User Interface Compiler (uic)}, and \l {Resource Compiler (rcc)}. + These rules are referred to as \e Qt/MSBuild. They enable running moc, + rcc and uic in parallel processes and setting build options, such as + selecting project items to process and the tools to process them. + Further, source files generated by the Qt tools are added to the C++ + compilation during the build process, so that generated files do not + need to be included in projects. + + The options for each Qt tool can be set either globally at project level + or for specific files. To set global options for handling files at project + level: + + \list + \li Select \uicontrol Properties in the context menu of the project. + \li Select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Qt Project Settings}. + \endlist + + \image qtvstools-qt-project-settings.png "Qt Project Settings" + + To set options for handling a specific file, select \uicontrol Properties + in the context menu of the file. Only tools relevant for the file type are + listed. For example, moc options are available for C++ source and header + files, whereas uic options are available for UI forms (.ui). + + When importing .pro files, \e {custom build steps} are automatically + converted into Qt/MSBuild steps. To convert custom build steps into + Qt/MSBuild steps for a solution, select \uicontrol Qt > + \uicontrol {Convert custom build steps to Qt/MSBuild} in the context + menu of the solution. + + To set the path to the Qt/MSBuild files, select \uicontrol Extensions > + \uicontrol {Qt VS Tools} > \uicontrol Options > \uicontrol {Qt/MSBuild}. + This corresponds to setting the value of the \c QTMSBUILDS environment + variable. +*/ + +/*! + \page qtvstools-cross-compiling.html + \previouspage Building + \nextpage Creating Projects + \title Cross-Compiling + + Qt VS Tools supports cross-compilation of Qt projects by integrating + Qt/MSBuild and the Linux development VS workload. This enables you to + develop widget-based and Qt Quick applications that target both Windows + and Linux, including embedded devices. + + To develop Qt applications for Linux using Visual Studio: + + \list + \li Install the Desktop development with C++ and Linux development with + C++ workloads. + \li Enable the Windows Subsystem for Linux (WSL) and install a + Linux distribution. + \li Alternatively, create a connection to a remote Linux build server. + \li Register the cross-compiled Qt build that resides in a WSL instance + or the remote build server. + \endlist + + To add a Qt version for cross-compilation: + + \list 1 + \li Select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Qt Versions} > \inlineimage qtvstools-plus.png + to add a new Qt version. + \li In the \uicontrol Version field, enter a name for the Qt version. + If you add the path to Qt first, this field is filled automatically. + \li In \uicontrol Host, select \uicontrol {Linux WSL} or + \uicontrol {Linux SSH}. + \li In the \uicontrol Path field, enter the path to the Qt installation. + \li In the \uicontrol Compiler field, enter the cross-compiler to use. + The default value is g++. + \endlist + + \section1 Compiling on WSL + + Using a WSL instance to run the cross-compiler is faster than using an + SSH connection to a build host. When using SSH, MSBuild must first upload + the source files to the remote host, whereas WSL mounts local drives in + \c /mnt, making file access instantaneous. + + MSBuild assumes that paths specified in options are local paths and maps + them to corresponding Linux paths rooted on \c /mnt. For example: + + \badcode + C:\include\foolib maps to /mnt/c/include/foolib + \endcode + + Using absolute Linux paths as value of \uicontrol Extensions > + \uicontrol {Qt VS Tools} > \uicontrol Options > \uicontrol {Qt/MSBuild} + can lead to incorrect mappings. For example, \c {/home/user/raspi} maps + to \c {/mnt/home/user/raspi}. + + To avoid this issue, create symbolic links in \c /mnt to other top-level + directories in the WSL instance file system. +*/ + +/*! + \page qtvstools-importing-and-exporting-projects.html + \previouspage Creating Projects + \nextpage Adding Form Files to Projects + \title Importing and Exporting Projects + + Qt and Visual Studio use different file formats to save projects. If you + build your application on multiple platforms, you probably already use + Qt \c .pro files with \l {qmake Manual}{qmake}. Otherwise, you might use + \c .vcproj files and Visual Studio to build your project, which is usually + more convenient for Windows-only development. + + Qt VS Tools provide a way to combine both approaches so that you do not have + to manually maintain \c .pro files and \c .vcproj files in parallel. You + start by creating a \c .vcproj file, as usual. When you need a qmake + \c .pro file for storing Linux and macOS specific settings, select + \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Create Basic .pro File}. + + \image qtvstools-export-project.png Export Projects dialog + + If you have multiple Qt projects in one Visual Studio solution, the basic + \c .pro file generator can create a master \c .pro file of type \c subdirs + that includes all of the projects. + + The generated \c .pro file is not meant to be a complete file, but a simple + starting point for porting to other platforms. However, these files + should be sufficient for compiling the skeleton projects created by the + predefined Qt project wizards. The \c .pro file includes a \c .pri file. + + The \c .pri file contains the list of source files, header files, \c .ui + files, and \c .qrc files in the project. To generate the \c .pri file, + select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Export Project to .pri File}. + When you add or remove a file from the Visual Studio project file, you must + regenerate the \c .pri file to ensure that the application can still be + built on other platforms. + + Also, make sure that the \c .pri file included in the \c .pro file points to + the correct file. If you saved the \c .pri file using the suggested path and + name, this reference should be correct. + + Developers on platforms other than Windows might add or remove files to the + project by editing the \c .pri file. When this happens, Windows developers + must select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Import .pri File to Project} to synchronize the Visual Studio + project file with the \c .pri file. + + If your project contains platform-specific source files, you should list + them in the \c .pro file so that they are not overwritten by Qt VS Tools. + + To summarize, a cross-platform Qt project consists of the following files: + + \list + \li A \c .vcproj file containing Windows-specific settings and listing + the files in the project. + \li A \c .pro file containing Linux and macOS specific settings. + \li A \c .pri file (a \c qmake include file) listing the files in the + project. + \endlist + + If you already have a \c .pro file but no \c .vcproj file, select + \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Open Qt Project File (.pro)} to + convert your \c .pro file to a \c .vcproj file. The generated \c .vcproj + only contains Windows-specific settings. Also, there is no other way to + convert a \c .vcproj file back to the \c .pro file format than using the + \uicontrol {Create Basic .pro File} function. +*/ + +/*! + \page qtvstools-form-files.html + \previouspage Importing and Exporting Projects + \nextpage Managing Resources + \title Adding Form Files to Projects + + You can start \QD from Qt VS Tools by double-clicking a \c .ui file. For + more information about using \QD, see the \l{Qt Designer Manual}. + + To add a new \c .ui file to the project, select \uicontrol Project > + \uicontrol {Add Qt Class} > \uicontrol Installed > \uicontrol {Visual C++} > + \uicontrol Qt > \uicontrol {Qt Widget Class}. + + \image qtvstools-qt-widget-class-wizard.png + + Typically, you need to wrap the code generated for the form file into a + QWidget subclass to add signals or slots to it. Therefore, Qt VS Tools + create a \c .h and \c .cpp file for you, along with an empty UI form. +*/ + +/*! + \page qtvstools-managing-resources.html + \previouspage Adding Form Files to Projects + \nextpage Creating Qt Translation Files for Projects + \title Managing Resources + + Adding new resources to a Qt project is similar to adding resources to a + normal C++ project. The main difference is that \c .qrc files (Qt + resource files) are used instead of Windows \c .rc files. Unlike \c .rc + files, \c .qrc files work on all platforms supported by Qt and are trivial + to load from Qt code. + + \image qtvstools-qrc-editor.png Qt Resource Editor dialog + + By default, Qt Resource Editor opens in Visual Studio. To run it in a + separate window, select \uicontrol Detach. + + To run \uicontrol {Qt Resource Editor} in a separate window by default, + select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol Options > \uicontrol {Qt Resource Editor}, and + set \uicontrol {Run in detached window} to \uicontrol True. + + To add new resources: + + \list 1 + \li Select \uicontrol Project > \uicontrol {Add New Item} > + \uicontrol Installed > \uicontrol {Visual C++} > \uicontrol Qt > + \uicontrol {Qt Resource File}. + \li In the \uicontrol Name field, enter a name for the resource file. + \li In the \uicontrol Location field, specify a location for the file. + \li Select \uicontrol Add to create a \c .qrc file and to open it in the + \uicontrol {Qt Resource Editor}. + \li To add resources to the file, select \uicontrol Add > + \uicontrol {Add Files}. + \li In the \uicontrol Prefix field, you can change the prefix. + \endlist + + When referring to the resources later on from Qt code, you must prepend the + prefix, the file name, and a colon. For example, \c{:/MyProject/print.bmp}. + + To add prefixes to the \c .qrc file, select \uicontrol Add > + \uicontrol {Add Prefix} in the \uicontrol {Qt Resource Editor}. + + For more information, see \l{The Qt Resource System}. +*/ + +/*! + \page qtvstools-translation-files.html + \previouspage Managing Resources + \nextpage Updating IntelliSense Info + \title Creating Qt Translation Files for Projects + + To add a new translation file to the project: + + \list 1 + \li Select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Create New Translation File}. + \li In the \uicontrol Language field, select a language from the list of + supported languages. + \li In the \uicontrol Filename field, enter a filename for the + translation file. + \li Select \uicontrol OK to create the file and have it listed in + \uicontrol {Translation Files} in Visual Studio's Solution Explorer. + \li Right-click a translation file to open a context menu with options + for running \c lupdate and \c lrelease. + \endlist + + To start \QL, select \uicontrol Extensions > \uicontrol {Qt VS Tools} > + \uicontrol {Launch Qt Linguist} or double-click a \c .ts file in the + Solution Explorer. + + By default, \QL opens in Visual Studio. To open it as a stand-alone + application, select \uicontrol Detach. + + To run \QL in a separate window by default, select \uicontrol Extensions > + \uicontrol {Qt VS Tools} > \uicontrol Options > \uicontrol {\QL}, and set + \uicontrol {Run in detached window} to \uicontrol True. + + For more information about Qt's translation tools, see the + \l {Qt Linguist Manual}. +*/ + +/*! + \page qtvstools-getting-help.html + \previouspage Updating IntelliSense Info + \title Getting Help + + By default, Qt VS Tools tries to display Qt online documentation when you + press \key F1. If you often end up in the Qt documentation even though you + wanted Microsoft documentation, you can turn off this feature and use the + default shortcut \key {Alt+F1}. Select \uicontrol Extensions > + \uicontrol {Qt VS Tools} > \uicontrol Options > \uicontrol Help, and set + \uicontrol {Try Qt documentation when F1 is pressed} to \uicontrol False + + To read the documentation delivered with your Qt version instead of the + latest online version, set \uicontrol {Preferred source} to + \uicontrol Offline. + + To set up another keyboard shortcut for Qt online help: + + \list 1 + \li Select \uicontrol Tools > \uicontrol Options > + \uicontrol Environment > \uicontrol Keyboard. + \li In the \uicontrol {Show commands containing} field, enter + \e {Help.F1Help}. + \li Select the \uicontrol {Press shortcut keys} field and press your + preferred keys to add them to the field. + \li Select \uicontrol Assign, and then select \uicontrol OK. + \endlist +*/ + +/*! + \page qtvstools-intellisense-info.html + \previouspage Creating Qt Translation Files for Projects + \nextpage Getting Help + \title Updating IntelliSense Info + + Visual Studio provides IntelliSense code editing features for C++ types + including code completion, parameter info, and member lists. + + To keep IntelliSense info updated also with code generated by + Qt tools, such as \l {Using the Meta-Object Compiler (moc)}{moc}, + \l {User Interface Compiler (uic)}{uic}, or \l {Resource Compiler (rcc)} + {rcc}, they are run in the background when source files change. If this + creates too much overhead for large solutions, you can disable the + \uicontrol {Run Qt tools in background build} option to only run + qmake and update a minimal set of Qt build properties. + + If you disable the option and code completion does not work, try to rebuild + the project. You might have to wait some time before code completion fully + works after updating a source file. If rebuilding does not help, refresh the + code model by selecting \uicontrol {Rescan Solution} in the context menu of + the project. + + To set options for updating IntelliSense info, select \uicontrol Extensions + > \uicontrol {Qt VS Tools} > \uicontrol Options > \uicontrol IntelliSense: + + \list + \li \uicontrol {Auto project tracking} tracks changes and triggers a + background build of Qt targets when necessary. + \li \uicontrol {Show debug information} displays debug information about + IntelliSense info updates. + \li \uicontrol {Verbosity of background build log} determines the amount + of info recorded in the background build log. + \endlist + +*/ + diff --git a/doc/tutorial/AddressBook/AddressBook.ico b/doc/tutorial/AddressBook/AddressBook.ico new file mode 100644 index 0000000..1c4fb80 --- /dev/null +++ b/doc/tutorial/AddressBook/AddressBook.ico Binary files differ diff --git a/doc/tutorial/AddressBook/AddressBook.pro b/doc/tutorial/AddressBook/AddressBook.pro new file mode 100644 index 0000000..ef6b065 --- /dev/null +++ b/doc/tutorial/AddressBook/AddressBook.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +INCLUDEPATH += . +TARGET = AddressBook +QT += core gui widgets + +HEADERS += adddialog.h \ + addressbook.h +SOURCES += main.cpp \ + adddialog.cpp \ + addressbook.cpp + +RC_FILE += AddressBook.rc +FORMS += adddialog.ui addressbook.ui diff --git a/doc/tutorial/AddressBook/AddressBook.rc b/doc/tutorial/AddressBook/AddressBook.rc new file mode 100644 index 0000000..54e9a28 --- /dev/null +++ b/doc/tutorial/AddressBook/AddressBook.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "AddressBook.ico" + diff --git a/doc/tutorial/AddressBook/adddialog.cpp b/doc/tutorial/AddressBook/adddialog.cpp new file mode 100644 index 0000000..667938a --- /dev/null +++ b/doc/tutorial/AddressBook/adddialog.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSensors module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "adddialog.h" + +AddDialog::AddDialog(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); +} + +AddDialog::~AddDialog() +{ +} diff --git a/doc/tutorial/AddressBook/adddialog.h b/doc/tutorial/AddressBook/adddialog.h new file mode 100644 index 0000000..68b312d --- /dev/null +++ b/doc/tutorial/AddressBook/adddialog.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSensors module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <QDialog> +#include "ui_adddialog.h" + +class AddDialog : public QDialog, public Ui::AddDialog +{ + Q_OBJECT + +public: + AddDialog(QWidget *parent = Q_NULLPTR); + ~AddDialog(); +}; diff --git a/doc/tutorial/AddressBook/adddialog.ui b/doc/tutorial/AddressBook/adddialog.ui new file mode 100644 index 0000000..e64c9ad --- /dev/null +++ b/doc/tutorial/AddressBook/adddialog.ui @@ -0,0 +1,126 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>AddDialogClass</class> + <widget class="QDialog" name="AddDialogClass" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>321</width> + <height>113</height> + </rect> + </property> + <property name="windowTitle" > + <string>AddDialog</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="1" column="1" > + <widget class="QLineEdit" name="emailEdit" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="emailText" > + <property name="text" > + <string>Email:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="nameEdit" /> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="nameText" > + <property name="text" > + <string>Name:</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>OK</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <tabstops> + <tabstop>nameEdit</tabstop> + <tabstop>emailEdit</tabstop> + <tabstop>okButton</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>okButton</sender> + <signal>clicked()</signal> + <receiver>AddDialogClass</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>290</x> + <y>92</y> + </hint> + <hint type="destinationlabel" > + <x>201</x> + <y>59</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/doc/tutorial/AddressBook/addressbook.cpp b/doc/tutorial/AddressBook/addressbook.cpp new file mode 100644 index 0000000..7864e48 --- /dev/null +++ b/doc/tutorial/AddressBook/addressbook.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSensors module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "addressbook.h" +#include "adddialog.h" + +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); +} + +AddressBook::~AddressBook() +{ +} + +void AddressBook::on_addButton_clicked() +{ + AddDialog dialog(this); + + if (dialog.exec()) { + QString name = dialog.nameEdit->text(); + QString email = dialog.emailEdit->text(); + + if (!name.isEmpty() && !email.isEmpty()) { + QListWidgetItem *item = new QListWidgetItem(name, ui.addressList); + item->setData(Qt::UserRole, email); + ui.addressList->setCurrentItem(item); + } + } +} + +void AddressBook::on_addressList_currentItemChanged() +{ + QListWidgetItem *curItem = ui.addressList->currentItem(); + + if (curItem) { + ui.nameLabel->setText("Name: " + curItem->text()); + ui.emailLabel->setText("Email: " + curItem->data(Qt::UserRole).toString()); + } else { + ui.nameLabel->setText("<No item selected>"); + ui.emailLabel->clear(); + } +} + +void AddressBook::on_deleteButton_clicked() +{ + QListWidgetItem *curItem = ui.addressList->currentItem(); + + if (curItem) { + int row = ui.addressList->row(curItem); + ui.addressList->takeItem(row); + delete curItem; + + if (ui.addressList->count() > 0) + ui.addressList->setCurrentRow(0); + else + on_addressList_currentItemChanged(); + } +} diff --git a/doc/tutorial/AddressBook/addressbook.h b/doc/tutorial/AddressBook/addressbook.h new file mode 100644 index 0000000..40dffb4 --- /dev/null +++ b/doc/tutorial/AddressBook/addressbook.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSensors module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#pragma once + +#include <QWidget> +#include "ui_addressbook.h" + +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = 0); + ~AddressBook(); + +private: + Ui::AddressBookClass ui; + +private slots: + void on_deleteButton_clicked(); + void on_addButton_clicked(); + void on_addressList_currentItemChanged(); +}; diff --git a/doc/tutorial/AddressBook/addressbook.ui b/doc/tutorial/AddressBook/addressbook.ui new file mode 100644 index 0000000..c72d99b --- /dev/null +++ b/doc/tutorial/AddressBook/addressbook.ui @@ -0,0 +1,86 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>AddressBookClass</class> + <widget class="QWidget" name="AddressBookClass" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>365</width> + <height>256</height> + </rect> + </property> + <property name="windowTitle" > + <string>AddressBook</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="1" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QPushButton" name="addButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deleteButton" > + <property name="text" > + <string>Delete</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="2" column="0" colspan="2" > + <widget class="QLabel" name="emailLabel" /> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QLabel" name="nameLabel" > + <property name="text" > + <string><No items selected></string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QListWidget" name="addressList" /> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <pixmapfunction></pixmapfunction> + <tabstops> + <tabstop>addressList</tabstop> + <tabstop>addButton</tabstop> + <tabstop>deleteButton</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/doc/tutorial/AddressBook/main.cpp b/doc/tutorial/AddressBook/main.cpp new file mode 100644 index 0000000..3f3822e --- /dev/null +++ b/doc/tutorial/AddressBook/main.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSensors module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include "addressbook.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + AddressBook w; + w.show(); + return a.exec(); +} diff --git a/references.props b/references.props new file mode 100644 index 0000000..0f4a231 --- /dev/null +++ b/references.props @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- + ///////////////////////////////////////////////////////////////////////////////////////////////// + // References versions + // --> + <!-- // Common --> + <PropertyGroup> + <Version_Microsoft_Bcl_AsyncInterfaces + >5.0.0</Version_Microsoft_Bcl_AsyncInterfaces> + <Version_Stub_System_Data_SQLite_Core_NetFramework + >1.0.115</Version_Stub_System_Data_SQLite_Core_NetFramework> + <Version_System_Collections_Immutable + >5.0.0</Version_System_Collections_Immutable> + <Version_System_ComponentModel_Composition + >5.0.0</Version_System_ComponentModel_Composition> + <Version_Newtonsoft_Json + >13.0.1</Version_Newtonsoft_Json> + <Version_MSTest_TestAdapter + >2.2.8</Version_MSTest_TestAdapter> + <Version_MSTest_TestFramework + >2.2.8</Version_MSTest_TestFramework> + </PropertyGroup> + <PropertyGroup> + <Name_DummyPackage + >Stub.System.Data.SQLite.Core.NetFramework</Name_DummyPackage> + <Version_DummyPackage + >$(Version_Stub_System_Data_SQLite_Core_NetFramework)</Version_DummyPackage> + </PropertyGroup> + <!-- // Visual Studio 2022 --> + <PropertyGroup Condition="'$(VisualStudioVersion)'=='17.0'"> + <Version_Microsoft_Build + >16.11.0</Version_Microsoft_Build> + <Version_Microsoft_Build_Framework + >16.11.0</Version_Microsoft_Build_Framework> + <Version_Microsoft_Build_Tasks_Core + >16.11.0</Version_Microsoft_Build_Tasks_Core> + <Version_Microsoft_VisualStudio_SDK + >17.0.0-previews-4-31709-430</Version_Microsoft_VisualStudio_SDK> + <Version_Microsoft_VSSDK_BuildTools + >17.0.5232</Version_Microsoft_VSSDK_BuildTools> + <Version_Microsoft_VisualStudio_ProjectSystem + >17.0.667-pre</Version_Microsoft_VisualStudio_ProjectSystem> + <Version_Microsoft_VisualStudio_Validation + >17.0.34</Version_Microsoft_VisualStudio_Validation> + <Version_Microsoft_VisualStudio_Shell_Framework + >17.0.0-previews-5-31722-452</Version_Microsoft_VisualStudio_Shell_Framework> + <Name_Microsoft_VisualStudio_VCProjectEngine + >Microsoft.VisualStudio.VCProjectEngine</Name_Microsoft_VisualStudio_VCProjectEngine> + <Version_Microsoft_VisualStudio_VCProjectEngine + >17.0.0-previews-4-31709-430</Version_Microsoft_VisualStudio_VCProjectEngine> + <Name_Microsoft_VisualStudio_TemplateWizardInterface + >Microsoft.VisualStudio.TemplateWizardInterface</Name_Microsoft_VisualStudio_TemplateWizardInterface> + <Version_Microsoft_VisualStudio_TemplateWizardInterface + >17.0.0-previews-1-31410-258</Version_Microsoft_VisualStudio_TemplateWizardInterface> + <Name_Microsoft_VisualStudio_Threading + >$(Name_DummyPackage)</Name_Microsoft_VisualStudio_Threading> + <Version_Microsoft_VisualStudio_Threading + >$(Version_DummyPackage)</Version_Microsoft_VisualStudio_Threading> + <Version_System_Collections_Immutable + >5.0.0</Version_System_Collections_Immutable> + </PropertyGroup> + <!-- // Visual Studio 2019 --> + <PropertyGroup Condition="'$(VisualStudioVersion)'=='16.0'"> + <Version_Microsoft_Build + >16.8.0</Version_Microsoft_Build> + <Version_Microsoft_Build_Framework + >16.8.0</Version_Microsoft_Build_Framework> + <Version_Microsoft_Build_Tasks_Core + >16.8.0</Version_Microsoft_Build_Tasks_Core> + <Version_Microsoft_VisualStudio_SDK + >16.0.206</Version_Microsoft_VisualStudio_SDK> + <Version_Microsoft_VSSDK_BuildTools + >16.4.1060</Version_Microsoft_VSSDK_BuildTools> + <Version_Microsoft_VisualStudio_ProjectSystem + >16.2.133-pre</Version_Microsoft_VisualStudio_ProjectSystem> + <Version_Microsoft_VisualStudio_Validation + >16.8.33</Version_Microsoft_VisualStudio_Validation> + <Version_Microsoft_VisualStudio_Shell_Framework + >16.4.29519.181</Version_Microsoft_VisualStudio_Shell_Framework> + <Name_Microsoft_VisualStudio_VCProjectEngine + >Microsoft.VisualStudio.VCProjectEngine</Name_Microsoft_VisualStudio_VCProjectEngine> + <Version_Microsoft_VisualStudio_VCProjectEngine + >16.7.30328.74</Version_Microsoft_VisualStudio_VCProjectEngine> + <Name_Microsoft_VisualStudio_TemplateWizardInterface + >$(Name_DummyPackage)</Name_Microsoft_VisualStudio_TemplateWizardInterface> + <Version_Microsoft_VisualStudio_TemplateWizardInterface + >$(Version_DummyPackage)</Version_Microsoft_VisualStudio_TemplateWizardInterface> + <Name_Microsoft_VisualStudio_Threading + >$(Name_DummyPackage)</Name_Microsoft_VisualStudio_Threading> + <Version_Microsoft_VisualStudio_Threading + >$(Version_DummyPackage)</Version_Microsoft_VisualStudio_Threading> + <Version_System_Collections_Immutable + >1.5.0</Version_System_Collections_Immutable> + </PropertyGroup> + <!-- // Visual Studio 2017 --> + <PropertyGroup Condition="'$(VisualStudioVersion)'=='15.0'"> + <Version_Microsoft_Build + >15.9.20</Version_Microsoft_Build> + <Version_Microsoft_Build_Framework + >15.9.20</Version_Microsoft_Build_Framework> + <Version_Microsoft_Build_Tasks_Core + >15.9.20</Version_Microsoft_Build_Tasks_Core> + <Version_Microsoft_VisualStudio_SDK + >15.0.1</Version_Microsoft_VisualStudio_SDK> + <Version_Microsoft_VSSDK_BuildTools + >15.9.3039</Version_Microsoft_VSSDK_BuildTools> + <Version_Microsoft_VisualStudio_ProjectSystem + >15.8.243</Version_Microsoft_VisualStudio_ProjectSystem> + <Version_Microsoft_VisualStudio_Validation + >15.5.31</Version_Microsoft_VisualStudio_Validation> + <Version_Microsoft_VisualStudio_Shell_Framework + >15.9.28307</Version_Microsoft_VisualStudio_Shell_Framework> + <Name_Microsoft_VisualStudio_TemplateWizardInterface + >$(Name_DummyPackage)</Name_Microsoft_VisualStudio_TemplateWizardInterface> + <Version_Microsoft_VisualStudio_TemplateWizardInterface + >$(Version_DummyPackage)</Version_Microsoft_VisualStudio_TemplateWizardInterface> + <Name_Microsoft_VisualStudio_Threading + >Microsoft.VisualStudio.Threading</Name_Microsoft_VisualStudio_Threading> + <Version_Microsoft_VisualStudio_Threading + >15.8.209</Version_Microsoft_VisualStudio_Threading> + <Version_System_Collections_Immutable + >1.5.0</Version_System_Collections_Immutable> + </PropertyGroup> +</Project> diff --git a/transform.targets b/transform.targets new file mode 100644 index 0000000..f74d4d5 --- /dev/null +++ b/transform.targets @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Condition="'$(VisualStudioVersion)'=='17.0'" + Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v17.0\TextTemplating\Microsoft.TextTemplating.targets" /> + <Import Condition="'$(VisualStudioVersion)'=='16.0'" + Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\TextTemplating\Microsoft.TextTemplating.targets" /> + <Import Condition="'$(VisualStudioVersion)'=='15.0'" + Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\TextTemplating\Microsoft.TextTemplating.targets" /> + <PropertyGroup> + <TransformOnBuild>true</TransformOnBuild> + <TransformOutOfDateOnly>true</TransformOutOfDateOnly> + <AssemblyRef_Microsoft_VisualStudio_Interop + Condition="'$(AssemblyRef_Microsoft_VisualStudio_Interop)' == '' + AND ( + '$(VisualStudioVersion)' != '17.0' + )" + >System</AssemblyRef_Microsoft_VisualStudio_Interop> + <AssemblyRef_Microsoft_VisualStudio_Interop + Condition="'$(AssemblyRef_Microsoft_VisualStudio_Interop)' == '' + AND ( + '$(VisualStudioVersion)' == '17.0' + )" + >$(VsInstallRoot)\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Interop.dll</AssemblyRef_Microsoft_VisualStudio_Interop> + <AssemblyRef_EnvDTE + Condition="'$(AssemblyRef_EnvDTE)' == ''" + >$(VsInstallRoot)\Common7\IDE\PublicAssemblies\EnvDTE.dll</AssemblyRef_EnvDTE> + <VsSdk_Shell_Interop + Condition="'$(VsSdk_Shell_Interop)' == ''" + >$(VsInstallRoot)\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.Shell.Interop.dll</VsSdk_Shell_Interop> + </PropertyGroup> + <Target Name="PrepareTransform"> + <ItemGroup> + <T4TransformInputs Include="@(T4Template)"/> + <T4ParameterValues Include="SolutionDir"> + <Value>$(SolutionDir)</Value> + <Visible>false</Visible> + </T4ParameterValues> + <T4ParameterValues Include="DevEnvDir"> + <Value>$(DevEnvDir)</Value> + <Visible>false</Visible> + </T4ParameterValues> + <T4ParameterValues Include="Configuration"> + <Value>$(Configuration)</Value> + <Visible>false</Visible> + </T4ParameterValues> + <T4ParameterValues Include="VisualStudioVersion"> + <Value>$(VisualStudioVersion)</Value> + <Visible>false</Visible> + </T4ParameterValues> + <T4ParameterValues Include="VCTargetsPath"> + <Value>$(VCTargetsPath)</Value> + <Visible>false</Visible> + </T4ParameterValues> + <T4ParameterValues Include="AssemblyRef_EnvDTE"> + <Value>$(AssemblyRef_EnvDTE)</Value> + <Visible>false</Visible> + </T4ParameterValues> + <T4ParameterValues Include="AssemblyRef_Microsoft_VisualStudio_Interop"> + <Value>$(AssemblyRef_Microsoft_VisualStudio_Interop)</Value> + <Visible>false</Visible> + </T4ParameterValues> + <T4ParameterValues Include="VsSdk_Shell_Interop"> + <Value>$(VsSdk_Shell_Interop)</Value> + <Visible>false</Visible> + </T4ParameterValues> + </ItemGroup> + </Target> + <PropertyGroup> + <BeforeTransform>$(BeforeTransform);PrepareTransform</BeforeTransform> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '15.0'"> + <DefineConstants>$(DefineConstants);VS2017</DefineConstants> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '16.0'"> + <DefineConstants>$(DefineConstants);VS2019</DefineConstants> + </PropertyGroup> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '17.0'"> + <DefineConstants>$(DefineConstants);VS2022</DefineConstants> + </PropertyGroup> +</Project> diff --git a/version.targets b/version.targets new file mode 100644 index 0000000..d6516b3 --- /dev/null +++ b/version.targets @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup Condition="'$(QtVSToolsVersion)' == ''"> + <QtVSToolsVersion>2.8.1</QtVSToolsVersion> + </PropertyGroup> +</Project> diff --git a/version.tt b/version.tt new file mode 100644 index 0000000..73ba168 --- /dev/null +++ b/version.tt @@ -0,0 +1,81 @@ +<#@include file="common.tt" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="System.Xml" #> +<#@ assembly name="System.Xml.Linq" #> +<#@ assembly name="System.Linq" #> +<#@ assembly name="System.Data.Linq" #> +<#@ import namespace="System.Diagnostics" #> +<#@ import namespace="System.IO" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Xml" #> +<#@ import namespace="System.Xml.Linq" #> +<# +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt VS Tools. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + Func<string> GetRevisionData = new Func<string>(() => + { + var proc = System.Diagnostics.Process.Start(new ProcessStartInfo + { + FileName = "git", + Arguments = "describe --tags", + WorkingDirectory = Directory.GetParent(SolutionDir).FullName, + CreateNoWindow = true, UseShellExecute = false, + RedirectStandardOutput = true, RedirectStandardError = true, + }); + return proc.WaitForExit(3000) && proc.ExitCode == 0 ? proc.StandardOutput.ReadLine() : ""; + }); + string REVISION_TAG = GetRevisionData(); + int REVISION_NUMBER = Convert.ToInt32(REVISION_TAG.Split('-').Skip(1).FirstOrDefault()); + +/***************************************************************************/ + + string QT_VS_TOOLS_VERSION = (string)XDocument.Load(SolutionDir + @"\version.targets") + .Descendants() + .Where(x => x.Name.LocalName == "QtVSToolsVersion") + .FirstOrDefault(); + +/***************************************************************************/ + + string QT_VS_TOOLS_VERSION_TAG + = QT_VS_TOOLS_VERSION.Replace(".", ""); + + string QT_VS_TOOLS_VERSION_ASSEMBLY + = string.Format("{0}.{1}", QT_VS_TOOLS_VERSION, REVISION_NUMBER); + + string QT_VS_TOOLS_VERSION_ASSEMBLY_FILE + = string.Format("{0}.{1}", QT_VS_TOOLS_VERSION, REVISION_NUMBER); + + string QT_VS_TOOLS_VERSION_USER + = REVISION_NUMBER > 0 + ? string.Format("{0} (rev.{1})", QT_VS_TOOLS_VERSION, REVISION_NUMBER) + : QT_VS_TOOLS_VERSION; + + string QT_VS_TOOLS_VERSION_MANIFEST + = REVISION_NUMBER > 0 ? QT_VS_TOOLS_VERSION_ASSEMBLY : QT_VS_TOOLS_VERSION; +#> \ No newline at end of file diff --git a/vstools.bat b/vstools.bat new file mode 100644 index 0000000..ad05fd5 --- /dev/null +++ b/vstools.bat @@ -0,0 +1,417 @@ +@ECHO OFF +SETLOCAL + +SET SCRIPT=%~n0 +SET ROOT=%CD% + +SET TRUE="0"=="0" +SET FALSE="0"=="1" +SET ALL="tokens=* usebackq" + +SET VSWHERE_EXE="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" +SET VSWHERE=%VSWHERE_EXE% +SET VSWHERE=%VSWHERE:(=^(% +SET VSWHERE=%VSWHERE:)=^)% +SET QUERY=-latest -prerelease +SET VSWHERE_MAJOR=2 +SET VSWHERE_MINOR=7 +SET VSWHERE_PATCH=1 +SET VSWHERE_VERSION=%VSWHERE_MAJOR%.%VSWHERE_MINOR%.%VSWHERE_PATCH% + +SET DEPENDENCIES=QtVsTools_RegExpr;QtMsBuild:TransformAll + +SET VS2022="-version [17.0^,18.0^)" +SET VS2019="-version [16.0^,17.0^)" +SET VS2017="-version [15.0^,16.0^)" +SET VS_ALL=%VS2022%,%VS2019%,%VS2017% +SET VS_LATEST="-all" + +SET VS_VERSIONS= +SET VS_VERSIONS_DEFAULT=%TRUE% +SET VERBOSE=%FALSE% +SET REBUILD=%FALSE% +SET INIT=%FALSE% +SET CLEAN=%FALSE% +SET BINARYLOG=%FALSE% +SET CONFIGURATION=Release +SET DO_INSTALL=%FALSE% +SET TRANSFORM_INCREMENTAL=true +SET START_VS=%FALSE% + +SET PLATFORM_VS2017="Any CPU" +SET PLATFORM_VS2019="Any CPU" +SET PLATFORM_VS2022="x64" + +SET FLAG_VS2022=-vs2022 +SET FLAG_VS2019=-vs2019 +SET FLAG_VS2017=-vs2017 + +REM /////////////////////////////////////////////////////////////////////////////////////////////// +REM // Process command line arguments +:parseArgs + +SET NEXT_ARG=%2 +SET NEXT_ARG_FIRST_TOKEN=%NEXT_ARG:~0,1% + +IF NOT "%1"=="" ( + IF "%1"=="-init" ( + SET INIT=%TRUE% + ) ELSE IF "%1"=="-rebuild" ( + SET REBUILD=%TRUE% + ) ELSE IF "%1"=="-config" ( + IF NOT "%NEXT_ARG_FIRST_TOKEN%"=="-" ( + IF "%NEXT_ARG%"=="" ( + ECHO Unknown argument '%2' 1>&2 + GOTO :usage + ) + SET CONFIGURATION=%NEXT_ARG% + SHIFT + ) + ) ELSE IF "%1"=="-deploy" ( + SET QtVSToolsDeployTarget=%~f2 + SHIFT + ) ELSE IF "%1"=="-install" ( + SET DO_INSTALL=%TRUE% + ) ELSE IF "%1"=="-build" ( + REM NOOP + ) ELSE IF "%1"=="-verbose" ( + SET VERBOSE=%TRUE% + ) ELSE IF "%1"=="-bl" ( + SET BINARYLOG=%TRUE% + ) ELSE IF "%1"=="-startvs" ( + SET START_VS=%TRUE% + ) ELSE IF "%1"=="-version" ( + SET VS_VERSIONS=%VS_VERSIONS%,"-version [%2^,%2.65535]" + SET VS_LATEST="-version [%2^,%2.65535]" + SHIFT + SET VS_LATEST="-version [%2^,%2.65535]" + SET VS_VERSIONS_DEFAULT=%FALSE% + ) ELSE IF "%1"=="%FLAG_VS2022%" ( + SET VS_VERSIONS=%VS_VERSIONS%,%VS2022% + SET VS_LATEST=%VS2022% + SET VS_VERSIONS_DEFAULT=%FALSE% + SET FLAG_VS2022= + ) ELSE IF "%1"=="%FLAG_VS2019%" ( + SET VS_VERSIONS=%VS_VERSIONS%,%VS2019% + SET VS_LATEST=%VS2019% + SET VS_VERSIONS_DEFAULT=%FALSE% + SET FLAG_VS2019= + ) ELSE IF "%1"=="%FLAG_VS2017%" ( + SET VS_VERSIONS=%VS_VERSIONS%,%VS2017% + SET VS_LATEST=%VS2017% + SET VS_VERSIONS_DEFAULT=%FALSE% + SET FLAG_VS2017= + ) ELSE IF "%1"=="-help" ( + GOTO :usage + ) ELSE ( + ECHO Unknown argument '%1' 1>&2 + GOTO :usage + ) + SHIFT + GOTO :parseArgs +) + +IF NOT EXIST vstools.sln ( + ECHO Error: could not find Qt VS Tools solution file. + EXIT /B 1 +) + +IF %INIT% ( + SET CLEAN=%TRUE% + SET MSBUILD_TARGETS=Clean + SET MSBUILD_EXTRAS=%MSBUILD_EXTRAS% -p:CleanDependsOn=TransformDuringBuild + SET VS_VERSIONS=%VS_LATEST% + SET DO_INSTALL=%FALSE% + SET TRANSFORM_INCREMENTAL=false +) ELSE IF %REBUILD% ( + SET CLEAN=%TRUE% + SET INIT=%TRUE% + SET MSBUILD_TARGETS=Rebuild + IF %VS_VERSIONS_DEFAULT% SET VS_VERSIONS=%VS_ALL% + SET TRANSFORM_INCREMENTAL=false +) ELSE ( + SET MSBUILD_TARGETS=Build + IF %VS_VERSIONS_DEFAULT% SET VS_VERSIONS=%VS_ALL% +) + +IF %VERBOSE% ( + SET MSBUILD_VERBOSITY=normal +) ELSE ( + SET MSBUILD_VERBOSITY=minimal +) + +IF %BINARYLOG% ( + SET MSBUILD_EXTRAS=%MSBUILD_EXTRAS% -bl +) + +REM /////////////////////////////////////////////////////////////////////////////////////////////// +REM // Check requirements + +IF %VERBOSE% ECHO ## root: %ROOT% + +IF NOT EXIST vstools.sln ( + ECHO Error: could not find Qt VS Tools solution file. + EXIT /B 1 +) + +IF NOT EXIST %VSWHERE_EXE% ( + ECHO Error: could not find Visual Studio Locator tool. + EXIT /B 1 +) + +FOR /F %ALL% %%v IN (`"%VSWHERE% -help"`) DO ( + SET VSWHERE_LOGO=%%v + GOTO :break_vswhere_help +) +:break_vswhere_help + +IF %VERBOSE% ECHO ## vswhere: %VSWHERE_LOGO% + +SET VSWHERE_OK=%TRUE% +FOR /F "tokens=5,6,7 delims=.+ " %%v IN ("%VSWHERE_LOGO%") DO ( + IF %%v LSS %VSWHERE_MAJOR% ( + SET VSWHERE_OK=%FALSE% + ) ELSE IF %%v EQU %VSWHERE_MAJOR% ( + IF %%w LSS %VSWHERE_MINOR% ( + SET VSWHERE_OK=%FALSE% + ) ELSE IF %%w EQU %VSWHERE_MINOR% IF %%x LSS %VSWHERE_PATCH% ( + SET VSWHERE_OK=%FALSE% + ) + ) +) +IF NOT %VSWHERE_OK% ( + ECHO Error: Visual Studio Locator version must be %VSWHERE_VERSION% or greater. + EXIT /B 1 +) + +REM /////////////////////////////////////////////////////////////////////////////////////////////// +REM // Cycle through installed VS products + +IF %VERBOSE% ECHO ## VS versions: %VS_VERSIONS% + +FOR %%v IN (%VS_VERSIONS%) DO ( + SETLOCAL + + IF %VERBOSE% ECHO ## Querying VS version: %%v + + IF %VERBOSE% ECHO ## %VSWHERE% %QUERY% %%~v -property installationPath + FOR /F %ALL% %%p IN (`"%VSWHERE% %QUERY% %%~v -property installationPath"`) DO ( + IF %VERBOSE% ECHO ## installationPath: %%p + + IF %VERBOSE% ECHO ## %VSWHERE% -path "%%p" -property catalog_productLineVersion + FOR /F %ALL% %%e IN (`"%VSWHERE% -path "%%p" -property catalog_productLineVersion"`) DO ( + IF %VERBOSE% ECHO ## catalog_productLineVersion: %%e + + IF %VERBOSE% ECHO ## %VSWHERE% -path "%%p" -property displayName + FOR /F %ALL% %%n IN (`"%VSWHERE% -path "%%p" -property displayName"`) DO ( + IF %VERBOSE% ECHO ## displayName: %%n + + IF %VERBOSE% ECHO ## %VSWHERE% -path "%%p" -property installationVersion + FOR /F %ALL% %%i IN (`"%VSWHERE% -path "%%p" -property installationVersion"`) DO ( + IF %VERBOSE% ECHO ## installationVersion: %%i + + FOR /F %ALL% %%f IN (`CMD /C "ECHO %%PLATFORM_VS%%e%%"`) DO ( + IF %VERBOSE% ECHO ## platform: %%f + + IF "%%e"=="2022" ( + IF %VERBOSE% ECHO ## CALL "%%p\VC\Auxiliary\Build\vcvars64.bat" + CALL "%%p\VC\Auxiliary\Build\vcvars64.bat" > NUL + ) ELSE ( + IF %VERBOSE% ECHO ## CALL "%%p\VC\Auxiliary\Build\vcvars32.bat" + CALL "%%p\VC\Auxiliary\Build\vcvars32.bat" > NUL + ) + + ECHO ################################################################################ + ECHO ## %%n ^(%%i^) + WHERE /Q msbuild && ( + FOR /F %ALL% %%m IN (`msbuild -version -nologo`) DO ( + ECHO ## msbuild v%%m + ) + ) || ( + ECHO Error: msbuild not found + EXIT /B 3 + ) + IF NOT "%QtVSToolsDeployTarget%"=="" ECHO ## Deploy to: %QtVSToolsDeployTarget% + ECHO ################################################################################ + ECHO. + + IF NOT %INIT% ( + IF NOT %REBUILD% ( + IF %START_VS% ( + ECHO ############################################################################### + ECHO ## Starting Visual Studio... + ECHO ############################################################################### + devenv vstools.sln + EXIT /B 0 + ))) + + IF %CLEAN% ( + ECHO ############################################################################### + ECHO ## Deleting output files... + ECHO ############################################################################### + RD /S /Q bin > NUL 2>&1 + FOR /F %ALL% %%d IN (`DIR /A:D /B /S bin 2^> NUL`) DO ( + RD /S /Q %%d > NUL 2>&1 + ) + RD /S /Q obj > NUL 2>&1 + FOR /F %ALL% %%d IN (`DIR /A:D /B /S obj 2^> NUL`) DO ( + RD /S /Q %%d > NUL 2>&1 + ) + ECHO. + + ECHO ############################################################################### + ECHO ## Restoring packages... + ECHO ############################################################################### + msbuild ^ + -nologo ^ + -verbosity:%MSBUILD_VERBOSITY% ^ + -t:Restore ^ + && ( + ECHO. + ) || ( + ECHO ############################################################################### + ECHO ## ERROR restoring packages! 1>&2 + ECHO ############################################################################### + EXIT /B %ERRORLEVEL% + ) + ) + + IF %INIT% ( + ECHO ################################################################################ + ECHO ## Building pre-requisites... + IF %VERBOSE% ( + ECHO ## msbuild: vstools.sln + ECHO ## msbuild: -t:%DEPENDENCIES% + ECHO ## msbuild: -p:Configuration=%CONFIGURATION% + ECHO ## msbuild: -p:Platform=%%f + ECHO ## msbuild: -p:TransformOutOfDateOnly=false + ECHO ## msbuild extras: %MSBUILD_EXTRAS% + ) + ECHO ################################################################################ + msbuild ^ + -nologo ^ + -verbosity:%MSBUILD_VERBOSITY% ^ + -maxCpuCount ^ + -t:%DEPENDENCIES% ^ + -p:Configuration=%CONFIGURATION% ^ + -p:Platform=%%f ^ + -p:TransformOutOfDateOnly=false ^ + %MSBUILD_EXTRAS% ^ + vstools.sln ^ + && ( + ECHO. + ) || ( + ECHO ############################################################################### + ECHO ## ERROR building pre-requisites 1>&2 + ECHO ############################################################################### + EXIT /B %ERRORLEVEL% + ) + ) + + ECHO ################################################################################ + ECHO ## msbuild: vstools.sln + ECHO ## msbuild: -t:%MSBUILD_TARGETS% + ECHO ## msbuild: -p:Configuration=%CONFIGURATION% + ECHO ## msbuild: -p:Platform=%%f + IF %VERBOSE% ( + ECHO ## msbuild: -p:TransformOutOfDateOnly=%TRANSFORM_INCREMENTAL% + ECHO ## msbuild: -verbosity:%MSBUILD_VERBOSITY% + ECHO ## msbuild extras: %MSBUILD_EXTRAS% + ) + ECHO ################################################################################ + msbuild ^ + -nologo ^ + -verbosity:%MSBUILD_VERBOSITY% ^ + -maxCpuCount ^ + -p:Configuration=%CONFIGURATION% ^ + -p:Platform=%%f ^ + -p:TransformOutOfDateOnly=%TRANSFORM_INCREMENTAL% ^ + -t:%MSBUILD_TARGETS% ^ + %MSBUILD_EXTRAS% ^ + vstools.sln ^ + && ( + ECHO ################################################################################ + ECHO ## %%n ^(%%i^) + ECHO ## Solution build successful. + ECHO ################################################################################ + ECHO. + ) || ( + ECHO ################################################################################ + ECHO ## %%n ^(%%i^) + ECHO ## ERROR building solution 1>&2 + ECHO ################################################################################ + EXIT /B %ERRORLEVEL% + ) + + IF %DO_INSTALL% ( + ECHO ################################################################################ + ECHO ## Installing VSIX package + ECHO ################################################################################ + ECHO Removing previous installation... + IF "%%e"=="2022" ( + VSIXInstaller /uninstall:QtVsTools.8e827d74-6fc4-40a6-a3aa-faf19652b3b8 + ) ELSE IF "%%e"=="2019" ( + VSIXInstaller /uninstall:QtVsTools.bf3c71c0-ab41-4427-ada9-9b3813d89ff5 + ) ELSE IF "%%e"=="2017" ( + VSIXInstaller /uninstall:QtVsTools.13121978-cd02-4fd0-89bd-e36f85abe16a + ) + ECHO Installing... + VSIXInstaller QtVsTools.Package\bin\Release\QtVsTools.vsix + ECHO. + ) + + IF %START_VS% ( + ECHO ############################################################################### + ECHO ## Starting Visual Studio... + ECHO ############################################################################### + devenv vstools.sln + EXIT /B 0 + ) + + ECHO. + ) + )))) + ENDLOCAL +) + +EXIT /B 0 + +:usage + +ECHO Usage: +ECHO. +ECHO %SCRIPT% [VS Versions] [Operation] [Options] +ECHO. +ECHO == 'VS Versions' can be one or more of the following: +ECHO -vs2022 ................ Select the latest version of Visual Studio 2022 +ECHO -vs2019 ................ Select the latest version of Visual Studio 2019 +ECHO -vs2017 ................ Select the latest version of Visual Studio 2017 +ECHO -version ^<X^>.^<Y^> ....... Select version X.Y of Visual Studio +ECHO Can be specified several times +ECHO. +ECHO If no version is specified, the most recent version of VS is selected. +ECHO. +ECHO == 'Operation' can be one of the following: +ECHO -build ......... Incremental build of solution +ECHO -rebuild ....... Clean build of solution +ECHO -init .......... Initialize vstools solution for the specified version of VS +ECHO If multiple versions are specified, the last one is selected +ECHO -startvs ....... Open vstools solution in selected VS version +ECHO. +ECHO If no operation is specified, -build is assumed by default. +ECHO. +ECHO == 'Options' can be one or more of the following +ECHO -config ^<CONFIG_NAME^> ....... Select CONFIG_NAME as the build configuration +ECHO Defaults to the 'Release' configuration +ECHO Only valid with -build or -rebuild +ECHO -deploy ^<DEPLOY_DIR^> ........ Deploy installation package to DEPLOY_DIR +ECHO Only valid with -build or -rebuild +ECHO -install .................... Install extension to selected VS version(s) +ECHO Only valid with -build or -rebuild +ECHO -startvs .................... Open vstools solution in selected VS version +ECHO -verbose .................... Print more detailed log information +ECHO -bl ......................... Generate MSBuild binary log +ECHO Only valid with -build or -rebuild +ECHO. + +EXIT /B 1 diff --git a/vstools.pri_TT b/vstools.pri_TT new file mode 100644 index 0000000..4fcee29 --- /dev/null +++ b/vstools.pri_TT @@ -0,0 +1,39 @@ +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# <#@output extension="pri" #> +# <#@include file="$(SolutionDir)\version.tt" #> +# <#=WARNING_GENERATED_FILE#> +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +!isEmpty(QTVSOOLS_PRI) { + error("vstools.pri already included") +} +QTVSTOOLS_PRI = 1 +QTVSTOOLS_VERSION = <#=QT_VS_TOOLS_VERSION#> +QTVSTOOLS_VERSION_TAG = <#=QT_VS_TOOLS_VERSION_TAG#> + +defineTest(minQtVersion) { + maj = $$1 + min = $$2 + patch = $$3 + isEqual(QT_MAJOR_VERSION, $$maj) { + isEqual(QT_MINOR_VERSION, $$min) { + isEqual(QT_PATCH_VERSION, $$patch) { + return(true) + } + greaterThan(QT_PATCH_VERSION, $$patch) { + return(true) + } + } + greaterThan(QT_MINOR_VERSION, $$min) { + return(true) + } + } + greaterThan(QT_MAJOR_VERSION, $$maj) { + return(true) + } + return(false) +} + +!minQtVersion(5, 6, 0) { + message("Cannot build Qt VS Tools with Qt version $${QT_VERSION}.") + error("Use at least Qt 5.6.0.") +} diff --git a/vstools.pro b/vstools.pro new file mode 100644 index 0000000..728afb0 --- /dev/null +++ b/vstools.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +include(vstools.pri) +include(doc/doc.pri) diff --git a/vstools.sln b/vstools.sln new file mode 100644 index 0000000..8bbcdd4 --- /dev/null +++ b/vstools.sln @@ -0,0 +1,609 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31717.71 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtVsTools.Package", "QtVsTools.Package\QtVsTools.Package.csproj", "{671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtVsTools.RegExpr", "QtVsTools.RegExpr\QtVsTools.RegExpr.csproj", "{A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtMSBuild", "QtMSBuild\QtMSBuild.csproj", "{A618D28B-9352-44F4-AA71-609BF68BF871}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtVsTools.Core", "QtVsTools.Core\QtVsTools.Core.csproj", "{2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtVsTools.Wizards", "QtVsTools.Wizards\QtVsTools.Wizards.csproj", "{8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Templates", "Templates", "{9D9290B2-9E87-46EA-84EA-02836F699BB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Project.Console", "Templates\console\QtTemplate.Project.Console.csproj", "{71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Project.Designer", "Templates\designer\QtTemplate.Project.Designer.csproj", "{191B2B91-D5FD-4138-A72A-AB0B149988C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Item.DialogButtonBottom", "Templates\dialogbuttonbottom\QtTemplate.Item.DialogButtonBottom.csproj", "{F7407750-5F72-460F-9C53-27CF509A39B1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Item.DialogButtonRight", "Templates\dialogbuttonright\QtTemplate.Item.DialogButtonRight.csproj", "{F2166B59-E41B-4328-B31D-9E2B9AC5A59C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Project.Empty", "Templates\empty\QtTemplate.Project.Empty.csproj", "{73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Project.Gui", "Templates\gui\QtTemplate.Project.Gui.csproj", "{3E1F9741-01D3-480A-A484-3CD373AE4A7F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Project.Lib", "Templates\lib\QtTemplate.Project.Lib.csproj", "{191B1B91-D9FD-4138-A72A-AB0B149958C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Item.MainWindow", "Templates\mainwindow\QtTemplate.Item.MainWindow.csproj", "{20055427-1352-44FB-8442-BF7F15F9C59E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Item.QMLFile", "Templates\qml\QtTemplate.Item.QMLFile.csproj", "{DC1AE91B-45CE-4C5B-8F77-CDB58566038F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Item.QMLDir", "Templates\qmldir\QtTemplate.Item.QMLDir.csproj", "{7AF6C34B-65D2-4010-92F6-420E59DDE9BF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Project.Quick", "Templates\quick\QtTemplate.Project.Quick.csproj", "{4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Item.Resource", "Templates\resource\QtTemplate.Item.Resource.csproj", "{BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Project.Server", "Templates\server\QtTemplate.Project.Server.csproj", "{8AE9D385-A379-4F5F-A703-3DF643DA6742}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtTemplate.Item.Widget", "Templates\widget\QtTemplate.Item.Widget.csproj", "{40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Item", "Item", "{A7918293-56E9-465A-AE1C-0724576ADD66}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project", "Project", "{DD307619-BF80-4E5D-AE54-196057187702}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{9B109DDA-0521-46AD-B087-B7CBCB33FEE5}" + ProjectSection(SolutionItems) = preProject + QtCppConfig.props = QtCppConfig.props + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VsQml", "VsQml\vsqml.vcxproj", "{B12702AD-ABFB-343A-A199-8E24837244A3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmakefilereader", "qmakefilereader\qmakefilereader.vcxproj", "{70711A97-D9B0-3A86-9756-9FF47337908B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qrceditor", "qrceditor\qrceditor.vcxproj", "{4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Text Generation", "Text Generation", "{58DB9025-019B-44AC-A206-6F1B2D2AECD8}" + ProjectSection(SolutionItems) = preProject + common.tt = common.tt + transform.targets = transform.targets + version.tt = version.tt + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Deployment", "Deployment", "{66ED34F1-F316-49BE-9137-C7AAF22115FF}" + ProjectSection(SolutionItems) = preProject + Changelog = Changelog + version.targets = version.targets + vstools.bat = vstools.bat + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{F96D578B-D78E-47F4-86DA-FE472B9942F0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{97DAEAD7-3D10-459C-8920-94E48C43AA5F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QtVsTest", "QtVsTest\QtVsTest.csproj", "{48A50432-6BDF-4DE2-A3AD-3A237D31E49D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D6FB29A4-8921-46F5-B170-B15538AB4D69}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test_QtVsTools.RegExpr", "Tests\Test_QtVsTools.RegExpr\Test_QtVsTools.RegExpr.csproj", "{D574EFED-5E19-45BE-9B05-310F65065303}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test_QtVsTools.PriorityQueue", "Tests\Test_QtVsTools.PriorityQueue\Test_QtVsTools.PriorityQueue.csproj", "{A5320606-37B8-4F15-97E2-16314109CAF9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleTest", "Tests\SampleTest\SampleTest.vcxproj", "{12857847-9877-466C-B056-DD286A219093}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C7F47BF-D12B-46BA-934B-CDC9C462D116}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test_QtMsBuild.Tasks", "Tests\Test_QtMsBuild.Tasks\Test_QtMsBuild.Tasks.csproj", "{E809DDE3-AE76-4F7A-8DC5-775AC4900138}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + Tests|Any CPU = Tests|Any CPU + Tests|x64 = Tests|x64 + Tests|x86 = Tests|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Debug|x64.ActiveCfg = Debug|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Debug|x64.Build.0 = Debug|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Debug|x86.ActiveCfg = Debug|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Debug|x86.Build.0 = Debug|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Release|Any CPU.Build.0 = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Release|x64.ActiveCfg = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Release|x64.Build.0 = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Release|x86.ActiveCfg = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Release|x86.Build.0 = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Tests|Any CPU.Build.0 = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Tests|x64.ActiveCfg = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Tests|x64.Build.0 = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Tests|x86.ActiveCfg = Release|Any CPU + {671C3821-7BD9-4C16-9A8D-EC1470EDBC9B}.Tests|x86.Build.0 = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Debug|x64.ActiveCfg = Debug|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Debug|x64.Build.0 = Debug|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Debug|x86.ActiveCfg = Debug|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Debug|x86.Build.0 = Debug|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Release|Any CPU.Build.0 = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Release|x64.ActiveCfg = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Release|x64.Build.0 = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Release|x86.ActiveCfg = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Release|x86.Build.0 = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Tests|Any CPU.Build.0 = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Tests|x64.ActiveCfg = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Tests|x64.Build.0 = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Tests|x86.ActiveCfg = Release|Any CPU + {A2831B9B-4D3B-46CB-85DF-1B5C277C17DB}.Tests|x86.Build.0 = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Debug|x64.ActiveCfg = Debug|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Debug|x64.Build.0 = Debug|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Debug|x86.ActiveCfg = Debug|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Debug|x86.Build.0 = Debug|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Release|Any CPU.Build.0 = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Release|x64.ActiveCfg = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Release|x64.Build.0 = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Release|x86.ActiveCfg = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Release|x86.Build.0 = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Tests|Any CPU.Build.0 = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Tests|x64.ActiveCfg = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Tests|x64.Build.0 = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Tests|x86.ActiveCfg = Release|Any CPU + {A618D28B-9352-44F4-AA71-609BF68BF871}.Tests|x86.Build.0 = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Debug|x64.ActiveCfg = Debug|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Debug|x64.Build.0 = Debug|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Debug|x86.ActiveCfg = Debug|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Debug|x86.Build.0 = Debug|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Release|Any CPU.Build.0 = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Release|x64.ActiveCfg = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Release|x64.Build.0 = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Release|x86.ActiveCfg = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Release|x86.Build.0 = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Tests|Any CPU.Build.0 = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Tests|x64.ActiveCfg = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Tests|x64.Build.0 = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Tests|x86.ActiveCfg = Release|Any CPU + {2621AD55-C4E9-4884-81E9-DA0D00B4C6E5}.Tests|x86.Build.0 = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Debug|x64.ActiveCfg = Debug|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Debug|x64.Build.0 = Debug|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Debug|x86.ActiveCfg = Debug|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Debug|x86.Build.0 = Debug|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Release|Any CPU.Build.0 = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Release|x64.ActiveCfg = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Release|x64.Build.0 = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Release|x86.ActiveCfg = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Release|x86.Build.0 = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Tests|Any CPU.Build.0 = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Tests|x64.ActiveCfg = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Tests|x64.Build.0 = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Tests|x86.ActiveCfg = Release|Any CPU + {8DF0A4C4-A32D-44A4-9DA4-41A6BCA80B44}.Tests|x86.Build.0 = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Debug|x64.ActiveCfg = Debug|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Debug|x64.Build.0 = Debug|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Debug|x86.ActiveCfg = Debug|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Debug|x86.Build.0 = Debug|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Release|Any CPU.Build.0 = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Release|x64.ActiveCfg = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Release|x64.Build.0 = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Release|x86.ActiveCfg = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Release|x86.Build.0 = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Tests|Any CPU.Build.0 = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Tests|x64.ActiveCfg = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Tests|x64.Build.0 = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Tests|x86.ActiveCfg = Release|Any CPU + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55}.Tests|x86.Build.0 = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Debug|x64.ActiveCfg = Debug|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Debug|x64.Build.0 = Debug|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Debug|x86.ActiveCfg = Debug|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Debug|x86.Build.0 = Debug|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Release|Any CPU.Build.0 = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Release|x64.ActiveCfg = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Release|x64.Build.0 = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Release|x86.ActiveCfg = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Release|x86.Build.0 = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Tests|Any CPU.Build.0 = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Tests|x64.ActiveCfg = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Tests|x64.Build.0 = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Tests|x86.ActiveCfg = Release|Any CPU + {191B2B91-D5FD-4138-A72A-AB0B149988C6}.Tests|x86.Build.0 = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Debug|x64.ActiveCfg = Debug|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Debug|x64.Build.0 = Debug|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Debug|x86.ActiveCfg = Debug|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Debug|x86.Build.0 = Debug|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Release|Any CPU.Build.0 = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Release|x64.ActiveCfg = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Release|x64.Build.0 = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Release|x86.ActiveCfg = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Release|x86.Build.0 = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Tests|Any CPU.Build.0 = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Tests|x64.ActiveCfg = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Tests|x64.Build.0 = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Tests|x86.ActiveCfg = Release|Any CPU + {F7407750-5F72-460F-9C53-27CF509A39B1}.Tests|x86.Build.0 = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Debug|x64.ActiveCfg = Debug|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Debug|x64.Build.0 = Debug|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Debug|x86.ActiveCfg = Debug|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Debug|x86.Build.0 = Debug|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Release|Any CPU.Build.0 = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Release|x64.ActiveCfg = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Release|x64.Build.0 = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Release|x86.ActiveCfg = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Release|x86.Build.0 = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Tests|Any CPU.Build.0 = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Tests|x64.ActiveCfg = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Tests|x64.Build.0 = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Tests|x86.ActiveCfg = Release|Any CPU + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C}.Tests|x86.Build.0 = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Debug|x64.ActiveCfg = Debug|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Debug|x64.Build.0 = Debug|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Debug|x86.ActiveCfg = Debug|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Debug|x86.Build.0 = Debug|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Release|Any CPU.Build.0 = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Release|x64.ActiveCfg = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Release|x64.Build.0 = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Release|x86.ActiveCfg = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Release|x86.Build.0 = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Tests|Any CPU.Build.0 = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Tests|x64.ActiveCfg = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Tests|x64.Build.0 = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Tests|x86.ActiveCfg = Release|Any CPU + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98}.Tests|x86.Build.0 = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Debug|x64.ActiveCfg = Debug|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Debug|x64.Build.0 = Debug|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Debug|x86.Build.0 = Debug|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Release|Any CPU.Build.0 = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Release|x64.ActiveCfg = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Release|x64.Build.0 = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Release|x86.ActiveCfg = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Release|x86.Build.0 = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Tests|Any CPU.Build.0 = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Tests|x64.ActiveCfg = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Tests|x64.Build.0 = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Tests|x86.ActiveCfg = Release|Any CPU + {3E1F9741-01D3-480A-A484-3CD373AE4A7F}.Tests|x86.Build.0 = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Debug|x64.ActiveCfg = Debug|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Debug|x64.Build.0 = Debug|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Debug|x86.ActiveCfg = Debug|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Debug|x86.Build.0 = Debug|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Release|Any CPU.Build.0 = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Release|x64.ActiveCfg = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Release|x64.Build.0 = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Release|x86.ActiveCfg = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Release|x86.Build.0 = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Tests|Any CPU.Build.0 = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Tests|x64.ActiveCfg = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Tests|x64.Build.0 = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Tests|x86.ActiveCfg = Release|Any CPU + {191B1B91-D9FD-4138-A72A-AB0B149958C6}.Tests|x86.Build.0 = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Debug|x64.ActiveCfg = Debug|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Debug|x64.Build.0 = Debug|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Debug|x86.ActiveCfg = Debug|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Debug|x86.Build.0 = Debug|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Release|Any CPU.Build.0 = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Release|x64.ActiveCfg = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Release|x64.Build.0 = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Release|x86.ActiveCfg = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Release|x86.Build.0 = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Tests|Any CPU.Build.0 = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Tests|x64.ActiveCfg = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Tests|x64.Build.0 = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Tests|x86.ActiveCfg = Release|Any CPU + {20055427-1352-44FB-8442-BF7F15F9C59E}.Tests|x86.Build.0 = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Debug|x64.ActiveCfg = Debug|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Debug|x64.Build.0 = Debug|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Debug|x86.ActiveCfg = Debug|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Debug|x86.Build.0 = Debug|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Release|Any CPU.Build.0 = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Release|x64.ActiveCfg = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Release|x64.Build.0 = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Release|x86.ActiveCfg = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Release|x86.Build.0 = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Tests|Any CPU.Build.0 = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Tests|x64.ActiveCfg = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Tests|x64.Build.0 = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Tests|x86.ActiveCfg = Release|Any CPU + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F}.Tests|x86.Build.0 = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Debug|x64.ActiveCfg = Debug|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Debug|x64.Build.0 = Debug|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Debug|x86.ActiveCfg = Debug|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Debug|x86.Build.0 = Debug|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Release|Any CPU.Build.0 = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Release|x64.ActiveCfg = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Release|x64.Build.0 = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Release|x86.ActiveCfg = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Release|x86.Build.0 = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Tests|Any CPU.Build.0 = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Tests|x64.ActiveCfg = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Tests|x64.Build.0 = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Tests|x86.ActiveCfg = Release|Any CPU + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF}.Tests|x86.Build.0 = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Debug|x64.ActiveCfg = Debug|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Debug|x64.Build.0 = Debug|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Debug|x86.ActiveCfg = Debug|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Debug|x86.Build.0 = Debug|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Release|Any CPU.Build.0 = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Release|x64.ActiveCfg = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Release|x64.Build.0 = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Release|x86.ActiveCfg = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Release|x86.Build.0 = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Tests|Any CPU.Build.0 = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Tests|x64.ActiveCfg = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Tests|x64.Build.0 = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Tests|x86.ActiveCfg = Release|Any CPU + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16}.Tests|x86.Build.0 = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Debug|x64.ActiveCfg = Debug|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Debug|x64.Build.0 = Debug|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Debug|x86.ActiveCfg = Debug|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Debug|x86.Build.0 = Debug|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Release|Any CPU.Build.0 = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Release|x64.ActiveCfg = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Release|x64.Build.0 = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Release|x86.ActiveCfg = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Release|x86.Build.0 = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Tests|Any CPU.Build.0 = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Tests|x64.ActiveCfg = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Tests|x64.Build.0 = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Tests|x86.ActiveCfg = Release|Any CPU + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471}.Tests|x86.Build.0 = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Debug|x64.ActiveCfg = Debug|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Debug|x64.Build.0 = Debug|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Debug|x86.ActiveCfg = Debug|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Debug|x86.Build.0 = Debug|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Release|Any CPU.Build.0 = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Release|x64.ActiveCfg = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Release|x64.Build.0 = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Release|x86.ActiveCfg = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Release|x86.Build.0 = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Tests|Any CPU.Build.0 = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Tests|x64.ActiveCfg = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Tests|x64.Build.0 = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Tests|x86.ActiveCfg = Release|Any CPU + {8AE9D385-A379-4F5F-A703-3DF643DA6742}.Tests|x86.Build.0 = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Debug|x64.ActiveCfg = Debug|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Debug|x64.Build.0 = Debug|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Debug|x86.ActiveCfg = Debug|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Debug|x86.Build.0 = Debug|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Release|Any CPU.Build.0 = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Release|x64.ActiveCfg = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Release|x64.Build.0 = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Release|x86.ActiveCfg = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Release|x86.Build.0 = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Tests|Any CPU.Build.0 = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Tests|x64.ActiveCfg = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Tests|x64.Build.0 = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Tests|x86.ActiveCfg = Release|Any CPU + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4}.Tests|x86.Build.0 = Release|Any CPU + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Any CPU.Build.0 = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.Build.0 = Debug|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x86.ActiveCfg = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x86.Build.0 = Debug|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Any CPU.ActiveCfg = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Any CPU.Build.0 = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.ActiveCfg = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x86.Build.0 = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Tests|Any CPU.ActiveCfg = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Tests|Any CPU.Build.0 = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Tests|x64.ActiveCfg = Release|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Tests|x64.Build.0 = Release|x64 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Tests|x86.ActiveCfg = Release|Win32 + {B12702AD-ABFB-343A-A199-8E24837244A3}.Tests|x86.Build.0 = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Debug|Any CPU.Build.0 = Debug|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Debug|x64.ActiveCfg = Debug|x64 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Debug|x64.Build.0 = Debug|x64 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Debug|x86.ActiveCfg = Debug|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Debug|x86.Build.0 = Debug|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Release|Any CPU.ActiveCfg = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Release|Any CPU.Build.0 = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Release|x64.ActiveCfg = Release|x64 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Release|x64.Build.0 = Release|x64 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Release|x86.ActiveCfg = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Release|x86.Build.0 = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Tests|Any CPU.ActiveCfg = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Tests|Any CPU.Build.0 = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Tests|x64.ActiveCfg = Release|x64 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Tests|x64.Build.0 = Release|x64 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Tests|x86.ActiveCfg = Release|Win32 + {70711A97-D9B0-3A86-9756-9FF47337908B}.Tests|x86.Build.0 = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Debug|Any CPU.Build.0 = Debug|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Debug|x64.ActiveCfg = Debug|x64 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Debug|x64.Build.0 = Debug|x64 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Debug|x86.ActiveCfg = Debug|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Debug|x86.Build.0 = Debug|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Release|Any CPU.ActiveCfg = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Release|Any CPU.Build.0 = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Release|x64.ActiveCfg = Release|x64 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Release|x64.Build.0 = Release|x64 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Release|x86.ActiveCfg = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Release|x86.Build.0 = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Tests|Any CPU.ActiveCfg = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Tests|Any CPU.Build.0 = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Tests|x64.ActiveCfg = Release|x64 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Tests|x64.Build.0 = Release|x64 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Tests|x86.ActiveCfg = Release|Win32 + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F}.Tests|x86.Build.0 = Release|Win32 + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Debug|x64.ActiveCfg = Debug|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Debug|x64.Build.0 = Debug|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Debug|x86.ActiveCfg = Debug|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Debug|x86.Build.0 = Debug|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Release|x64.ActiveCfg = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Release|x86.ActiveCfg = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Tests|Any CPU.Build.0 = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Tests|x64.ActiveCfg = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Tests|x64.Build.0 = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Tests|x86.ActiveCfg = Release|Any CPU + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D}.Tests|x86.Build.0 = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Debug|x64.ActiveCfg = Debug|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Debug|x64.Build.0 = Debug|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Debug|x86.ActiveCfg = Debug|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Debug|x86.Build.0 = Debug|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Release|x64.ActiveCfg = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Release|x86.ActiveCfg = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Tests|Any CPU.Build.0 = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Tests|x64.ActiveCfg = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Tests|x64.Build.0 = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Tests|x86.ActiveCfg = Release|Any CPU + {D574EFED-5E19-45BE-9B05-310F65065303}.Tests|x86.Build.0 = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Debug|x64.ActiveCfg = Debug|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Debug|x64.Build.0 = Debug|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Debug|x86.Build.0 = Debug|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Release|x64.ActiveCfg = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Release|x86.ActiveCfg = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Tests|Any CPU.Build.0 = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Tests|x64.ActiveCfg = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Tests|x64.Build.0 = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Tests|x86.ActiveCfg = Release|Any CPU + {A5320606-37B8-4F15-97E2-16314109CAF9}.Tests|x86.Build.0 = Release|Any CPU + {12857847-9877-466C-B056-DD286A219093}.Debug|Any CPU.ActiveCfg = Debug|x64 + {12857847-9877-466C-B056-DD286A219093}.Debug|x64.ActiveCfg = Debug|x64 + {12857847-9877-466C-B056-DD286A219093}.Debug|x86.ActiveCfg = Debug|x64 + {12857847-9877-466C-B056-DD286A219093}.Release|Any CPU.ActiveCfg = Release|x64 + {12857847-9877-466C-B056-DD286A219093}.Release|x64.ActiveCfg = Release|x64 + {12857847-9877-466C-B056-DD286A219093}.Release|x86.ActiveCfg = Release|x64 + {12857847-9877-466C-B056-DD286A219093}.Tests|Any CPU.ActiveCfg = Release|x64 + {12857847-9877-466C-B056-DD286A219093}.Tests|x64.ActiveCfg = Release|x64 + {12857847-9877-466C-B056-DD286A219093}.Tests|x86.ActiveCfg = Release|x64 + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Debug|x64.ActiveCfg = Debug|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Debug|x64.Build.0 = Debug|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Debug|x86.ActiveCfg = Debug|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Debug|x86.Build.0 = Debug|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Release|x64.ActiveCfg = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Release|x86.ActiveCfg = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Tests|Any CPU.ActiveCfg = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Tests|Any CPU.Build.0 = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Tests|x64.ActiveCfg = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Tests|x64.Build.0 = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Tests|x86.ActiveCfg = Release|Any CPU + {E809DDE3-AE76-4F7A-8DC5-775AC4900138}.Tests|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {71E4F2B9-3628-45E2-A664-A2F6E3AAFE55} = {DD307619-BF80-4E5D-AE54-196057187702} + {191B2B91-D5FD-4138-A72A-AB0B149988C6} = {DD307619-BF80-4E5D-AE54-196057187702} + {F7407750-5F72-460F-9C53-27CF509A39B1} = {A7918293-56E9-465A-AE1C-0724576ADD66} + {F2166B59-E41B-4328-B31D-9E2B9AC5A59C} = {A7918293-56E9-465A-AE1C-0724576ADD66} + {73C3DFB8-BD8A-4634-AAD5-DC71EDC37B98} = {DD307619-BF80-4E5D-AE54-196057187702} + {3E1F9741-01D3-480A-A484-3CD373AE4A7F} = {DD307619-BF80-4E5D-AE54-196057187702} + {191B1B91-D9FD-4138-A72A-AB0B149958C6} = {DD307619-BF80-4E5D-AE54-196057187702} + {20055427-1352-44FB-8442-BF7F15F9C59E} = {A7918293-56E9-465A-AE1C-0724576ADD66} + {DC1AE91B-45CE-4C5B-8F77-CDB58566038F} = {A7918293-56E9-465A-AE1C-0724576ADD66} + {7AF6C34B-65D2-4010-92F6-420E59DDE9BF} = {A7918293-56E9-465A-AE1C-0724576ADD66} + {4833E4C7-FFFF-4DA5-A7A5-36C6C3840F16} = {DD307619-BF80-4E5D-AE54-196057187702} + {BDA1CD69-624B-4D9D-9B88-ACBEB14AC471} = {A7918293-56E9-465A-AE1C-0724576ADD66} + {8AE9D385-A379-4F5F-A703-3DF643DA6742} = {DD307619-BF80-4E5D-AE54-196057187702} + {40ADFD6A-64EA-4C77-9D4B-3A91D6AB76B4} = {A7918293-56E9-465A-AE1C-0724576ADD66} + {A7918293-56E9-465A-AE1C-0724576ADD66} = {9D9290B2-9E87-46EA-84EA-02836F699BB8} + {DD307619-BF80-4E5D-AE54-196057187702} = {9D9290B2-9E87-46EA-84EA-02836F699BB8} + {B12702AD-ABFB-343A-A199-8E24837244A3} = {9B109DDA-0521-46AD-B087-B7CBCB33FEE5} + {70711A97-D9B0-3A86-9756-9FF47337908B} = {9B109DDA-0521-46AD-B087-B7CBCB33FEE5} + {4CEE73C9-FCFA-3A72-A0A3-036BDBB3240F} = {9B109DDA-0521-46AD-B087-B7CBCB33FEE5} + {97DAEAD7-3D10-459C-8920-94E48C43AA5F} = {F96D578B-D78E-47F4-86DA-FE472B9942F0} + {48A50432-6BDF-4DE2-A3AD-3A237D31E49D} = {97DAEAD7-3D10-459C-8920-94E48C43AA5F} + {D6FB29A4-8921-46F5-B170-B15538AB4D69} = {F96D578B-D78E-47F4-86DA-FE472B9942F0} + {D574EFED-5E19-45BE-9B05-310F65065303} = {D6FB29A4-8921-46F5-B170-B15538AB4D69} + {A5320606-37B8-4F15-97E2-16314109CAF9} = {D6FB29A4-8921-46F5-B170-B15538AB4D69} + {12857847-9877-466C-B056-DD286A219093} = {D6FB29A4-8921-46F5-B170-B15538AB4D69} + {E809DDE3-AE76-4F7A-8DC5-775AC4900138} = {D6FB29A4-8921-46F5-B170-B15538AB4D69} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {17FF4AFE-273C-47CD-8D84-F0D023B10BE5} + EndGlobalSection +EndGlobal -- Gitblit v1.9.1