我为MsBuild创建了MergeSolution任务。它用于将多个解决方案合并到一个解决方案文件中。我正在使用PLINQ来并行解析和处理所有解决方案。我正在使用 我的DisposableProject类可以访问每个解决方案中的项目文件,以获取一些项目特定的值。创建DisposableProject类是为了修复竞争条件问题,因为一些项目文件包含在多个解决方案中,并且还用于在合并过程中修复大量内存消耗。
/// <summary>
/// Adding disposable functionality to Microsof.Build.BuildEngine.Project class.
/// Every project file is locked during processing to get a rid of huge memory consumption and race condition issues.
/// Project files are unloaded from build engine to save memory.
/// </summary>
public class DisposableProject : Project, IDisposable
{
// Build engine - is it really needed?
private static Engine buildEngine = new Engine();
// Event used for thread safe Mutex creation
static AutoResetEvent loadingEvent = new AutoResetEvent(true);
// Project file name
private string FileName {get;set;}
// Dictionary with Mutex name - Mutex
static ConcurrentDictionary<string, Mutex> mutexLocks = new ConcurrentDictionary<string, Mutex>();
/// <summary>
/// DisposableProject constructor. Project file is loaded and named Mutex is created to lock file by its file name.
/// </summary>
/// <param name="projectFileName">MsBuild project file name</param>
public DisposableProject(string projectFileName)
: base(buildEngine)
{
// Use file name as mutex name
FileName = GetMutexName(projectFileName);
// Lock file during processing
// Lock Mutex creation because multiple threads can create duplicate Mutexes causing sync exceptions
loadingEvent.WaitOne();
// Check if mutex object already exists
if(!mutexLocks.ContainsKey(FileName))
mutexLocks[FileName] = new Mutex(false, FileName);
// Unlock mutex creation
loadingEvent.Set();
// Wait for file
mutexLocks[FileName].WaitOne();
// Load project file
this.Load(projectFileName, ProjectLoadSettings.IgnoreMissingImports);
}
/// <summary>
/// Unload MsBuild project file from memory and Mutex (file lock) is released.
/// </summary>
public void Dispose()
{
// Unload from build engine to save memory
this.ParentEngine.UnloadProject(this);
// Unlock file
mutexLocks[FileName].ReleaseMutex();
}
/// <summary>
/// Create mutex name from file name.
/// </summary>
/// <param name="pathName">File name</param>
/// <returns>Mutex name</returns>
private string GetMutexName(string pathName)
{
// Replace directory separator chars with underscore
return pathName.Replace(Path.DirectorySeparatorChar, '_');
}
}
有时我的MergeSolutions任务在加载第一个项目文件时失败并出现以下异常:
InvalidProjectFileException: Invalid static method invocation syntax: "$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToStandardLibraries($(TargetFrameworkIdentifier), $(TargetFrameworkVersion), $(TargetFrameworkProfile)))". Static method invocation should be of the form: $([FullTypeName]::Method()), e.g. $([System.IO.Path]::Combine(`a`, `b`))\r
at Microsoft.Build.BuildEngine.Shared.ProjectErrorUtilities.VerifyThrowInvalidProject(Boolean condition, String errorSubCategoryResourceName, XmlNode xmlNode, String resourceName, Object arg0)\r
at Microsoft.Build.BuildEngine.Expander.Function.ExtractPropertyFunction(String expressionFunction, Object propertyValue)\r
at Microsoft.Build.BuildEngine.Expander.ExpandPropertyBody(String propertyBody, Object propertyValue, BuildPropertyGroup properties, ExpanderOptions options)\r
at Microsoft.Build.BuildEngine.Expander.ExpandPropertiesLeaveTypedAndEscaped(String expression, XmlNode expressionNode)\r
at Microsoft.Build.BuildEngine.Expander.ExpandAllIntoStringLeaveEscaped(String expression, XmlNode expressionNode)\r
at Microsoft.Build.BuildEngine.BuildPropertyGroup.Evaluate(BuildPropertyGroup evaluatedPropertyBag, Hashtable conditionedPropertiesTable, ProcessingPass pass)\r
at Microsoft.Build.BuildEngine.Project.ProcessProjectChildren(XmlElement projectElement, String projectDirectoryLocation, Boolean importedProject)\r
at Microsoft.Build.BuildEngine.Project.ProcessImportElement(XmlElement importElement, String projectDirectoryLocation, Boolean importedProject)\r
at Microsoft.Build.BuildEngine.Project.ProcessProjectChildren(XmlElement projectElement, String projectDirectoryLocation, Boolean importedProject)\r
at Microsoft.Build.BuildEngine.Project.ProcessImportElement(XmlElement importElement, String projectDirectoryLocation, Boolean importedProject)\r
at Microsoft.Build.BuildEngine.Project.ProcessProjectChildren(XmlElement projectElement, String projectDirectoryLocation, Boolean importedProject)\r
at Microsoft.Build.BuildEngine.Project.ProcessMainProjectElement()\r
at Microsoft.Build.BuildEngine.Project.InternalLoadFromXmlDocument(XmlDocument projectXml, ProjectLoadSettings projectLoadSettings)\r
at Microsoft.Build.BuildEngine.Project.Load(String projectFileName, BuildEventContext buildEventContext, ProjectLoadSettings projectLoadSettings)\r
at XXX.Build.Common.Types.DisposableProject..ctor(String projectFileName) in D:\XXX\DisposableProject.cs:line 35\r
MergeSolution任务总是在合并过程开始时抛出此异常,有时只会发生。是猜测是BuildEngine的初始化问题。 BuildEngine构造函数可能不是线程安全的。我正在使用默认的MSBuild构造函数。 任何想法如何解决?