You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
10 KiB
311 lines
10 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Threading; |
|
using System.Threading.Tasks; |
|
|
|
using ICSharpCode.Core; |
|
using ICSharpCode.SharpDevelop.Dom; |
|
using ICSharpCode.SharpDevelop.Gui; |
|
using ICSharpCode.SharpDevelop.Project; |
|
|
|
namespace ICSharpCode.SharpDevelop |
|
{ |
|
public sealed class ParseProjectContent : DefaultProjectContent |
|
{ |
|
public ParseProjectContent(IProject project) |
|
{ |
|
if (project == null) |
|
throw new ArgumentNullException("project"); |
|
this.project = project; |
|
this.Language = project.LanguageProperties; |
|
this.initializing = true; |
|
} |
|
|
|
readonly IProject project; |
|
|
|
public override object Project { |
|
get { |
|
return project; |
|
} |
|
} |
|
|
|
public string ProjectName { |
|
get { return project.Name; } |
|
} |
|
|
|
public override string AssemblyName { |
|
get { return project.AssemblyName; } |
|
} |
|
|
|
bool initializing; |
|
|
|
public override string ToString() |
|
{ |
|
return string.Format("[{0}: {1}]", GetType().Name, project.Name); |
|
} |
|
|
|
internal void Initialize1(IProgressMonitor progressMonitor) |
|
{ |
|
ICollection<ProjectItem> items = project.Items; |
|
ProjectService.ProjectItemAdded += OnProjectItemAdded; |
|
ProjectService.ProjectItemRemoved += OnProjectItemRemoved; |
|
UpdateDefaultImports(items); |
|
// TODO: Translate me |
|
// progressMonitor.TaskName = "Resolving references for " + project.Name + "..."; |
|
AbstractProject abstractProject = project as AbstractProject; |
|
if (abstractProject != null) { |
|
foreach (var reference in abstractProject.ResolveAssemblyReferences(progressMonitor.CancellationToken)) { |
|
if (!initializing) return; // abort initialization |
|
AddReference(reference, false, progressMonitor.CancellationToken); |
|
} |
|
} else { |
|
project.ResolveAssemblyReferences(); |
|
AddReferencedContent(AssemblyParserService.DefaultProjectContentRegistry.Mscorlib); |
|
foreach (ProjectItem item in items) { |
|
if (!initializing) return; // abort initialization |
|
progressMonitor.CancellationToken.ThrowIfCancellationRequested(); |
|
if (ItemType.ReferenceItemTypes.Contains(item.ItemType)) { |
|
ReferenceProjectItem reference = item as ReferenceProjectItem; |
|
if (reference != null) { |
|
AddReference(reference, false, progressMonitor.CancellationToken); |
|
} |
|
} |
|
} |
|
} |
|
UpdateReferenceInterDependencies(); |
|
OnReferencedContentsChanged(EventArgs.Empty); |
|
} |
|
|
|
internal void ReInitialize1(IProgressMonitor progressMonitor) |
|
{ |
|
// prevent adding event handler twice |
|
ProjectService.ProjectItemAdded -= OnProjectItemAdded; |
|
ProjectService.ProjectItemRemoved -= OnProjectItemRemoved; |
|
initializing = true; |
|
try { |
|
Initialize1(progressMonitor); |
|
} finally { |
|
initializing = false; |
|
} |
|
} |
|
|
|
void UpdateReferenceInterDependencies() |
|
{ |
|
// Use ToArray because the collection could be modified inside the loop |
|
IProjectContent[] referencedContents; |
|
lock (this.ReferencedContents) { |
|
referencedContents = new IProjectContent[this.ReferencedContents.Count]; |
|
this.ReferencedContents.CopyTo(referencedContents, 0); |
|
} |
|
foreach (IProjectContent referencedContent in referencedContents) { |
|
if (referencedContent is ReflectionProjectContent) { |
|
((ReflectionProjectContent)referencedContent).InitializeReferences(referencedContents); |
|
} |
|
} |
|
} |
|
|
|
void AddReference(ReferenceProjectItem reference, bool updateInterDependencies, CancellationToken cancellationToken) |
|
{ |
|
try { |
|
cancellationToken.ThrowIfCancellationRequested(); |
|
AddReferencedContent(AssemblyParserService.GetProjectContentForReference(reference)); |
|
if (updateInterDependencies) { |
|
UpdateReferenceInterDependencies(); |
|
} |
|
OnReferencedContentsChanged(EventArgs.Empty); |
|
|
|
// Refresh the reference if required. |
|
// If the user removes the reference and then re-adds it, there might be other references |
|
// in the project depending on it, so we do the refresh after the old reference was added. |
|
AssemblyParserService.RefreshProjectContentForReference(reference); |
|
} catch (OperationCanceledException) { |
|
throw; |
|
} catch (ObjectDisposedException e) { |
|
// ObjectDisposedException can happen if project gets disposed while LoadSolutionProjectsThread is running. |
|
// We will ignore the ObjectDisposedException and throw OperationCanceledException instead. |
|
cancellationToken.ThrowIfCancellationRequested(); |
|
MessageService.ShowException(e); |
|
} catch (Exception e) { |
|
MessageService.ShowException(e); |
|
} |
|
} |
|
|
|
// ensure that com references are built serially because we cannot invoke multiple instances of MSBuild |
|
static Queue<System.Windows.Forms.MethodInvoker> callAfterAddComReference = new Queue<System.Windows.Forms.MethodInvoker>(); |
|
static bool buildingComReference; |
|
|
|
void OnProjectItemAdded(object sender, ProjectItemEventArgs e) |
|
{ |
|
if (e.Project != project) return; |
|
|
|
ReferenceProjectItem reference = e.ProjectItem as ReferenceProjectItem; |
|
if (reference != null) { |
|
if (reference.ItemType == ItemType.COMReference) { |
|
System.Windows.Forms.MethodInvoker action = delegate { |
|
// Compile project to ensure interop library is generated |
|
project.Save(); // project is not yet saved when ItemAdded fires, so save it here |
|
string message = StringParser.Parse("\n${res:MainWindow.CompilerMessages.CreatingCOMInteropAssembly}\n"); |
|
TaskService.BuildMessageViewCategory.AppendText(message); |
|
BuildCallback afterBuildCallback = delegate { |
|
lock (callAfterAddComReference) { |
|
if (callAfterAddComReference.Count > 0) { |
|
// run next enqueued action |
|
callAfterAddComReference.Dequeue()(); |
|
} else { |
|
buildingComReference = false; |
|
ParserService.Reparse(project, true, false); |
|
} |
|
} |
|
}; |
|
BuildEngine.BuildInGui(project, new BuildOptions(BuildTarget.ResolveComReferences, afterBuildCallback)); |
|
}; |
|
|
|
// enqueue actions when adding multiple COM references so that multiple builds of the same project |
|
// are not started parallely |
|
lock (callAfterAddComReference) { |
|
if (buildingComReference) { |
|
callAfterAddComReference.Enqueue(action); |
|
} else { |
|
buildingComReference = true; |
|
action(); |
|
} |
|
} |
|
} else { |
|
ParserService.Reparse(project, true, false); |
|
} |
|
} |
|
if (e.ProjectItem.ItemType == ItemType.Import) { |
|
UpdateDefaultImports(project.Items); |
|
} else if (e.ProjectItem.ItemType == ItemType.Compile) { |
|
if (System.IO.File.Exists(e.ProjectItem.FileName)) { |
|
ParserService.BeginParse(e.ProjectItem.FileName); |
|
} |
|
} |
|
} |
|
|
|
void OnProjectItemRemoved(object sender, ProjectItemEventArgs e) |
|
{ |
|
if (e.Project != project) return; |
|
|
|
ReferenceProjectItem reference = e.ProjectItem as ReferenceProjectItem; |
|
if (reference != null) { |
|
try { |
|
IProjectContent referencedContent = AssemblyParserService.GetExistingProjectContentForReference(reference); |
|
if (referencedContent != null) { |
|
lock (ReferencedContents) { |
|
ReferencedContents.Remove(referencedContent); |
|
} |
|
OnReferencedContentsChanged(EventArgs.Empty); |
|
} |
|
} catch (Exception ex) { |
|
MessageService.ShowException(ex); |
|
} |
|
} |
|
|
|
if (e.ProjectItem.ItemType == ItemType.Import) { |
|
UpdateDefaultImports(project.Items); |
|
} else if (e.ProjectItem.ItemType == ItemType.Compile) { |
|
ParserService.ClearParseInformation(e.ProjectItem.FileName); |
|
} |
|
} |
|
|
|
int languageDefaultImportCount = -1; |
|
|
|
void UpdateDefaultImports(ICollection<ProjectItem> items) |
|
{ |
|
if (languageDefaultImportCount < 0) { |
|
languageDefaultImportCount = (DefaultImports != null) ? DefaultImports.Usings.Count : 0; |
|
} |
|
if (languageDefaultImportCount == 0) { |
|
DefaultImports = null; |
|
} else { |
|
while (DefaultImports.Usings.Count > languageDefaultImportCount) { |
|
DefaultImports.Usings.RemoveAt(languageDefaultImportCount); |
|
} |
|
} |
|
foreach (ProjectItem item in items) { |
|
if (item.ItemType == ItemType.Import) { |
|
if (DefaultImports == null) { |
|
DefaultImports = new DefaultUsing(this); |
|
} |
|
DefaultImports.Usings.Add(item.Include); |
|
} |
|
} |
|
} |
|
|
|
internal int GetInitializationWorkAmount() |
|
{ |
|
return project.Items.Count; |
|
} |
|
|
|
internal void ReInitialize2(IProgressMonitor progressMonitor) |
|
{ |
|
if (initializing) return; |
|
initializing = true; |
|
Initialize2(progressMonitor); |
|
} |
|
|
|
internal void Initialize2(IProgressMonitor progressMonitor) |
|
{ |
|
if (!initializing) return; |
|
try { |
|
IProjectContent[] referencedContents; |
|
lock (this.ReferencedContents) { |
|
referencedContents = new IProjectContent[this.ReferencedContents.Count]; |
|
this.ReferencedContents.CopyTo(referencedContents, 0); |
|
} |
|
|
|
foreach (IProjectContent referencedContent in referencedContents) { |
|
if (referencedContent is ReflectionProjectContent) { |
|
((ReflectionProjectContent)referencedContent).InitializeReferences(referencedContents); |
|
} |
|
} |
|
|
|
ParseableFileContentFinder finder = new ParseableFileContentFinder(); |
|
var fileContents = ( |
|
from p in project.Items.AsParallel().WithCancellation(progressMonitor.CancellationToken) |
|
where !ItemType.NonFileItemTypes.Contains(p.ItemType) && !String.IsNullOrEmpty(p.FileName) |
|
select FileName.Create(p.FileName) |
|
).ToList(); |
|
|
|
object progressLock = new object(); |
|
double fileCountInverse = 1.0 / fileContents.Count; |
|
Parallel.ForEach( |
|
fileContents, |
|
new ParallelOptions { |
|
MaxDegreeOfParallelism = Environment.ProcessorCount * 2, |
|
CancellationToken = progressMonitor.CancellationToken |
|
}, |
|
fileName => { |
|
// Don't read files we don't have a parser for. |
|
// This avoids loading huge files (e.g. sdps) when we have no intention of parsing them. |
|
if (ParserService.GetParser(fileName) != null) { |
|
ITextBuffer content = finder.Create(fileName); |
|
if (content != null) |
|
ParserService.ParseFile(this, fileName, content); |
|
} |
|
lock (progressLock) { |
|
progressMonitor.Progress += fileCountInverse; |
|
} |
|
} |
|
); |
|
} finally { |
|
initializing = false; |
|
progressMonitor.Progress = 1; |
|
} |
|
} |
|
|
|
public override void Dispose() |
|
{ |
|
ProjectService.ProjectItemAdded -= OnProjectItemAdded; |
|
ProjectService.ProjectItemRemoved -= OnProjectItemRemoved; |
|
initializing = false; |
|
base.Dispose(); |
|
} |
|
} |
|
}
|
|
|