Esempio n. 1
0
        public async Task <string> GetTempPEDescriptionXmlAsync(string moniker)
        {
            Requires.NotNull(moniker, nameof(moniker));

            await InitializeAsync();

            await _unconfiguredProjectServices.ThreadingService.SwitchToUIThread();

            DesignTimeInputsItem inputs = AppliedValue.Value;

            if (!inputs.Inputs.Contains(moniker))
            {
                throw new ArgumentException("Moniker supplied must be one of the DesignTime source files", nameof(moniker));
            }

            string outputPath     = inputs.OutputPath;
            string outputFileName = GetOutputFileName(outputPath, moniker);

            // For parity with legacy we don't care about the compilation result: Legacy only errors here if it runs out of memory queuing the compilation
            // Additionally for parity, we compile here on the UI thread and block (whilst still preventing simultaneous work)
            await ScheduleCompilationAsync(moniker, inputs, waitForCompletion : true, forceCompilation : false);

            // VSTypeResolutionService is the only consumer, and it only uses the codebase element so it's fine to default most of these (VC++ does the same)
            return($@"<root>
  <Application private_binpath = ""{outputPath}""/>
  <Assembly
    codebase = ""{outputFileName}""
    name = ""{moniker}""
    version = ""0.0.0.0""
    snapshot_id = ""1""
    replaceable = ""True""
  />
</root>");
        }
Esempio n. 2
0
        private void DisposeTaskSchedulers()
        {
            DesignTimeInputsItem value = AppliedValue?.Value;

            if (value == null)
            {
                return;
            }

            foreach (ITaskDelayScheduler scheduler in value.TaskSchedulers.Values)
            {
                scheduler.Dispose();
            }
        }
Esempio n. 3
0
        private async Task ScheduleCompilationAsync(string moniker, DesignTimeInputsItem inputs, bool waitForCompletion, bool forceCompilation)
        {
            HashSet <string> files          = GetFilesToCompile(moniker, inputs.SharedInputs);
            string           outputFileName = GetOutputFileName(inputs.OutputPath, moniker);

            if (forceCompilation || CompilationNeeded(files, outputFileName))
            {
                if (inputs.TaskSchedulers.TryGetValue(moniker, out ITaskDelayScheduler scheduler))
                {
                    if (waitForCompletion)
                    {
                        await scheduler.RunAsyncTask(token => CompileTempPEAsync(files, outputFileName, token)).Task;
                    }
                    else
                    {
                        _projectFaultHandlerService.Forget(scheduler.ScheduleAsyncTask(token => CompileTempPEAsync(files, outputFileName, token)).Task, UnconfiguredProject);
                    }
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// ApplyAsync is called on the UI thread and its job is to update AppliedValue to be correct based on the changes that have come through data flow after being processed
        /// </summary>
        protected override async Task ApplyAsync(DesignTimeInputsDelta value)
        {
            // Not using use the ThreadingService property because unit tests
            await _unconfiguredProjectServices.ThreadingService.SwitchToUIThread();

            DesignTimeInputsItem previousValue = AppliedValue?.Value ?? new DesignTimeInputsItem();

            // Calculate the new value
            ImmutableHashSet <string>          newDesignTimeInputs;
            ImmutableHashSet <string>          newSharedDesignTimeInputs;
            ImmutableDictionary <string, uint> newCookies;
            ImmutableDictionary <string, ITaskDelayScheduler> newSchedulers;
            var  addedDesignTimeInputs            = new List <string>();
            var  removedDesignTimeInputs          = new List <string>();
            bool hasRemovedDesignTimeSharedInputs = false;
            bool hasAddedDesignTimeSharedInputs   = false;

            if (value.HasFileChanges)
            {
                var designTimeInputs       = previousValue.Inputs.ToBuilder();
                var designTimeSharedInputs = previousValue.SharedInputs.ToBuilder();
                var cookies    = previousValue.Cookies.ToBuilder();
                var schedulers = previousValue.TaskSchedulers.ToBuilder();

                foreach (string item in value.AddedItems)
                {
                    if (designTimeInputs.Add(item))
                    {
                        addedDesignTimeInputs.Add(item);
                        schedulers.Add(item, CreateTaskScheduler());
                    }
                }

                foreach (string item in value.RemovedItems)
                {
                    if (designTimeInputs.Remove(item))
                    {
                        removedDesignTimeInputs.Add(item);
                        // We only unsubscribe from file changes if there is no other reason to care about this file
                        if (TryGetValueIfUnused(item, cookies, designTimeSharedInputs, out _))
                        {
                            cookies.Remove(item);
                        }
                        if (TryGetValueIfUnused(item, schedulers, designTimeSharedInputs, out ITaskDelayScheduler scheduler))
                        {
                            schedulers.Remove(item);
                            scheduler.Dispose();
                        }
                    }
                }

                foreach (string item in value.AddedSharedItems)
                {
                    if (designTimeSharedInputs.Add(item))
                    {
                        hasAddedDesignTimeSharedInputs = true;
                        if (!schedulers.ContainsKey(item))
                        {
                            schedulers.Add(item, CreateTaskScheduler());
                        }
                    }
                }

                foreach (string item in value.RemovedSharedItems)
                {
                    if (designTimeSharedInputs.Remove(item))
                    {
                        hasRemovedDesignTimeSharedInputs = true;
                        if (TryGetValueIfUnused(item, cookies, designTimeInputs, out _))
                        {
                            cookies.Remove(item);
                        }
                        if (TryGetValueIfUnused(item, schedulers, designTimeInputs, out ITaskDelayScheduler scheduler))
                        {
                            schedulers.Remove(item);
                            scheduler.Dispose();
                        }
                    }
                }

                newDesignTimeInputs       = designTimeInputs.ToImmutable();
                newSharedDesignTimeInputs = designTimeSharedInputs.ToImmutable();
                newCookies    = cookies.ToImmutable();
                newSchedulers = schedulers.ToImmutable();
            }
            else
            {
                // If there haven't been file changes we can just flow our previous collections to the new version to avoid roundtriping our collections to builders and back
                newDesignTimeInputs       = previousValue.Inputs;
                newSharedDesignTimeInputs = previousValue.SharedInputs;
                newCookies    = previousValue.Cookies;
                newSchedulers = previousValue.TaskSchedulers;
            }

            // Apply our new value
            AppliedValue = new ProjectVersionedValue <DesignTimeInputsItem>(new DesignTimeInputsItem
            {
                Inputs       = newDesignTimeInputs,
                SharedInputs = newSharedDesignTimeInputs,
                // We always need an output path, so if it hasn't changed we just reuse the previous value
                OutputPath     = value.OutputPath ?? previousValue.OutputPath,
                Cookies        = newCookies,
                TaskSchedulers = newSchedulers
            }, value.DataSourceVersions);

            // Project properties changes cause all PEs to be dirty and recompile if this isn't the first update
            if (value.HasProjectPropertyChanges)
            {
                foreach (string item in newDesignTimeInputs)
                {
                    await FireTempPEDirtyAsync(item, value.ShouldCompile);
                }
            }
            else
            {
                // Individual inputs dirty their PEs and possibly recompile
                foreach (string item in addedDesignTimeInputs)
                {
                    await FireTempPEDirtyAsync(item, value.ShouldCompile);
                }
            }

            // Shared items cause all TempPEs to be dirty, but don't recompile, to match legacy behaviour
            if (hasRemovedDesignTimeSharedInputs || hasAddedDesignTimeSharedInputs)
            {
                // adding or removing shared design time inputs dirties things but doesn't recompile
                foreach (string item in newDesignTimeInputs)
                {
                    // We don't want to fire again if we already fired above and compiled
                    if (!addedDesignTimeInputs.Contains(item))
                    {
                        await FireTempPEDirtyAsync(item, false);
                    }
                }
            }

            foreach (string item in removedDesignTimeInputs)
            {
                BuildManager.OnDesignTimeOutputDeleted(item);
            }