diff options
author | Miguel Costa <miguel.costa@qt.io> | 2019-06-25 10:46:41 +0200 |
---|---|---|
committer | Miguel Costa <miguel.costa@qt.io> | 2019-08-05 09:24:41 +0000 |
commit | fd9d180dd2f434eab5520da7a2eb3821479ecf63 (patch) | |
tree | ec16a6dafdabdea968cc17cd96c5c27356173541 | |
parent | 6a77aa5ee036621b1f79827edce481eafe20d29f (diff) |
Add support for VS design-time build
This change adds support for VS project build and property evaluation
during design time. This will have an impact on the availability and
accuracy of build-time information in property pages (e.g. compiler
switches) and of syntax highlighting/code model in the VS editor.
Change-Id: Ia05f5a14a43d0c76cb2eea2f7b0d3d519acafbe6
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
-rw-r--r-- | src/qtmsbuild/qt.props | 10 | ||||
-rw-r--r-- | src/qtmsbuild/qt_globals.targets | 101 | ||||
-rw-r--r-- | src/qtmsbuild/qt_vars.targets | 23 | ||||
-rw-r--r-- | src/qtvstools/DteEventsHandler.cs | 5 | ||||
-rw-r--r-- | src/qtvstools/QtMsBuild/Components/QtVarsDesignTime.cs | 184 | ||||
-rw-r--r-- | src/qtvstools/QtVsTools.csproj | 5 |
6 files changed, 297 insertions, 31 deletions
diff --git a/src/qtmsbuild/qt.props b/src/qtmsbuild/qt.props index 08f42d6c..90ef73ee 100644 --- a/src/qtmsbuild/qt.props +++ b/src/qtmsbuild/qt.props @@ -105,12 +105,22 @@ >$(QtVarsWorkDir)$(QtVarsFileName)</QtVarsWorkPath> <QtVarsFilePath >$(QtVarsOutputDir)$(QtVarsFileName)</QtVarsFilePath> + <QtVarsIndexPathDesignTime + >$(QtVarsOutputDir)$(QtVarsFileNameWithoutExt).designtime.idx</QtVarsIndexPathDesignTime> + <QtVarsDesignTime Condition="Exists('$(QtVarsIndexPathDesignTime)')" + >$([System.IO.File]::ReadAllText('$(QtVarsIndexPathDesignTime)'))</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)"/> + <!--// Add Qt variables to compiler and linker options --> <ItemDefinitionGroup Condition="'$(QtVsProjectSettings)' == 'true'"> <ClCompile> diff --git a/src/qtmsbuild/qt_globals.targets b/src/qtmsbuild/qt_globals.targets index e3be06f8..90154d6f 100644 --- a/src/qtmsbuild/qt_globals.targets +++ b/src/qtmsbuild/qt_globals.targets @@ -41,8 +41,27 @@ // Build dependencies // --> <PropertyGroup> - <BuildDependsOn>QtVersion;$(BuildDependsOn);Qt</BuildDependsOn> - <CleanDependsOn>$(CleanDependsOn);QtClean</CleanDependsOn> + <BuildDependsOn> + QtVersion; + $(BuildDependsOn); + Qt + </BuildDependsOn> + <CleanDependsOn> + $(CleanDependsOn); + QtClean + </CleanDependsOn> + <DesignTimeBuildInitTargets> + $(DesignTimeBuildInitTargets); + Qt + </DesignTimeBuildInitTargets> + <ComputeCompileInputsTargets> + $(ComputeCompileInputsTargets); + QtWork + </ComputeCompileInputsTargets> + <ComputeLinkInputsTargets> + $(ComputeLinkInputsTargets); + QtWork + </ComputeLinkInputsTargets> </PropertyGroup> <!-- @@ -87,7 +106,9 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// // Prepare Qt build: read and parse work log file // --> - <Target Name="QtPrepare"> + <Target Name="QtPrepare" + Condition="'$(QtSkipWork)' != 'true'" + DependsOnTargets="$(QtDependsOn)"> <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="#### QtPrepare"/> @@ -113,7 +134,8 @@ // 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="$(QtBuildTargets)" + <Target Name="QtWorkPrepare" DependsOnTargets="$(QtDependsOn);$(QtBuildTargets)" + Condition="'$(QtSkipWork)' != 'true'" Inputs="%(QtWork.WorkType)(%(QtWork.Identity))" Outputs="@(QtWork->'####### Don't skip this target #######')"> @@ -137,6 +159,9 @@ <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'" @@ -150,12 +175,27 @@ <!-- /////////////////////////////////////////////////////////////////////////////////////////////// - // If the hash of the requested work matches the logged hash, skip work item + // 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 // --> - <ItemGroup Condition="'@(QtWork)' != '' - AND ('$(dependencies_changed)' != 'true' - OR ('$(input_changed)' != 'true' - AND '$(log_hash)' == '$(work_hash)'))"> + <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> @@ -165,22 +205,16 @@ <!-- /////////////////////////////////////////////////////////////////////////////////////////////// - // If the hashes do not match, generate new work log entry and ensure path to output exists + // Generate new work log entry and ensure path to output exists // --> - <ItemGroup Condition="'@(QtWork)' != '' - AND '$(dependencies_changed)' == 'true' - AND ('$(input_changed)' == 'true' - OR '$(log_hash)' != '$(work_hash)')"> + <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 '$(dependencies_changed)' == 'true' - AND ('$(input_changed)' == 'true' - OR '$(log_hash)' != '$(work_hash)')" + <MakeDir Condition="'@(QtWork)' != '' AND '$(skip_work)' != 'true'" Directories="$([System.IO.Path]::GetDirectoryName(%(QtWork.OutputFile)))"/> <!-- @@ -192,6 +226,9 @@ <log_hash/> <dependencies_changed/> <input_changed/> + <project_changed/> + <do_work/> + <skip_work/> </PropertyGroup> <ItemGroup> <log_entry Remove="@(log_entry)"/> @@ -205,9 +242,8 @@ // Run Qt tools and add dynamic C++ sources to build // --> <Target Name="QtWork" - DependsOnTargets="QtVersion;QtPrepare;QtWorkPrepare" - AfterTargets="CustomBuild" - BeforeTargets="_ClCompile"> + Condition="'$(QtSkipWork)' != 'true'" + DependsOnTargets="QtPrepare;QtWorkPrepare"> <Message Importance="High" Condition="'$(QtDebug)' == 'true'" Text="## Qt Build $(QtBuildTargets.Replace(';',' ').Trim())" /> @@ -216,7 +252,10 @@ /////////////////////////////////////////////////////////////////////////////////////////////// // Run work in parallel processes // --> - <QtRunWork Condition="'@(QtWork)' != '' AND '%(QtWork.ParallelBuild)' == 'true'" + <QtRunWork + Condition="'@(QtWork)' != '' + AND '%(QtWork.ParallelBuild)' == 'true' + AND '$(DesignTimeBuild)' != 'true'" QtWork="@(QtWork)" QtMaxProcs="$(QtMaxProcs)" QtDebug="$(QtDebug)"> <Output TaskParameter="Result" ItemName="QtWorkResult" /> </QtRunWork> @@ -225,7 +264,10 @@ /////////////////////////////////////////////////////////////////////////////////////////////// // Run work in a single process // --> - <QtRunWork Condition="'@(QtWork)' != '' AND '%(QtWork.ParallelBuild)' != 'true'" + <QtRunWork + Condition="'@(QtWork)' != '' + AND '%(QtWork.ParallelBuild)' != 'true' + AND '$(DesignTimeBuild)' != 'true'" QtWork="@(QtWork)" QtMaxProcs="1" QtDebug="$(QtDebug)"> <Output TaskParameter="Result" ItemName="QtWorkResult" /> </QtRunWork> @@ -234,7 +276,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////// // Save tracking log of files read during build; used by VS to check the up-to-date status // --> - <ItemGroup> + <ItemGroup Condition="'$(DesignTimeBuild)' != 'true'"> <read_log Include="^%(QtWorkResult.FullPath);%(QtWorkResult.AdditionalDependencies)" Condition="'%(QtWorkResult.ExitCode)' == '0' AND '%(QtWorkResult.DisableLog)' != 'true'"> <WorkType>%(QtWorkResult.WorkType)</WorkType> @@ -257,7 +299,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////// // Save tracking log of files written during build; used by VS to check the up-to-date status // --> - <ItemGroup> + <ItemGroup Condition="'$(DesignTimeBuild)' != 'true'"> <write_log Include="^%(QtWorkResult.FullPath);%(QtWorkResult.OutputFile)" Condition="'%(QtWorkResult.ExitCode)' == '0' AND '%(QtWorkResult.DisableLog)' != 'true'"> <WorkType>%(QtWorkResult.WorkType)</WorkType> @@ -278,7 +320,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////// // Log output files; this is used by VS to determine what files to delete on "Clean" // --> - <ItemGroup> + <ItemGroup Condition="'$(DesignTimeBuild)' != 'true'"> <clean_log Include="%(QtWorkResult.OutputFile)" Condition="'%(QtWorkResult.ExitCode)' == '0'"> <Source>@(QtWorkResult, '|')</Source> @@ -294,7 +336,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////// // Log calls to Qt tools; used in QtWorkPrepare to detect changes to the options of Qt tools // --> - <WriteLinesToFile Condition="'@(QtWorkLog)' != ''" + <WriteLinesToFile Condition="'@(QtWorkLog)' != '' AND '$(DesignTimeBuild)' != 'true'" File="$(QtLogFilePath)" Lines="@(QtWorkLog->'%(Identity)|%(Hash)')" Overwrite="true" Encoding="Unicode"/> @@ -303,7 +345,10 @@ /////////////////////////////////////////////////////////////////////////////////////////////// // Generate build error if a Qt tool did not terminate correctly // --> - <Error Condition="'%(QtWorkResult.ExitCode)' != '' AND '%(QtWorkResult.ExitCode)' != '0'" + <Error + Condition="'%(QtWorkResult.ExitCode)' != '' + AND '%(QtWorkResult.ExitCode)' != '0' + AND '$(DesignTimeBuild)' != 'true'" File="%(QtWorkResult.Identity)" Code="%(QtWorkResult.ExitCode)" Text="%(QtWorkResult.WorkType) (%(QtWorkResult.ToolPath))"/> diff --git a/src/qtmsbuild/qt_vars.targets b/src/qtmsbuild/qt_vars.targets index 0d4cf834..f58525c5 100644 --- a/src/qtmsbuild/qt_vars.targets +++ b/src/qtmsbuild/qt_vars.targets @@ -35,7 +35,7 @@ <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> - <QtDependsOn>QtVars;$(QtDependsOn)</QtDependsOn> + <QtDependsOn Condition="'$(DesignTimeBuild)' != 'true'">QtVars;$(QtDependsOn)</QtDependsOn> </PropertyGroup> <ItemGroup> <QtModuleList Include="$(QtModules)"/> @@ -245,7 +245,7 @@ for (varName, $$list($$sorted(varNames))) { Condition="'$(QtVarsDebug)' == 'true'" Text="@(QMakeError->'%(Identity)','%0D%0A')"/> <Error - Condition="'$(ErrorLevel)' != '0'" + Condition="'$(ErrorLevel)' != '0' AND '$(DesignTimeBuild)' != 'true'" File="$(MSBuildProjectFile)" Text="@(QMakeError->'%(Identity)','%0D%0A')"/> @@ -284,6 +284,24 @@ for (varName, $$list($$sorted(varNames))) { Condition="'$(QtVarsDebug)' != 'true'" Directories="$(QtVarsWorkDir)"/> + <!--// In design-time, copy generated .props to randomly named file --> + <PropertyGroup> + <QtVarsDesignTimeNew + Condition="'$(ErrorLevel)' == '0' AND '$(DesignTimeBuild)' == 'true'" + >$([System.IO.Path]::Combine('$(QtVarsOutputDir)', + 'qtvars_$([System.IO.Path]::GetRandomFileName()).designtime.props'))</QtVarsDesignTimeNew> + </PropertyGroup> + <Delete + Condition="'$(ErrorLevel)' == '0' + AND '$(DesignTimeBuild)' == 'true'" + Files="$([System.IO.Directory]::GetFiles('$(QtVarsOutputDir)', '*.designtime.props'))"/> + <Copy + Condition="'$(ErrorLevel)' == '0' AND '$(DesignTimeBuild)' == 'true'" + SourceFiles="$(QtVarsFilePath)" DestinationFiles="$(QtVarsDesignTimeNew)"/> + <WriteLinesToFile + Condition="'$(ErrorLevel)' == '0' AND '$(DesignTimeBuild)' == 'true'" + File="$(QtVarsIndexPathDesignTime)" Overwrite="true" Lines="$(QtVarsDesignTimeNew)"/> + <!--// Skip remaining build --> <CreateProperty Value="true"> <Output TaskParameter="ValueSetByTask" PropertyName="QtSkipWork" /> @@ -294,6 +312,7 @@ for (varName, $$list($$sorted(varNames))) { <!--// Restart build in second MSBuild instance with updated Qt variables --> <MSBuild + Condition="'$(DesignTimeBuild)' != 'true'" Projects="$(MSBuildProjectFullPath)" Targets="Build" Properties="RandomFileName=$([System.IO.Path]::GetRandomFileName())"> diff --git a/src/qtvstools/DteEventsHandler.cs b/src/qtvstools/DteEventsHandler.cs index 516bc585..7ec13f4c 100644 --- a/src/qtvstools/DteEventsHandler.cs +++ b/src/qtvstools/DteEventsHandler.cs @@ -33,6 +33,7 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.VCProjectEngine; using QtProjectLib; using QtProjectLib.QtMsBuild; +using QtVsTools.QtMsBuild; using System; using System.IO; using System.Linq; @@ -542,8 +543,10 @@ namespace QtVsTools void SolutionEvents_Opened() { foreach (var p in HelperFunctions.ProjectsInSolution(Vsix.Instance.Dte)) { - if (HelperFunctions.IsQtProject(p)) + if (HelperFunctions.IsQtProject(p)) { InitializeVCProject(p); + QtVarsDesignTime.AddProject(p); + } } } diff --git a/src/qtvstools/QtMsBuild/Components/QtVarsDesignTime.cs b/src/qtvstools/QtMsBuild/Components/QtVarsDesignTime.cs new file mode 100644 index 00000000..85dcd399 --- /dev/null +++ b/src/qtvstools/QtMsBuild/Components/QtVarsDesignTime.cs @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** 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.Collections.Immutable; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Execution; +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Properties; +using EnvDTE; +using QtProjectLib; +using System.Diagnostics; + +namespace QtVsTools.QtMsBuild +{ + class QtVarsDesignTime + { + static ConcurrentDictionary<EnvDTE.Project, QtVarsDesignTime> Instances { get; set; } + + EnvDTE.Project Project { get; set; } + + UnconfiguredProject UnconfiguredProject { get; set; } + IEnumerable<ConfiguredProject> ConfiguredProjects { get; set; } + IProjectLockService LockService { get; set; } + + static readonly object criticalSection = new object(); + public static void AddProject(EnvDTE.Project project) + { + lock (criticalSection) { + if (Instances == null) + Instances = new ConcurrentDictionary<EnvDTE.Project, QtVarsDesignTime>(); + } + Instances[project] = new QtVarsDesignTime(project); + } + + private QtVarsDesignTime(EnvDTE.Project project) + { + Project = project; + Task.Run(Initialize).Wait(5000); + } + + bool initialized = false; + + private async Task Initialize() + { + var context = Project.Object as IVsBrowseObjectContext; + if (context == null) + return; + + UnconfiguredProject = context.UnconfiguredProject; + if (UnconfiguredProject == null + || UnconfiguredProject.ProjectService == null + || UnconfiguredProject.ProjectService.Services == null) + return; + + LockService = UnconfiguredProject.ProjectService.Services.ProjectLockService; + if (LockService == null) + return; + + var configs = await UnconfiguredProject.Services + .ProjectConfigurationsService.GetKnownProjectConfigurationsAsync(); + + initialized = true; + + foreach (var config in configs) { + var configProject = await UnconfiguredProject.LoadConfiguredProjectAsync(config); + configProject.ProjectChanged += OnProjectChanged; + configProject.ProjectUnloading += OnProjectUnloading; + await DesignTimeUpdateQtVarsAsync(configProject); + } + } + + private void OnProjectChanged(object sender, EventArgs e) + { + if (!initialized) + return; + + var project = sender as ConfiguredProject; + if (project == null || project.Services == null) + return; + + Task.Run(async () => await DesignTimeUpdateQtVarsAsync(project)); + } + + private async Task DesignTimeUpdateQtVarsAsync(ConfiguredProject project) + { + if (project == null) + return; + + try { + ProjectWriteLockReleaser writeAccess; + var timer = Stopwatch.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) + using (var buildManager = new BuildManager()) { + var msBuildProject = await writeAccess.GetProjectAsync(project); + + var configProps = project.ProjectConfiguration + .Dimensions.ToImmutableDictionary(); + + var projectInstance = new ProjectInstance(msBuildProject.Xml, + new Dictionary<string, string>(configProps) + { { "DesignTimeBuild", "true" } }, + null, new ProjectCollection()); + + var buildRequest = new BuildRequestData(projectInstance, + targetsToBuild: new[] { "QtVars" }, + hostServices: null, + flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild); + + var result = buildManager.Build(new BuildParameters(), buildRequest); + + if (result == null + || result.ResultsByTarget == null + || result.OverallResult != BuildResultCode.Success) { + Messages.PaneMessageSafe(Vsix.Instance.Dte, timeout: 5000, + str: string.Format("{0}: design-time pre-build FAILED!", + Path.GetFileName(UnconfiguredProject.FullPath))); + } else { + var resultQtVars = result.ResultsByTarget["QtVars"]; + if (resultQtVars.ResultCode == TargetResultCode.Success) { + msBuildProject.MarkDirty(); + } + } + } + } catch (Exception e) { + Messages.PaneMessageSafe(Vsix.Instance.Dte, timeout: 5000, + str: string.Format("{0}: design-time pre-build ERROR: {1}", + Path.GetFileName(UnconfiguredProject.FullPath), e.Message)); + } + } + + private async Task OnProjectUnloading(object sender, EventArgs args) + { + var project = sender as ConfiguredProject; + if (project == null || project.Services == null) + return; + project.ProjectChanged -= OnProjectChanged; + project.ProjectUnloading -= OnProjectUnloading; + Instances[Project] = null; + await Task.Yield(); + } + } +} diff --git a/src/qtvstools/QtVsTools.csproj b/src/qtvstools/QtVsTools.csproj index 2134d88a..7418b515 100644 --- a/src/qtvstools/QtVsTools.csproj +++ b/src/qtvstools/QtVsTools.csproj @@ -153,6 +153,7 @@ <Compile Include="QtMsBuildConverter.cs" /> <Compile Include="QtMsBuild\Components\QtModulesEditor.cs" /> <Compile Include="QtMsBuild\Components\QtVersionProvider.cs" /> + <Compile Include="QtMsBuild\Components\QtVarsDesignTime.cs" /> <Compile Include="QtProjectContextMenu.cs" /> <Compile Include="QtSolutionContextMenu.cs" /> <Compile Include="QtVsToolsDefaultEditors.cs" /> @@ -462,17 +463,21 @@ </ProjectReference> </ItemGroup> <ItemGroup> + <Reference Include="Microsoft.Build" /> + <Reference Include="Microsoft.Build.Framework" /> <Reference Include="Microsoft.VisualStudio.Composition"> <HintPath>$(VsInstallRoot)\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Composition.dll</HintPath> </Reference> <Reference Include="Microsoft.VisualStudio.ProjectSystem"> <HintPath>$(VsInstallRoot)\Common7\IDE\CommonExtensions\Microsoft\Project\Microsoft.VisualStudio.ProjectSystem.dll</HintPath> </Reference> + <Reference Include="System.Collections.Immutable, Version=1.2.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> <Reference Include="System.Data" /> <Reference Include="System.Data.SQLite, Version=1.0.102.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL"> <HintPath>..\packages\System.Data.SQLite.Core.1.0.102.0\lib\net45\System.Data.SQLite.dll</HintPath> <Private>True</Private> </Reference> + <Reference Include="System.Threading.Tasks.Dataflow, Version=4.5.24.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> </ItemGroup> <ItemGroup> <Page Include="QtHelpLinkChooser.xaml"> |