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 packageRestoreConfiguredSource = new UnwrapCollectionChainedProjectValueDataSource <IReadOnlyCollection <ConfiguredProject>, ProjectRestoreUpdate>( _project.Services, projects => projects.Select(project => GetProjectRestoreDataSource(project)), includeSourceVersions: true); disposables.AddDisposable(packageRestoreConfiguredSource); IProjectValueDataSource <IConfigurationGroup <ConfiguredProject> > activeConfiguredProjectsSource = _activeConfigurationGroupService.ActiveConfiguredProjectGroupSource; disposables.AddDisposable(activeConfiguredProjectsSource.SourceBlock.LinkTo(packageRestoreConfiguredSource, DataflowOption.PropagateCompletion)); // Transform all restore data -> combined restore data DisposableValue <ISourceBlock <RestoreInfo> > mergeBlock = packageRestoreConfiguredSource.SourceBlock .TransformWithNoDelta(update => update.Derive(MergeRestoreData)); disposables.AddDisposable(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(packageRestoreConfiguredSource, activeConfiguredProjectsSource); return(disposables); }
public static IActiveConfigurationGroupService Implement(IProjectValueDataSource <IConfigurationGroup <ProjectConfiguration> > source) { var mock = new Mock <IActiveConfigurationGroupService>(); mock.SetupGet(s => s.ActiveConfigurationGroupSource) .Returns(source); return(mock.Object); }
protected override IDisposable LinkExternalInput(ITargetBlock <IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > targetBlock) { Assumes.Present(_configuredProject.Services.ProjectSubscription); // Initial state is empty. We will evolve this reference over time, updating it iteratively // on each new data update. UpToDateCheckImplicitConfiguredInput state = UpToDateCheckImplicitConfiguredInput.Empty; IPropagatorBlock <IProjectVersionedValue <UpdateValues>, IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > transformBlock = DataflowBlockSlim.CreateTransformBlock <IProjectVersionedValue <UpdateValues>, IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> >(Transform); IProjectValueDataSource <IProjectSubscriptionUpdate> source1 = _configuredProject.Services.ProjectSubscription.JointRuleSource; IProjectValueDataSource <IProjectSubscriptionUpdate> source2 = _configuredProject.Services.ProjectSubscription.SourceItemsRuleSource; IProjectValueDataSource <IProjectSnapshot> source3 = _configuredProject.Services.ProjectSubscription.ProjectSource; IProjectItemSchemaService source4 = _projectItemSchemaService; IProjectValueDataSource <IProjectCatalogSnapshot> source5 = _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(), source5.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, source5) }); IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> Transform(IProjectVersionedValue <UpdateValues> e) { var snapshot = e.Value.Item3 as IProjectSnapshot2; Assumes.NotNull(snapshot); state = state.Update( jointRuleUpdate: e.Value.Item1, sourceItemsUpdate: e.Value.Item2, projectSnapshot: snapshot, projectItemSchema: e.Value.Item4, projectCatalogSnapshot: e.Value.Item5, configuredProjectVersion: e.DataSourceVersions[ProjectDataSources.ConfiguredProjectVersion]); return(new ProjectVersionedValue <UpToDateCheckImplicitConfiguredInput>(state, e.DataSourceVersions)); } }
public static IActiveConfiguredProjectSubscriptionService Create(IProjectValueDataSource <IProjectSubscriptionUpdate>?sourceItemsRuleSource = null) { var mock = new Mock <IActiveConfiguredProjectSubscriptionService>(); mock.SetupGet(s => s.ProjectRuleSource) .Returns(() => IProjectValueDataSourceFactory.CreateInstance <IProjectSubscriptionUpdate>()); if (sourceItemsRuleSource != null) { mock.SetupGet(s => s.SourceItemsRuleSource) .Returns(() => sourceItemsRuleSource); } return(mock.Object); }
protected override IDisposable LinkExternalInput(ITargetBlock <RestoreUpdate> targetBlock) { IProjectValueDataSource <IProjectSubscriptionUpdate> source = _projectSubscriptionService.JointRuleSource; // Transform the changes from evaluation/design-time build -> restore data DisposableValue <ISourceBlock <RestoreUpdate> > transformBlock = source.SourceBlock.TransformWithNoDelta(update => update.Derive(u => CreateRestoreInput(u.ProjectConfiguration, u.CurrentState)), suppressVersionOnlyUpdates: false, // We need to coordinate these at the unconfigured-level ruleNames: s_rules); // Set the link up so that we publish changes to target block transformBlock.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(source); return(transformBlock); }
protected override IDisposable?LinkExternalInput(ITargetBlock <IProjectVersionedValue <ISet <WorkloadDescriptor> > > targetBlock) { IProjectValueDataSource <IProjectSubscriptionUpdate> source = _projectSubscriptionService.ProjectBuildRuleSource; // Transform the changes from design-time build -> workload data DisposableValue <ISourceBlock <IProjectVersionedValue <ISet <WorkloadDescriptor> > > > transformBlock = source.SourceBlock.TransformWithNoDelta(update => update.Derive(u => CreateWorkloadDescriptor(u.CurrentState)), suppressVersionOnlyUpdates: false, ruleNames: s_rules); // Set the link up so that we publish changes to target block transformBlock.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(source); return(transformBlock); }
protected override IDisposable?LinkExternalInput(ITargetBlock <EnumCollectionProjectValue> targetBlock) { IProjectValueDataSource <IProjectSubscriptionUpdate> source = SubscriptionService.ProjectRuleSource; // Transform the values from evaluation to structure from the rule schema. DisposableValue <ISourceBlock <EnumCollectionProjectValue> > transformBlock = source.SourceBlock.TransformWithNoDelta( update => update.Derive(Transform), suppressVersionOnlyUpdates: false, ruleNames: RuleNames); // Set the link up so that we publish changes to target block. transformBlock.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(source); return(transformBlock); }
protected override IDisposable LinkExternalInput(ITargetBlock <IProjectVersionedValue <FileWatchData> > targetBlock) { IProjectValueDataSource <IProjectSubscriptionUpdate> source = _projectSubscriptionService.ProjectRuleSource; // Transform the changes from evaluation -> FileWatchData DisposableValue <ISourceBlock <IProjectVersionedValue <FileWatchData> > > transformBlock = source.SourceBlock .TransformWithNoDelta(update => update.Derive(u => CreateFileWatch(u.CurrentState)), suppressVersionOnlyUpdates: true, ruleNames: new[] { ItemSchemaName }); // Set the link up so that we publish changes to target block transformBlock.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(source); return(transformBlock); }
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); }
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 void Subscribe( ConfiguredProject configuredProject, IProjectValueDataSource <IProjectSubscriptionUpdate> dataSource, string[] ruleNames, string nameFormat, Func <(BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> > Intermediate, ITargetBlock <IProjectVersionedValue <T> > Action), IDisposable> syncLink)
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)); } }
public DropConfiguredProjectVersionDataSource(IProjectServices commonServices, IProjectValueDataSource <T> dataSource) : base(commonServices, synchronousDisposal: true, registerDataSource: false) { _dataSource = dataSource; }
public DropConfiguredProjectVersionDataSource(UnconfiguredProject project, IProjectValueDataSource <T> dataSource) : base(project, synchronousDisposal: true, registerDataSource: false) { _dataSource = dataSource; }