protected override Task InitializeCoreAsync(CancellationToken cancellationToken) { _joinedDataSources = ProjectDataSources.JoinUpstreamDataSources(JoinableFactory, _projectFaultHandlerService, _projectSubscriptionService.ProjectSource, _dataSource); _progressRegistration = _dataProgressTrackerService.RegisterOutputDataSource(this); Action <IProjectVersionedValue <ValueTuple <IProjectSnapshot, RestoreData> > > action = OnRestoreCompleted; _subscription = ProjectDataSources.SyncLinkTo( _projectSubscriptionService.ProjectSource.SourceBlock.SyncLinkOptions(), _dataSource.SourceBlock.SyncLinkOptions(), DataflowBlockSlim.CreateActionBlock(action), linkOptions: DataflowOption.PropagateCompletion); return(Task.CompletedTask); }
private void OnActiveConfigurationsChanged(IProjectVersionedValue <IConfigurationGroup <ConfiguredProject> > e) { if (IsDisposing || IsDisposed) { return; } // Clean up past subscriptions _designTimeBuildSubscriptionLink?.Dispose(); if (e.Value.Count > 0) { var sourceLinkOptions = new StandardRuleDataflowLinkOptions { RuleNames = s_designTimeBuildWatchedRules, PropagateCompletion = true }; var disposableBag = new DisposableBag(CancellationToken.None); // We are taking source blocks from multiple configured projects and creating a SyncLink to combine the sources. // The SyncLink will only publish data when the versions of the sources match. There is a problem with that. // The sources have some version components that will make this impossible to match across TFMs. We introduce a // intermediate block here that will remove those version components so that the synclink can actually sync versions. IEnumerable <ProjectDataSources.SourceBlockAndLink <IProjectValueVersions> > sourceBlocks = e.Value.Select( cp => { IReceivableSourceBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> > sourceBlock = cp.Services.ProjectSubscription.JointRuleSource.SourceBlock; IPropagatorBlock <IProjectVersionedValue <IProjectSubscriptionUpdate>, IProjectVersionedValue <IProjectSubscriptionUpdate> > versionDropper = CreateVersionDropperBlock(); disposableBag.AddDisposable(sourceBlock.LinkTo(versionDropper, sourceLinkOptions)); return(versionDropper.SyncLinkOptions <IProjectValueVersions>(sourceLinkOptions)); }); Action <Tuple <ImmutableList <IProjectValueVersions>, TIdentityDictionary> > action = ProjectPropertyChanged; var target = new ActionBlock <Tuple <ImmutableList <IProjectValueVersions>, TIdentityDictionary> >(action); var targetLinkOptions = new DataflowLinkOptions { PropagateCompletion = true }; ImmutableList <ProjectDataSources.SourceBlockAndLink <IProjectValueVersions> > sourceBlocksAndCapabilitiesOptions = sourceBlocks.ToImmutableList() .Insert(0, _projectVsServices.Project.Capabilities.SourceBlock.SyncLinkOptions <IProjectValueVersions>()); disposableBag.AddDisposable(ProjectDataSources.SyncLinkTo(sourceBlocksAndCapabilitiesOptions, target, targetLinkOptions)); _designTimeBuildSubscriptionLink = disposableBag; } }
protected override void SubscribeToConfiguredProject( ConfiguredProject configuredProject, IProjectSubscriptionService subscriptionService) { Subscribe( configuredProject, subscriptionService.ProjectRuleSource, ruleNames: new[] { ConfigurationGeneral.SchemaName }, "Dependencies Shared Projects Input: {1}", blocks => ProjectDataSources.SyncLinkTo( blocks.Intermediate.SyncLinkOptions(), subscriptionService.SharedFoldersSource.SourceBlock.SyncLinkOptions(), subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), configuredProject.Capabilities.SourceBlock.SyncLinkOptions(), blocks.Action, linkOptions: DataflowOption.PropagateCompletion)); }
protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) { _contextAccessor = await _workspaceProjectContextProvider.CreateProjectContextAsync(_project); if (_contextAccessor == null) { return; } _activeWorkspaceProjectContextTracker.RegisterContext(_contextAccessor.ContextId); _applyChangesToWorkspaceContext = _applyChangesToWorkspaceContextFactory.CreateExport(); _applyChangesToWorkspaceContext.Value.Initialize(_contextAccessor.Context); _evaluationProgressRegistration = _dataProgressTrackerService.RegisterForIntelliSense(this, _project, nameof(WorkspaceProjectContextHostInstance) + ".Evaluation"); _projectBuildProgressRegistration = _dataProgressTrackerService.RegisterForIntelliSense(this, _project, nameof(WorkspaceProjectContextHostInstance) + ".ProjectBuild"); _disposables = new DisposableBag { _applyChangesToWorkspaceContext, _evaluationProgressRegistration, _projectBuildProgressRegistration, ProjectDataSources.SyncLinkTo( _activeConfiguredProjectProvider.ActiveConfiguredProjectBlock.SyncLinkOptions(), _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(GetProjectEvaluationOptions()), _projectBuildSnapshotService.SourceBlock.SyncLinkOptions(), target: DataflowBlockFactory.CreateActionBlock <IProjectVersionedValue <ValueTuple <ConfiguredProject, IProjectSubscriptionUpdate, IProjectBuildSnapshot> > >(e => OnProjectChangedAsync(e, evaluation: true), _project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), linkOptions: DataflowOption.PropagateCompletion, cancellationToken: cancellationToken), ProjectDataSources.SyncLinkTo( _activeConfiguredProjectProvider.ActiveConfiguredProjectBlock.SyncLinkOptions(), _projectSubscriptionService.ProjectBuildRuleSource.SourceBlock.SyncLinkOptions(GetProjectBuildOptions()), _projectBuildSnapshotService.SourceBlock.SyncLinkOptions(), target: DataflowBlockFactory.CreateActionBlock <IProjectVersionedValue <ValueTuple <ConfiguredProject, IProjectSubscriptionUpdate, IProjectBuildSnapshot> > >(e => OnProjectChangedAsync(e, evaluation: false), _project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), linkOptions: DataflowOption.PropagateCompletion, cancellationToken: cancellationToken), }; }
private void SubscribeToConfiguredProject( ConfiguredProject configuredProject, IProjectSubscriptionService subscriptionService, IReadOnlyCollection <string> watchedEvaluationRules, IReadOnlyCollection <string> watchedJointRules) { Subscribe(RuleSource.Evaluation, subscriptionService.ProjectRuleSource, watchedEvaluationRules); Subscribe(RuleSource.Joint, subscriptionService.JointRuleSource, watchedJointRules); void Subscribe(RuleSource source, IProjectValueDataSource <IProjectSubscriptionUpdate> dataSource, IReadOnlyCollection <string> ruleNames) { // Use intermediate buffer blocks for project rule data to allow subsequent blocks // to only observe specific rule name(s). var intermediateBlock = new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >( new ExecutionDataflowBlockOptions() { NameFormat = string.Intern($"CrossTarget Intermediate {source} Input: {{1}}") }); ITargetBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > > actionBlock = DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > >( e => OnProjectChangedAsync(e.Value.Item1, e.Value.Item2, e.Value.Item3, configuredProject), new ExecutionDataflowBlockOptions() { NameFormat = string.Intern($"CrossTarget {source} Input: {{1}}") }); _subscriptions ??= new DisposableBag(); _subscriptions.Add( dataSource.SourceBlock.LinkTo( intermediateBlock, ruleNames: ruleNames, suppressVersionOnlyUpdates: true, linkOptions: DataflowOption.PropagateCompletion)); _subscriptions.Add(ProjectDataSources.SyncLinkTo( intermediateBlock.SyncLinkOptions(), subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), configuredProject.Capabilities.SourceBlock.SyncLinkOptions(), actionBlock, linkOptions: DataflowOption.PropagateCompletion)); } }
protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) { _projectGuid = await _project.UnconfiguredProject.GetProjectGuidAsync(); _joinedDataSources = ProjectDataSources.JoinUpstreamDataSources(JoinableFactory, _projectFaultHandlerService, _projectSubscriptionService.ProjectSource, _workloadDescriptorDataSource); _missingSetupComponentRegistrationService.RegisterProjectConfiguration(_projectGuid, _project); Action <IProjectVersionedValue <ValueTuple <IProjectSnapshot, ISet <WorkloadDescriptor> > > > action = OnWorkloadDescriptorsComputed; _subscription = ProjectDataSources.SyncLinkTo( _projectSubscriptionService.ProjectSource.SourceBlock.SyncLinkOptions(), _workloadDescriptorDataSource.SourceBlock.SyncLinkOptions(), DataflowBlockFactory.CreateActionBlock(action, _project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), linkOptions: DataflowOption.PropagateCompletion, cancellationToken: cancellationToken); }
/// <summary> /// Note: this is important to merge data source versions correctly here, since /// and different providers sending this event might have different processing time /// and we might end up in later data source versions coming before earlier ones. If /// we post greater versions before lower ones there will be exception and data flow /// might be broken after that. /// Another reason post data source versions here is that there could be other /// components waiting for Dependencies tree changes and if we don't post versions, /// they could not track our changes. /// </summary> private IImmutableDictionary <NamedIdentity, IComparable> GetMergedDataSourceVersions( DependenciesChangedEventArgs e) { IImmutableDictionary <NamedIdentity, IComparable> mergedDataSourcesVersions = null; lock (_latestDataSourcesVersions) { if (!string.IsNullOrEmpty(e.Provider.ProviderType) && e.DataSourceVersions != null) { _latestDataSourcesVersions[e.Provider.ProviderType] = e.DataSourceVersions; } mergedDataSourcesVersions = ProjectDataSources.MergeDataSourceVersions(_latestDataSourcesVersions.Values); } return(mergedDataSourcesVersions); }
/// <summary> /// Initialize the watcher. /// </summary> protected override Task InitializeCoreAsync(CancellationToken cancellationToken) { return(_projectTasksService.LoadedProjectAsync(() => { // The tree source to get changes to the tree so that we can identify when the assets file changes. ProjectDataSources.SourceBlockAndLink <IProjectVersionedValue <IProjectTreeSnapshot> > treeSource = _fileSystemTreeProvider.Tree.SyncLinkOptions(); // The property source used to get the value of the $ProjectAssetsFile property so that we can identify the location of the assets file. StandardRuleDataflowLinkOptions sourceLinkOptions = DataflowOption.WithRuleNames(ConfigurationGeneral.SchemaName); ProjectDataSources.SourceBlockAndLink <IProjectVersionedValue <IProjectSubscriptionUpdate> > propertySource = _activeConfiguredProjectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(sourceLinkOptions); ITargetBlock <IProjectVersionedValue <Tuple <IProjectTreeSnapshot, IProjectSubscriptionUpdate> > > target = DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <Tuple <IProjectTreeSnapshot, IProjectSubscriptionUpdate> > >(DataFlow_ChangedAsync); // Join the two sources so that we get synchronized versions of the data. _treeWatcher = ProjectDataSources.SyncLinkTo(treeSource, propertySource, target); return Task.CompletedTask; })); }
/// <summary> /// Initialize the watcher. /// </summary> protected override void Initialize() { _fileChangeService = _serviceProvider.GetService <IVsFileChangeEx, SVsFileChangeEx>(); // The tree source to get changes to the tree so that we can identify when the assets file changes. var treeSource = _fileSystemTreeProvider.Tree.SyncLinkOptions(); // The property source used to get the value of the $ProjectAssetsFile property so that we can identify the location of the assets file. var sourceLinkOptions = new StandardRuleDataflowLinkOptions { RuleNames = Empty.OrdinalIgnoreCaseStringSet.Add(ConfigurationGeneral.SchemaName), PropagateCompletion = true }; var propertySource = _activeConfiguredProjectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(sourceLinkOptions); var target = new ActionBlock <IProjectVersionedValue <Tuple <IProjectTreeSnapshot, IProjectSubscriptionUpdate> > >(new Action <IProjectVersionedValue <Tuple <IProjectTreeSnapshot, IProjectSubscriptionUpdate> > >(DataFlow_Changed)); // Join the two sources so that we get synchronized versions of the data. _treeWatcher = ProjectDataSources.SyncLinkTo(treeSource, propertySource, target); }
protected override IDisposable LinkExternalInput(ITargetBlock <RestoreInfo> targetBlock) { // At a high-level, we want to combine all implicitly active configurations (ie the active config of each TFM) restore data // (via ProjectRestoreUpdate) and combine it into a single IVsProjectRestoreInfo2 instance and publish that. When a change is // made to a configuration, such as adding a PackageReference, we should react to it and push a new version of our output. If the // active configuration changes, we should react to it, and publish data from the new set of implicitly active configurations. var disposables = new DisposableBag(); var restoreConfiguredInputSource = new UnwrapCollectionChainedProjectValueDataSource <IReadOnlyCollection <ConfiguredProject>, PackageRestoreConfiguredInput>( _project, projects => projects.Select(project => project.Services.ExportProvider.GetExportedValueOrDefault <IPackageRestoreConfiguredInputDataSource>()) .WhereNotNull() // Filter out those without PackageReference .Select(DropConfiguredProjectVersions), includeSourceVersions: true); disposables.Add(restoreConfiguredInputSource); IProjectValueDataSource <IConfigurationGroup <ConfiguredProject> > activeConfiguredProjectsSource = _activeConfigurationGroupService.ActiveConfiguredProjectGroupSource; disposables.Add(activeConfiguredProjectsSource.SourceBlock.LinkTo(restoreConfiguredInputSource, DataflowOption.PropagateCompletion)); // Dataflow from two configurations can depend on a same unconfigured level data source, and processes it at a different speed. // Introduce a forward-only block to prevent regressions in versions. var forwardOnlyBlock = ProjectDataSources.CreateDataSourceVersionForwardOnlyFilteringBlock <IReadOnlyCollection <PackageRestoreConfiguredInput> >(); disposables.Add(restoreConfiguredInputSource.SourceBlock.LinkTo(forwardOnlyBlock, DataflowOption.PropagateCompletion)); // Transform all restore data -> combined restore data DisposableValue <ISourceBlock <RestoreInfo> > mergeBlock = forwardOnlyBlock.TransformWithNoDelta(update => update.Derive(MergeRestoreInputs)); disposables.Add(mergeBlock); // Set the link up so that we publish changes to target block mergeBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion); // Join the source blocks, so if they need to switch to UI thread to complete // and someone is blocked on us on the same thread, the call proceeds JoinUpstreamDataSources(restoreConfiguredInputSource, activeConfiguredProjectsSource); _packageReferenceTelemetryService.PostPackageRestoreEvent(PackageRestoreOperationNames.PackageRestoreUnconfiguredInputDataSourceLinkedToExternalInput); return(disposables); }
private async Task ResetSubscriptionsAsync() { // active configuration should be updated before resetting subscriptions await RefreshActiveConfigurationAsync().ConfigureAwait(false); _designTimeBuildSubscriptionLink?.Dispose(); var currentProjects = await _activeConfiguredProjectsProvider.GetActiveConfiguredProjectsAsync() .ConfigureAwait(false); if (currentProjects != null) { var sourceLinkOptions = new StandardRuleDataflowLinkOptions { RuleNames = s_designTimeBuildWatchedRules, PropagateCompletion = true }; var disposableBag = new DisposableBag(CancellationToken.None); // We are taking source blocks from multiple configured projects and creating a SyncLink to combine the sources. // The SyncLink will only publish data when the versions of the sources match. There is a problem with that. // The sources have some version components that will make this impossible to match across TFMs. We introduce a // intermediate block here that will remove those version components so that the synclink can actually sync versions. var sourceBlocks = currentProjects.Objects.Select( cp => { var sourceBlock = cp.Services.ProjectSubscription.JointRuleSource.SourceBlock; var versionDropper = CreateVersionDropperBlock(); disposableBag.AddDisposable(sourceBlock.LinkTo(versionDropper, sourceLinkOptions)); return(versionDropper.SyncLinkOptions <IProjectValueVersions>(sourceLinkOptions)); }); var target = new ActionBlock <Tuple <ImmutableList <IProjectValueVersions>, TIdentityDictionary> >(ProjectPropertyChangedAsync); var targetLinkOptions = new DataflowLinkOptions { PropagateCompletion = true }; disposableBag.AddDisposable(ProjectDataSources.SyncLinkTo(sourceBlocks.ToImmutableList(), target, targetLinkOptions)); _designTimeBuildSubscriptionLink = disposableBag; } }
protected override Task InitializeCoreAsync(CancellationToken cancellationToken) { _joinedDataSources = ProjectDataSources.JoinUpstreamDataSources(JoinableFactory, _projectFaultHandlerService, _projectSubscriptionService.ProjectSource, _dataSource); _progressRegistration = _dataProgressTrackerService.RegisterOutputDataSource(this); Action <IProjectVersionedValue <ValueTuple <IProjectSnapshot, RestoreData> > > action = OnRestoreCompleted; _subscription = ProjectDataSources.SyncLinkTo( _projectSubscriptionService.ProjectSource.SourceBlock.SyncLinkOptions(), _dataSource.SourceBlock.SyncLinkOptions(), DataflowBlockFactory.CreateActionBlock(action, ConfiguredProject.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), linkOptions: DataflowOption.PropagateCompletion, cancellationToken: cancellationToken); _packageReferenceTelemetryService.PostPackageRestoreEvent(PackageRestoreOperationNames.PackageRestoreProgressTrackerInstanceInitialized, _packageRestoreProgressTrackerId); return(Task.CompletedTask); }
protected override IDisposable LinkExternalInput(ITargetBlock <IProjectVersionedValue <AssetsFileDependenciesSnapshot> > targetBlock) { JoinUpstreamDataSources(_activeConfiguredProjectSubscriptionService.ProjectRuleSource); JoinUpstreamDataSources(_activeConfiguredProjectSnapshotService); string? lastAssetsFilePath = null; DateTime lastTimestampUtc = DateTime.MinValue; AssetsFileDependenciesSnapshot lastSnapshot = AssetsFileDependenciesSnapshot.Empty; var intermediateBlock = new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >( new ExecutionDataflowBlockOptions { NameFormat = nameof(AssetsFileDependenciesDataSource) + " Intermediate: {1}" }); IReceivableSourceBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> > projectRuleSource = _activeConfiguredProjectSubscriptionService.ProjectRuleSource.SourceBlock; IPropagatorBlock <IProjectVersionedValue <ValueTuple <IProjectSnapshot, IProjectSubscriptionUpdate> >, IProjectVersionedValue <AssetsFileDependenciesSnapshot> > transformBlock = DataflowBlockSlim.CreateTransformBlock <IProjectVersionedValue <ValueTuple <IProjectSnapshot, IProjectSubscriptionUpdate> >, IProjectVersionedValue <AssetsFileDependenciesSnapshot> >(Transform, skipIntermediateInputData: true, skipIntermediateOutputData: true); return(new DisposableBag { // Subscribe to "ConfigurationGeneral" rule data projectRuleSource.LinkTo( intermediateBlock, ruleNames: ConfigurationGeneralRule.SchemaName, suppressVersionOnlyUpdates: false, linkOptions: PropagateCompletion()), // Sync link inputs, joining on versions, and passing joined data to our transform block ProjectDataSources.SyncLinkTo( _activeConfiguredProjectSnapshotService.SourceBlock.SyncLinkOptions(), intermediateBlock.SyncLinkOptions(), transformBlock, linkOptions: PropagateCompletion()), // Flow transformed data to the output/target transformBlock.LinkTo(targetBlock, PropagateCompletion()) });
/// <summary> /// Initialize the watcher. /// </summary> protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) { // Explicitly get back to the thread pool for the rest of this method so we don't tie up the UI thread; await TaskScheduler.Default; await _projectTasksService.LoadedProjectAsync(() => { // The tree source to get changes to the tree so that we can identify when the assets file changes. ProjectDataSources.SourceBlockAndLink <IProjectVersionedValue <IProjectTreeSnapshot> > treeSource = _fileSystemTreeProvider.Tree.SyncLinkOptions(); // The property source used to get the value of the $ProjectAssetsFile property so that we can identify the location of the assets file. StandardRuleDataflowLinkOptions sourceLinkOptions = DataflowOption.WithRuleNames(ConfigurationGeneral.SchemaName); ProjectDataSources.SourceBlockAndLink <IProjectVersionedValue <IProjectSubscriptionUpdate> > propertySource = _activeConfiguredProjectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(sourceLinkOptions); var target = new ActionBlock <IProjectVersionedValue <Tuple <IProjectTreeSnapshot, IProjectSubscriptionUpdate> > >(DataFlow_ChangedAsync); // Join the two sources so that we get synchronized versions of the data. _treeWatcher = ProjectDataSources.SyncLinkTo(treeSource, propertySource, target); return(Task.CompletedTask); }); }
/// <summary> /// This method is where we tell data flow which blocks we're interested in receiving updates for /// </summary> protected override IDisposable LinkExternalInput(ITargetBlock <IProjectVersionedValue <InputTuple> > targetBlock) { JoinUpstreamDataSources(_projectSubscriptionService.SourceItemsRuleSource, _projectSubscriptionService.ProjectRuleSource); return(ProjectDataSources.SyncLinkTo( _projectSubscriptionService.SourceItemsRuleSource.SourceBlock.SyncLinkOptions( linkOptions: new StandardRuleDataflowLinkOptions { RuleNames = Empty.OrdinalIgnoreCaseStringSet.Add(Compile.SchemaName), PropagateCompletion = true, }), _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions( linkOptions: new StandardRuleDataflowLinkOptions { RuleNames = Empty.OrdinalIgnoreCaseStringSet.Add(ConfigurationGeneral.SchemaName), PropagateCompletion = true, }), targetBlock, new DataflowLinkOptions { PropagateCompletion = true }, cancellationToken: ProjectAsynchronousTasksService.UnloadCancellationToken)); }
protected override IDisposable LinkExternalInput(ITargetBlock <IProjectVersionedValue <UpToDateCheckConfiguredInput> > targetBlock) { // Provides the set of implicitly active configured projects IProjectValueDataSource <IConfigurationGroup <ConfiguredProject> > activeConfiguredProjectsSource = _activeConfigurationGroupService.ActiveConfiguredProjectGroupSource; // Aggregates implicitly active UpToDateCheckImplicitConfiguredInput inputs from their sources var restoreConfiguredInputSource = new UnwrapCollectionChainedProjectValueDataSource <IReadOnlyCollection <ConfiguredProject>, UpToDateCheckImplicitConfiguredInput>( _configuredProject, projects => projects.Select(project => project.Services.ExportProvider.GetExportedValueOrDefault <IUpToDateCheckImplicitConfiguredInputDataSource>()) .WhereNotNull() // Filter out any configurations which don't have this export .Select(DropConfiguredProjectVersions), includeSourceVersions: true); // Dataflow from two configurations can depend on a same unconfigured level data source, and processes it at a different speed. // Introduce a forward-only block to prevent regressions in versions. var forwardOnlyBlock = ProjectDataSources.CreateDataSourceVersionForwardOnlyFilteringBlock <IReadOnlyCollection <UpToDateCheckImplicitConfiguredInput> >(); DisposableValue <ISourceBlock <IProjectVersionedValue <UpToDateCheckConfiguredInput> > > mergeBlock = forwardOnlyBlock.TransformWithNoDelta(update => update.Derive(MergeInputs)); JoinUpstreamDataSources(restoreConfiguredInputSource, activeConfiguredProjectsSource); return(new DisposableBag { restoreConfiguredInputSource, activeConfiguredProjectsSource.SourceBlock.LinkTo(restoreConfiguredInputSource, DataflowOption.PropagateCompletion), restoreConfiguredInputSource.SourceBlock.LinkTo(forwardOnlyBlock, DataflowOption.PropagateCompletion), mergeBlock, mergeBlock.Value.LinkTo(targetBlock, DataflowOption.PropagateCompletion) }); IProjectValueDataSource <UpToDateCheckImplicitConfiguredInput> DropConfiguredProjectVersions(IUpToDateCheckImplicitConfiguredInputDataSource dataSource) { // Wrap it in a data source that will drop project version and identity versions so as they will never agree // on these versions as they are unique to each configuration. They'll be consistent by all other versions. return(new DropConfiguredProjectVersionDataSource <UpToDateCheckImplicitConfiguredInput>(_configuredProject.UnconfiguredProject, dataSource)); }
protected override void Initialize() { _subscriptionLinks = new List <IDisposable>(); _projectSyncLinks = new List <IDisposable>(); using (UnconfiguredProjectAsynchronousTasksService.LoadedProject()) { // this.IsApplicable may take a project lock, so we can't do it inline with this method // which is holding a private lock. It turns out that doing it asynchronously isn't a problem anyway, // so long as we guard against races with the Dispose method. UnconfiguredProjectAsynchronousTasksService.LoadedProjectAsync( async delegate { await TaskScheduler.Default.SwitchTo(alwaysYield: true); UnconfiguredProjectAsynchronousTasksService. UnloadCancellationToken.ThrowIfCancellationRequested(); lock (SyncObject) { Verify.NotDisposed(this); var intermediateBlockDesignTime = new BufferBlock < IProjectVersionedValue < IProjectSubscriptionUpdate> >(); _subscriptionLinks.Add(ProjectSubscriptionService.JointRuleSource.SourceBlock.LinkTo( intermediateBlockDesignTime, ruleNames: UnresolvedReferenceRuleNames.Union(ResolvedReferenceRuleNames), suppressVersionOnlyUpdates: true)); var actionBlock = new ActionBlock < IProjectVersionedValue < Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectSharedFoldersSnapshot> > > (new Action < IProjectVersionedValue < Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectSharedFoldersSnapshot> > >( ProjectSubscriptionService_Changed), new ExecutionDataflowBlockOptions() { NameFormat = "ReferencesSubtree Input: {1}" }); _projectSyncLinks.Add(ProjectDataSources.SyncLinkTo( intermediateBlockDesignTime.SyncLinkOptions(), ProjectSubscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), ProjectSubscriptionService.SharedFoldersSource.SourceBlock.SyncLinkOptions(), actionBlock)); var intermediateBlockEvaluation = new BufferBlock < IProjectVersionedValue < IProjectSubscriptionUpdate> >(); _subscriptionLinks.Add(ProjectSubscriptionService.ProjectRuleSource.SourceBlock.LinkTo( intermediateBlockEvaluation, ruleNames: UnresolvedReferenceRuleNames, suppressVersionOnlyUpdates: true)); _projectSyncLinks.Add(ProjectDataSources.SyncLinkTo( intermediateBlockEvaluation.SyncLinkOptions(), ProjectSubscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), ProjectSubscriptionService.SharedFoldersSource.SourceBlock.SyncLinkOptions(), actionBlock)); } }, registerFaultHandler: true); } }
private void SubscribeToConfiguredProject( ConfiguredProject configuredProject, IProjectSubscriptionService subscriptionService, IReadOnlyCollection <string> watchedEvaluationRules, IReadOnlyCollection <string> watchedDesignTimeBuildRules) { // Use intermediate buffer blocks for project rule data to allow subsequent blocks // to only observe specific rule name(s). var intermediateBlockDesignTime = new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >( new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget Intermediate DesignTime Input: {1}" }); var intermediateBlockEvaluation = new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >( new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget Intermediate Evaluation Input: {1}" }); _subscriptions = _subscriptions ?? new DisposableBag(); _subscriptions.AddDisposable( subscriptionService.JointRuleSource.SourceBlock.LinkTo( intermediateBlockDesignTime, ruleNames: watchedDesignTimeBuildRules.Union(watchedEvaluationRules), suppressVersionOnlyUpdates: true, linkOptions: DataflowOption.PropagateCompletion)); _subscriptions.AddDisposable( subscriptionService.ProjectRuleSource.SourceBlock.LinkTo( intermediateBlockEvaluation, ruleNames: watchedEvaluationRules, suppressVersionOnlyUpdates: true, linkOptions: DataflowOption.PropagateCompletion)); ITargetBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > > actionBlockDesignTimeBuild = DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > >( e => OnProjectChangedAsync(e.Value.Item1, e.Value.Item2, e.Value.Item3, configuredProject, RuleHandlerType.DesignTimeBuild), new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget DesignTime Input: {1}" }); ITargetBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > > actionBlockEvaluation = DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > >( e => OnProjectChangedAsync(e.Value.Item1, e.Value.Item2, e.Value.Item3, configuredProject, RuleHandlerType.Evaluation), new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget Evaluation Input: {1}" }); _subscriptions.AddDisposable(ProjectDataSources.SyncLinkTo( intermediateBlockDesignTime.SyncLinkOptions(), subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), configuredProject.Capabilities.SourceBlock.SyncLinkOptions(), actionBlockDesignTimeBuild, linkOptions: DataflowOption.PropagateCompletion)); _subscriptions.AddDisposable(ProjectDataSources.SyncLinkTo( intermediateBlockEvaluation.SyncLinkOptions(), subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), configuredProject.Capabilities.SourceBlock.SyncLinkOptions(), actionBlockEvaluation, linkOptions: DataflowOption.PropagateCompletion)); }
protected override IDisposable LinkExternalInput(ITargetBlock <IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > targetBlock) { Assumes.Present(_configuredProject.Services.ProjectSubscription); bool attemptedStateRestore = false; // Initial state is empty. We will evolve this reference over time, updating it iteratively // on each new data update. UpToDateCheckImplicitConfiguredInput state = UpToDateCheckImplicitConfiguredInput.CreateEmpty(_configuredProject.ProjectConfiguration); IPropagatorBlock <IProjectVersionedValue <UpdateValues>, IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > transformBlock = DataflowBlockSlim.CreateTransformBlock <IProjectVersionedValue <UpdateValues>, IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> >(TransformAsync); IProjectValueDataSource <IProjectSubscriptionUpdate> source1 = _configuredProject.Services.ProjectSubscription.JointRuleSource; IProjectValueDataSource <IProjectSubscriptionUpdate> source2 = _configuredProject.Services.ProjectSubscription.SourceItemsRuleSource; IProjectItemSchemaService source3 = _projectItemSchemaService; IProjectValueDataSource <IProjectCatalogSnapshot> source4 = _configuredProject.Services.ProjectSubscription.ProjectCatalogSource; return(new DisposableBag { // Sync-link various sources to our transform block ProjectDataSources.SyncLinkTo( source1.SourceBlock.SyncLinkOptions(DataflowOption.WithRuleNames(ProjectPropertiesSchemas)), source2.SourceBlock.SyncLinkOptions(), source3.SourceBlock.SyncLinkOptions(), source4.SourceBlock.SyncLinkOptions(), target: transformBlock, linkOptions: DataflowOption.PropagateCompletion, CancellationToken.None), // Link the transform block to our target block transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion), JoinUpstreamDataSources(source1, source2, source3, source4) }); async Task <IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > TransformAsync(IProjectVersionedValue <UpdateValues> e) { if (!attemptedStateRestore) { attemptedStateRestore = true; if (_persistentState is not null) { // Restoring state requires the UI thread. We must use JTF.RunAsync here to ensure the UI // thread is shared between related work and prevent deadlocks. (int ItemHash, DateTime InputsChangedAtUtc)? restoredState = await JoinableFactory.RunAsync(() => _persistentState.RestoreStateAsync(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions, _projectAsynchronousTasksService.UnloadCancellationToken)); if (restoredState is not null) { state = state.WithRestoredState(restoredState.Value.ItemHash, restoredState.Value.InputsChangedAtUtc); } } } int? priorItemHash = state.ItemHash; DateTime priorLastItemsChangedAtUtc = state.LastItemsChangedAtUtc; state = state.Update( jointRuleUpdate: e.Value.Item1, sourceItemsUpdate: e.Value.Item2, projectItemSchema: e.Value.Item3, projectCatalogSnapshot: e.Value.Item4); if (state.ItemHash is not null && _persistentState is not null && (priorItemHash != state.ItemHash || priorLastItemsChangedAtUtc != state.LastItemsChangedAtUtc)) { await _persistentState.StoreStateAsync(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions, state.ItemHash.Value, state.LastItemsChangedAtUtc, _projectAsynchronousTasksService.UnloadCancellationToken); } return(new ProjectVersionedValue <UpToDateCheckImplicitConfiguredInput>(state, e.DataSourceVersions)); } }
private void SubscribeToConfiguredProject( ConfiguredProject configuredProject, IProjectSubscriptionService subscriptionService, IEnumerable <string> watchedEvaluationRules, IEnumerable <string> watchedDesignTimeBuildRules) { var intermediateBlockDesignTime = new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >( new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget Intermediate DesignTime Input: {1}" }); var intermediateBlockEvaluation = new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >( new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget Intermediate Evaluation Input: {1}" }); _designTimeBuildSubscriptionLinks.Add( subscriptionService.JointRuleSource.SourceBlock.LinkTo( intermediateBlockDesignTime, ruleNames: watchedDesignTimeBuildRules.Union(watchedEvaluationRules), suppressVersionOnlyUpdates: true, linkOptions: new DataflowLinkOptions { PropagateCompletion = true })); _evaluationSubscriptionLinks.Add( subscriptionService.ProjectRuleSource.SourceBlock.LinkTo( intermediateBlockEvaluation, ruleNames: watchedEvaluationRules, suppressVersionOnlyUpdates: true, linkOptions: new DataflowLinkOptions { PropagateCompletion = true })); var actionBlockDesignTimeBuild = new ActionBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > >( e => OnProjectChangedAsync(e, configuredProject, RuleHandlerType.DesignTimeBuild), new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget DesignTime Input: {1}" }); var actionBlockEvaluation = new ActionBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > >( e => OnProjectChangedAsync(e, configuredProject, RuleHandlerType.Evaluation), new ExecutionDataflowBlockOptions() { NameFormat = "CrossTarget Evaluation Input: {1}" }); _designTimeBuildSubscriptionLinks.Add(ProjectDataSources.SyncLinkTo( intermediateBlockDesignTime.SyncLinkOptions(), subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), configuredProject.Capabilities.SourceBlock.SyncLinkOptions(), actionBlockDesignTimeBuild, linkOptions: new DataflowLinkOptions { PropagateCompletion = true })); _evaluationSubscriptionLinks.Add(ProjectDataSources.SyncLinkTo( intermediateBlockEvaluation.SyncLinkOptions(), subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(), configuredProject.Capabilities.SourceBlock.SyncLinkOptions(), actionBlockEvaluation, linkOptions: new DataflowLinkOptions { PropagateCompletion = true })); }
protected override async Task InitializeCoreAsync(CancellationToken cancellationToken) { _contextAccessor = await _workspaceProjectContextProvider.CreateProjectContextAsync(_project); if (_contextAccessor == null) { return; } _activeWorkspaceProjectContextTracker.RegisterContext(_contextAccessor.ContextId); _applyChangesToWorkspaceContext = _applyChangesToWorkspaceContextFactory.CreateExport(); _applyChangesToWorkspaceContext.Value.Initialize(_contextAccessor.Context); _evaluationProgressRegistration = _dataProgressTrackerService.RegisterForIntelliSense(this, _project, nameof(WorkspaceProjectContextHostInstance) + ".Evaluation"); _projectBuildProgressRegistration = _dataProgressTrackerService.RegisterForIntelliSense(this, _project, nameof(WorkspaceProjectContextHostInstance) + ".ProjectBuild"); _disposables = new DisposableBag { _applyChangesToWorkspaceContext, _evaluationProgressRegistration, _projectBuildProgressRegistration, ProjectDataSources.SyncLinkTo( _activeConfiguredProjectProvider.ActiveConfiguredProjectBlock.SyncLinkOptions(), _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(GetProjectEvaluationOptions()), _projectSubscriptionService.SourceItemsRuleSource.SourceBlock.SyncLinkOptions(), target: DataflowBlockFactory.CreateActionBlock <IProjectVersionedValue <(ConfiguredProject, IProjectSubscriptionUpdate, IProjectSubscriptionUpdate)> >( OnEvaluationUpdateAsync, _project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), linkOptions: DataflowOption.PropagateCompletion, cancellationToken: cancellationToken), ProjectDataSources.SyncLinkTo( _activeConfiguredProjectProvider.ActiveConfiguredProjectBlock.SyncLinkOptions(), _projectSubscriptionService.ProjectBuildRuleSource.SourceBlock.SyncLinkOptions(GetProjectBuildOptions()), _commandLineArgumentsProvider.SourceBlock.SyncLinkOptions(), target: DataflowBlockFactory.CreateActionBlock <IProjectVersionedValue <(ConfiguredProject, IProjectSubscriptionUpdate, CommandLineArgumentsSnapshot)> >( OnBuildUpdateAsync, _project.UnconfiguredProject, ProjectFaultSeverity.LimitedFunctionality), linkOptions: DataflowOption.PropagateCompletion, cancellationToken: cancellationToken) }; return; StandardRuleDataflowLinkOptions GetProjectEvaluationOptions() { return(DataflowOption.WithRuleNames(_applyChangesToWorkspaceContext.Value.GetProjectEvaluationRules())); } StandardRuleDataflowLinkOptions GetProjectBuildOptions() { return(DataflowOption.WithRuleNames(_applyChangesToWorkspaceContext.Value.GetProjectBuildRules())); } Task OnEvaluationUpdateAsync(IProjectVersionedValue <(ConfiguredProject ActiveConfiguredProject, IProjectSubscriptionUpdate ProjectUpdate, IProjectSubscriptionUpdate SourceItemsUpdate)> e) { return(OnProjectChangedAsync( _evaluationProgressRegistration, e.Value.ActiveConfiguredProject, e, hasChange: static e => e.Value.ProjectUpdate.ProjectChanges.HasChange() || e.Value.SourceItemsUpdate.ProjectChanges.HasChange(), applyFunc: static (e, applyChangesToWorkspaceContext, contextState, token) => applyChangesToWorkspaceContext.ApplyProjectEvaluation(e.Derive(v => (v.ProjectUpdate, v.SourceItemsUpdate)), contextState, token))); } Task OnBuildUpdateAsync(IProjectVersionedValue <(ConfiguredProject ActiveConfiguredProject, IProjectSubscriptionUpdate BuildUpdate, CommandLineArgumentsSnapshot CommandLineArgumentsUpdate)> e) { return(OnProjectChangedAsync( _projectBuildProgressRegistration, e.Value.ActiveConfiguredProject, e, hasChange: static e => e.Value.BuildUpdate.ProjectChanges.HasChange() || e.Value.CommandLineArgumentsUpdate.IsChanged, applyFunc: static (e, applyChangesToWorkspaceContext, contextState, token) => applyChangesToWorkspaceContext.ApplyProjectBuild(e.Derive(v => (v.BuildUpdate, v.CommandLineArgumentsUpdate)), contextState, token))); } }