Наша сборка Qt VS Tools
giy
2022-09-02 ca47896204482bf4a6979e3838bf7f09f61cebeb
QtVsTools.Package/QtMsBuild/QtProjectTracker.cs
@@ -29,41 +29,41 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
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.Shell;
using Microsoft.VisualStudio.TaskStatusCenter;
using Microsoft.VisualStudio.Threading;
using EnvDTE;
using Microsoft.VisualStudio.VCProjectEngine;
using Task = System.Threading.Tasks.Task;
namespace QtVsTools.QtMsBuild
{
    using Common;
    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 LazyFactory StaticLazy { get; } = new LazyFactory();
        static PunisherQueue<QtProjectTracker> _InitQueue;
        static PunisherQueue<QtProjectTracker> InitQueue =>
            StaticThreadSafeInit(() => _InitQueue, () =>
                _InitQueue = new PunisherQueue<QtProjectTracker>());
        static ConcurrentDictionary<string, QtProjectTracker> Instances => StaticLazy.Get(() =>
            Instances, () => new ConcurrentDictionary<string, QtProjectTracker>());
        static IVsTaskStatusCenterService _StatusCenter;
        static IVsTaskStatusCenterService StatusCenter => StaticThreadSafeInit(() => _StatusCenter,
                () => _StatusCenter = VsServiceProvider
                    .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
        static PunisherQueue<QtProjectTracker> InitQueue => StaticLazy.Get(() =>
            InitQueue, () => new PunisherQueue<QtProjectTracker>());
        static IVsTaskStatusCenterService StatusCenter => StaticLazy.Get(() =>
            StatusCenter, () => VsServiceProvider
                .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
        static Task InitDispatcher { get; set; }
        static ITaskHandler2 InitStatus { get; set; }
@@ -75,73 +75,37 @@
            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 string ProjectPath { get; private set; }
        public VCProject VcProject { get; private set; }
        public UnconfiguredProject UnconfiguredProject { get; private set; }
        public EventWaitHandle Initialized { get; private set; }
        List<Subscriber> Subscribers { get; set; }
        public EventWaitHandle Initialized { get; }
        public static bool IsTracked(EnvDTE.Project project)
        public static bool IsTracked(string projectPath)
        {
            return Instances.ContainsKey(project.FullName);
            return Instances.ContainsKey(projectPath);
        }
        public static void Add(EnvDTE.Project project)
        {
            if (!QtVsToolsPackage.Instance.Options.ProjectTracking)
                return;
            Get(project);
            ThreadHelper.ThrowIfNotOnUIThread();
            Get(project, project.FullName);
        }
        public static QtProjectTracker Get(EnvDTE.Project project)
        public static QtProjectTracker Get(EnvDTE.Project project, string projectPath)
        {
            lock (StaticCriticalSection) {
                QtProjectTracker tracker = null;
                if (Instances.TryGetValue(project.FullName, out tracker))
                if (Instances.TryGetValue(projectPath, out QtProjectTracker tracker))
                    return tracker;
                tracker = new QtProjectTracker
                {
                    Project = project,
                    ProjectPath = projectPath
                };
                Instances[project.FullName] = tracker;
                Instances[projectPath] = tracker;
                InitQueue.Enqueue(tracker);
                if (InitDispatcher == null)
                    InitDispatcher = Task.Run(InitDispatcherLoopAsync);
@@ -162,14 +126,17 @@
            while (!QtVsToolsPackage.Instance.Zombied) {
                while (InitQueue.IsEmpty)
                    await Task.Delay(100);
                QtProjectTracker tracker;
                if (InitQueue.TryDequeue(out tracker)) {
                if (InitQueue.TryDequeue(out QtProjectTracker tracker)) {
                    if (InitStatus == null) {
                        await QtVsToolsPackage.Instance.JoinableTaskFactory.SwitchToMainThreadAsync();
                        await QtVsToolsPackage.Instance.JoinableTaskFactory
                            .SwitchToMainThreadAsync();
                        tracker.BeginInitStatus();
                        await TaskScheduler.Default;
                    } else {
                        await QtVsToolsPackage.Instance.JoinableTaskFactory
                            .SwitchToMainThreadAsync();
                        tracker.UpdateInitStatus(0);
                        await TaskScheduler.Default;
                    }
                    await tracker.InitializeAsync();
                }
@@ -187,9 +154,12 @@
        async Task InitializeAsync()
        {
            int p = 0;
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
            UpdateInitStatus(p += 10);
            await QtVsToolsPackage.Instance.JoinableTaskFactory.SwitchToMainThreadAsync();
            VcProject = Project.Object as VCProject;
            if (VcProject == null)
                return;
            UpdateInitStatus(p += 10);
            var context = Project.Object as IVsBrowseObjectContext;
@@ -211,57 +181,23 @@
            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;
                configProject.ProjectUnloading += OnProjectUnloadingAsync;
                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));
                        config.Name, ProjectPath));
                }
                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)
        async Task OnProjectUnloadingAsync(object sender, EventArgs args)
        {
            var project = sender as ConfiguredProject;
            if (project == null || project.Services == null)
@@ -274,18 +210,16 @@
                    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);
                project.ProjectUnloading -= OnProjectUnloadingAsync;
                Instances.TryRemove(project.UnconfiguredProject.FullPath, out QtProjectTracker _);
            }
            await Task.Yield();
        }
        void BeginInitStatus()
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            lock (StaticCriticalSection) {
                if (InitStatus != null)
                    return;
@@ -303,9 +237,8 @@
                            PercentComplete = 0
                        })
                        as ITaskHandler2;
                } catch (Exception e) {
                    Messages.Print(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                }
                InitStatus.RegisterTask(new Task(() => throw new InvalidOperationException()));
            }
@@ -320,13 +253,12 @@
                    InitStatus.Progress.Report(new TaskProgressData
                    {
                        ProgressText = string.Format("{0} ({1} project(s) remaining)",
                            Project.Name, InitQueue.Count),
                            Path.GetFileNameWithoutExtension(ProjectPath), InitQueue.Count),
                        CanBeCanceled = true,
                        PercentComplete = percentComplete
                    });
                } catch (Exception e) {
                    Messages.Print(
                        e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace);
                } catch (Exception exception) {
                    exception.Log();
                }
            }
        }