Недопустимое исключение синтаксиса вызова статического метода при загрузке файла проекта MsBuild - PullRequest
4 голосов
/ 28 ноября 2011

Я создал задачу MergeSolution для MsBuild.Он предназначен для объединения нескольких решений в один файл решения.Я использую 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 по умолчанию.Есть идеи как это решить?

...