/// <summary> /// Evaluates a string representing a condition from a "condition" attribute. /// If the condition is a malformed string, it throws an InvalidProjectFileException. /// This method uses cached expression trees to avoid generating them from scratch every time it's called. /// This method is thread safe and is called from engine and task execution module threads /// </summary> internal static bool EvaluateCondition <P, I> ( string condition, ParserOptions options, Expander <P, I> expander, ExpanderOptions expanderOptions, string evaluationDirectory, ElementLocation elementLocation, ILoggingService loggingServices, BuildEventContext buildEventContext, IFileSystem fileSystem, ProjectRootElementCache projectRootElementCache = null) where P : class, IProperty where I : class, IItem { return(EvaluateConditionCollectingConditionedProperties( condition, options, expander, expanderOptions, null /* do not collect conditioned properties */, evaluationDirectory, elementLocation, loggingServices, buildEventContext, fileSystem, projectRootElementCache)); }
internal ConditionEvaluationState ( string condition, Expander <P, I> expander, ExpanderOptions expanderOptions, Dictionary <string, List <string> > conditionedPropertiesInProject, string evaluationDirectory, ElementLocation elementLocation, IFileSystem fileSystem, ProjectRootElementCache projectRootElementCache = null ) { ErrorUtilities.VerifyThrowArgumentNull(condition, "condition"); ErrorUtilities.VerifyThrowArgumentNull(expander, "expander"); ErrorUtilities.VerifyThrowArgumentNull(evaluationDirectory, "evaluationDirectory"); ErrorUtilities.VerifyThrowArgumentNull(elementLocation, "elementLocation"); Condition = condition; _expander = expander; _expanderOptions = expanderOptions; ConditionedPropertiesInProject = conditionedPropertiesInProject; // May be null EvaluationDirectory = evaluationDirectory; ElementLocation = elementLocation; LoadedProjectsCache = projectRootElementCache; FileSystem = fileSystem; }
/// <summary> /// Creates a new ProjectRootElement for a specific PRE cache /// </summary> /// <param name="path">The path to the file to load.</param> /// <param name="projectRootElementCache">The cache to load the PRE into.</param> private static ProjectRootElement OpenLoader(string path, ProjectRootElementCache projectRootElementCache) { return new ProjectRootElement( path, projectRootElementCache, new BuildEventContext(0, BuildEventContext.InvalidNodeId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId)); }
/// <summary> /// Centralization of the common parts of construction. /// </summary> private void Initialize(PropertyDictionary<ProjectPropertyInstance> environmentProperties, ProjectRootElementCache projectRootElementCache, ToolsetProvider toolsetProvider) { _buildProcessEnvironment = CommunicationsUtilities.GetEnvironmentVariables(); _environmentProperties = environmentProperties; this.ProjectRootElementCache = projectRootElementCache; this.ResetCaches = true; _toolsetProvider = toolsetProvider; if (Environment.GetEnvironmentVariable("MSBUILDDISABLENODEREUSE") == "1") // For example to disable node reuse within Visual Studio { _enableNodeReuse = false; } if (Environment.GetEnvironmentVariable("MSBUILDDETAILEDSUMMARY") == "1") // For example to get detailed summary within Visual Studio { _detailedSummary = true; } FindMSBuildExe(); }
/// <summary> /// Used to load information about default MSBuild tasks i.e. tasks that do not need to be explicitly declared in projects /// with the <UsingTask> element. Default task information is read from special files, which are located in the same /// directory as the MSBuild binaries. /// </summary> /// <remarks> /// 1) a default tasks file needs the <Project> root tag in order to be well-formed /// 2) the XML declaration tag <?xml ...> is ignored /// 3) comment tags are always ignored regardless of their placement /// 4) the rest of the tags are expected to be <UsingTask> tags /// </remarks> /// <param name="loggingServices">The logging services to use to log during this registration.</param> /// <param name="buildEventContext">The build event context to use to log during this registration.</param> private void RegisterDefaultTasks(ILoggingService loggingServices, BuildEventContext buildEventContext, ProjectRootElementCache projectRootElementCache) { if (!_defaultTasksRegistrationAttempted) { try { _defaultTaskRegistry = new TaskRegistry(projectRootElementCache); InitializeProperties(loggingServices, buildEventContext); string[] defaultTasksFiles = GetTaskFiles(_getFiles, loggingServices, buildEventContext, DefaultTasksFilePattern, ToolsPath, "DefaultTasksFileLoadFailureWarning"); LoadAndRegisterFromTasksFile(ToolsPath, defaultTasksFiles, loggingServices, buildEventContext, DefaultTasksFilePattern, "DefaultTasksFileFailure", projectRootElementCache, _defaultTaskRegistry); } finally { _defaultTasksRegistrationAttempted = true; } } }
/// <summary> /// Return a task registry stub for the tasks in the *.tasks file for this toolset /// </summary> /// <param name="loggingServices">The logging services used to log during task registration.</param> /// <param name="buildEventContext">The build event context used to log during task registration.</param> /// <returns>The task registry</returns> internal TaskRegistry GetTaskRegistry(ILoggingService loggingServices, BuildEventContext buildEventContext, ProjectRootElementCache projectRootElementCache) { RegisterDefaultTasks(loggingServices, buildEventContext, projectRootElementCache); return _defaultTaskRegistry; }
public void GetProjectRootElementChangedOnDisk2() { string path = null; try { ProjectRootElementCache cache = new ProjectRootElementCache(false /* do not auto reload from disk */); path = FileUtilities.GetTemporaryFile(); ProjectRootElement xml0 = ProjectRootElement.Create(path); xml0.Save(); cache.AddEntry(xml0); ProjectRootElement xml1 = cache.TryGet(path); Assert.Equal(true, Object.ReferenceEquals(xml0, xml1)); File.SetLastWriteTime(path, DateTime.Now + new TimeSpan(1, 0, 0)); ProjectRootElement xml2 = cache.TryGet(path); Assert.Equal(true, Object.ReferenceEquals(xml0, xml2)); } finally { File.Delete(path); } }
/// <summary> /// Initialize a ProjectRootElement instance from an existing document. /// May throw InvalidProjectFileException. /// Leaves the project dirty, indicating there are unsaved changes. /// </summary> /// <remarks> /// Do not make public: we do not wish to expose particular XML API's. /// </remarks> private ProjectRootElement(XmlDocumentWithLocation document, ProjectRootElementCache projectRootElementCache) : base() { ErrorUtilities.VerifyThrowArgumentNull(document, "document"); ErrorUtilities.VerifyThrowArgumentNull(projectRootElementCache, "projectRootElementCache"); _projectRootElementCache = projectRootElementCache; _directory = NativeMethodsShared.GetCurrentDirectory(); IncrementVersion(); ProjectParser.Parse(document, this); }
private static ProjectRootElement OpenLoaderPreserveFormatting(string path, ProjectRootElementCache projectRootElementCache) { return OpenLoader(path, projectRootElementCache, preserveFormatting: true); }
/// <summary> /// Initialize an in-memory, empty ProjectRootElement instance that can be saved later. /// Leaves the project dirty, indicating there are unsaved changes. /// </summary> private ProjectRootElement(ProjectRootElementCache projectRootElementCache, NewProjectFileOptions projectFileOptions) { ErrorUtilities.VerifyThrowArgumentNull(projectRootElementCache, "projectRootElementCache"); _projectRootElementCache = projectRootElementCache; _directory = NativeMethodsShared.GetCurrentDirectory(); IncrementVersion(); XmlDocumentWithLocation document = new XmlDocumentWithLocation(); XmlReaderSettings xrs = new XmlReaderSettings(); xrs.DtdProcessing = DtdProcessing.Ignore; var emptyProjectFile = string.Format(EmptyProjectFileContent, (projectFileOptions & NewProjectFileOptions.IncludeXmlDeclaration) != 0 ? EmptyProjectFileXmlDeclaration : string.Empty, (projectFileOptions & NewProjectFileOptions.IncludeToolsVersion) != 0 ? EmptyProjectFileToolsVersion : string.Empty, (projectFileOptions & NewProjectFileOptions.IncludeXmlNamespace) != 0 ? EmptyProjectFileXmlNamespace : string.Empty); using (XmlReader xr = XmlReader.Create(new StringReader(emptyProjectFile), xrs)) { document.Load(xr); } ProjectParser.Parse(document, this); }
/// <summary> /// Initialize a ProjectRootElement instance by loading from the specified file path. /// Assumes path is already normalized. /// Uses the specified project root element cache. /// May throw InvalidProjectFileException. /// </summary> internal static ProjectRootElement Open(string path, ProjectRootElementCache projectRootElementCache, bool isExplicitlyLoaded, bool preserveFormatting) { ErrorUtilities.VerifyThrowInternalRooted(path); ProjectRootElement projectRootElement = projectRootElementCache.Get(path, preserveFormatting ? s_openLoaderPreserveFormattingDelegate : s_openLoaderDelegate, isExplicitlyLoaded, preserveFormatting); return projectRootElement; }
internal static ProjectRootElement Create(ProjectRootElementCache projectRootElementCache, NewProjectFileOptions projectFileOptions) { return new ProjectRootElement(projectRootElementCache, projectFileOptions); }
/// <summary> /// Initialize an in-memory, empty ProjectRootElement instance that can be saved later. /// Uses the specified project root element cache. /// </summary> internal static ProjectRootElement Create(ProjectRootElementCache projectRootElementCache) { return new ProjectRootElement(projectRootElementCache, Project.DefaultNewProjectTemplateOptions); }
private void CleanupCaches() { IConfigCache configCache = _componentFactories.GetComponent(BuildComponentType.ConfigCache) as IConfigCache; if (null != configCache) { configCache.ClearConfigurations(); } IResultsCache resultsCache = _componentFactories.GetComponent(BuildComponentType.ResultsCache) as IResultsCache; if (null != resultsCache) { resultsCache.ClearResults(); } if (Environment.GetEnvironmentVariable("MSBUILDCLEARXMLCACHEONCHILDNODES") == "1") { // Optionally clear out the cache. This has the advantage of releasing memory, // but the disadvantage of causing the next build to repeat the load and parse. // We'll experiment here and ship with the best default. s_projectRootElementCache = null; } // Since we aren't going to be doing any more work, lets clean up all our memory usage. GC.Collect(); }
/// <summary> /// Constructor. /// </summary> public OutOfProcNode() { s_isOutOfProcNode = true; AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(ExceptionHandling.UnhandledExceptionHandler); _debugCommunications = (Environment.GetEnvironmentVariable("MSBUILDDEBUGCOMM") == "1"); _receivedPackets = new Queue<INodePacket>(); _packetReceivedEvent = new AutoResetEvent(false); _shutdownEvent = new ManualResetEvent(false); _legacyThreadingData = new LegacyThreadingData(); _componentFactories = new BuildComponentFactoryCollection(this); _componentFactories.RegisterDefaultFactories(); _packetFactory = new NodePacketFactory(); _buildRequestEngine = (this as IBuildComponentHost).GetComponent(BuildComponentType.RequestEngine) as IBuildRequestEngine; _globalConfigCache = (this as IBuildComponentHost).GetComponent(BuildComponentType.ConfigCache) as IConfigCache; _taskHostNodeManager = (this as IBuildComponentHost).GetComponent(BuildComponentType.TaskHostNodeManager) as INodeManager; if (s_projectRootElementCache == null) { s_projectRootElementCache = new ProjectRootElementCache(true /* automatically reload any changes from disk */); } _buildRequestEngine.OnEngineException += new EngineExceptionDelegate(OnEngineException); _buildRequestEngine.OnNewConfigurationRequest += new NewConfigurationRequestDelegate(OnNewConfigurationRequest); _buildRequestEngine.OnRequestBlocked += new RequestBlockedDelegate(OnNewRequest); _buildRequestEngine.OnRequestComplete += new RequestCompleteDelegate(OnRequestComplete); (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.BuildRequest, BuildRequest.FactoryForDeserialization, this); (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.BuildRequestConfiguration, BuildRequestConfiguration.FactoryForDeserialization, this); (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.BuildRequestConfigurationResponse, BuildRequestConfigurationResponse.FactoryForDeserialization, this); (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.BuildRequestUnblocker, BuildRequestUnblocker.FactoryForDeserialization, this); (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.NodeConfiguration, NodeConfiguration.FactoryForDeserialization, this); (this as INodePacketFactory).RegisterPacketHandler(NodePacketType.NodeBuildComplete, NodeBuildComplete.FactoryForDeserialization, this); }
/// <summary> /// Creates a ProjectRootElement representing a file, where the file may be a .sln instead of /// an MSBuild format file. /// Assumes path is already normalized. /// If the file is in MSBuild format, may throw InvalidProjectFileException. /// If the file is a solution, will throw an IO-related exception if the file cannot be read. /// </summary> private static ProjectRootElement CreateProjectFromPath ( string projectFile, IDictionary<string, string> globalProperties, string toolsVersion, ILoggingService loggingService, ProjectRootElementCache projectRootElementCache, BuildEventContext buildEventContext ) { ErrorUtilities.VerifyThrowInternalRooted(projectFile); try { if (FileUtilities.IsVCProjFilename(projectFile)) { ProjectFileErrorUtilities.ThrowInvalidProjectFile(new BuildEventFileInfo(projectFile), "ProjectUpgradeNeededToVcxProj", projectFile); } // OK it's a regular project file, load it normally. return new ProjectRootElement(projectFile, projectRootElementCache, buildEventContext); } catch (InvalidProjectFileException) { throw; } catch (Exception ex) { if (!ExceptionHandling.NotExpectedException(ex)) { ProjectFileErrorUtilities.ThrowInvalidProjectFile(new BuildEventFileInfo(projectFile), ex, "InvalidProjectFile", ex.Message); } throw; } }
/// <summary> /// Initialize a ProjectRootElement instance over a project with the specified file path. /// Assumes path is already normalized. /// May throw InvalidProjectFileException. /// </summary> private ProjectRootElement(string path, ProjectRootElementCache projectRootElementCache, BuildEventContext buildEventContext) : base() { ErrorUtilities.VerifyThrowArgumentLength(path, "path"); ErrorUtilities.VerifyThrowInternalRooted(path); ErrorUtilities.VerifyThrowArgumentNull(projectRootElementCache, "projectRootElementCache"); ErrorUtilities.VerifyThrowArgumentNull(buildEventContext, "buildEventContext"); _projectRootElementCache = projectRootElementCache; _buildEventContext = buildEventContext; IncrementVersion(); _versionOnDisk = _version; _timeLastChangedUtc = DateTime.UtcNow; XmlDocumentWithLocation document = LoadDocument(path); ProjectParser.Parse(document, this); projectRootElementCache.AddEntry(this); }
/// <summary> /// Creates a ProjectRootElement representing a file, where the file may be a .sln instead of /// an MSBuild format file. /// Assumes path is already normalized. /// If the file is in MSBuild format, may throw InvalidProjectFileException. /// If the file is a solution, will throw an IO-related exception if the file cannot be read. /// </summary> private static ProjectRootElement CreateProjectFromPath ( string projectFile, IDictionary<string, string> globalProperties, string toolsVersion, ILoggingService loggingService, ProjectRootElementCache projectRootElementCache, BuildEventContext buildEventContext, bool preserveFormatting ) { ErrorUtilities.VerifyThrowInternalRooted(projectFile); try { if (FileUtilities.IsVCProjFilename(projectFile)) { ProjectFileErrorUtilities.ThrowInvalidProjectFile(new BuildEventFileInfo(projectFile), "ProjectUpgradeNeededToVcxProj", projectFile); } // OK it's a regular project file, load it normally. return new ProjectRootElement(projectFile, projectRootElementCache, buildEventContext, preserveFormatting); } catch (InvalidProjectFileException) { throw; } catch (Exception ex) when (ExceptionHandling.IsIoRelatedException(ex)) { ProjectFileErrorUtilities.ThrowInvalidProjectFile(new BuildEventFileInfo(projectFile), ex, "InvalidProjectFile", ex.Message); throw; // Without this there's a spurious CS0161 because csc 1.2.0.60317 can't see that the above is an unconditional throw. } }
/// <summary> /// Evaluates a string representing a condition from a "condition" attribute. /// If the condition is a malformed string, it throws an InvalidProjectFileException. /// This method uses cached expression trees to avoid generating them from scratch every time it's called. /// This method is thread safe and is called from engine and task execution module threads /// Logging service may be null. /// </summary> internal static bool EvaluateConditionCollectingConditionedProperties <P, I> ( string condition, ParserOptions options, Expander <P, I> expander, ExpanderOptions expanderOptions, Dictionary <string, List <string> > conditionedPropertiesTable, string evaluationDirectory, ElementLocation elementLocation, ILoggingService loggingServices, BuildEventContext buildEventContext, ProjectRootElementCache projectRootElementCache = null ) where P : class, IProperty where I : class, IItem { ErrorUtilities.VerifyThrowArgumentNull(condition, "condition"); ErrorUtilities.VerifyThrowArgumentNull(expander, "expander"); ErrorUtilities.VerifyThrowArgumentLength(evaluationDirectory, "evaluationDirectory"); ErrorUtilities.VerifyThrowArgumentNull(buildEventContext, "buildEventContext"); // An empty condition is equivalent to a "true" condition. if (condition.Length == 0) { return(true); } // If the condition wasn't empty, there must be a location for it ErrorUtilities.VerifyThrowArgumentNull(elementLocation, "elementLocation"); // Get the expression tree cache for the current parsing options. ExpressionTreeForCurrentOptionsWithSize cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees.GetOrAdd( (int)options, _ => new ExpressionTreeForCurrentOptionsWithSize(new ConcurrentDictionary <string, ConcurrentStack <GenericExpressionNode> >(StringComparer.Ordinal))); cachedExpressionTreesForCurrentOptions = FlushCacheIfLargerThanThreshold(options, cachedExpressionTreesForCurrentOptions); // Get the pool of expressions for this condition. var expressionPool = cachedExpressionTreesForCurrentOptions.GetOrAdd(condition, _ => new ConcurrentStack <GenericExpressionNode>()); // Try and see if there's an available expression tree in the pool. // If not, parse a new expression tree and add it back to the pool. GenericExpressionNode parsedExpression; if (!expressionPool.TryPop(out parsedExpression)) { Parser conditionParser = new Parser(); #region REMOVE_COMPAT_WARNING conditionParser.LoggingServices = loggingServices; conditionParser.LogBuildEventContext = buildEventContext; #endregion parsedExpression = conditionParser.Parse(condition, options, elementLocation); } bool result; ConditionEvaluationState <P, I> state = new ConditionEvaluationState <P, I>(condition, expander, expanderOptions, conditionedPropertiesTable, evaluationDirectory, elementLocation, projectRootElementCache); // We are evaluating this expression now and it can cache some state for the duration, // so we don't want multiple threads working on the same expression lock (parsedExpression) { try { result = parsedExpression.Evaluate(state); } finally { parsedExpression.ResetState(); if (!s_disableExpressionCaching) { // Finished using the expression tree. Add it back to the pool so other threads can use it. expressionPool.Push(parsedExpression); } } } return(result); }
/// <summary> /// Initialize a ProjectRootElement instance from a XmlReader. /// May throw InvalidProjectFileException. /// Leaves the project dirty, indicating there are unsaved changes. /// Used to create a root element for solutions loaded by the 3.5 version of the solution wrapper. /// </summary> internal ProjectRootElement(XmlReader xmlReader, ProjectRootElementCache projectRootElementCache, bool isExplicitlyLoaded) : base() { ErrorUtilities.VerifyThrowArgumentNull(xmlReader, "xmlReader"); ErrorUtilities.VerifyThrowArgumentNull(projectRootElementCache, "projectRootElementCache"); this.IsExplicitlyLoaded = isExplicitlyLoaded; _projectRootElementCache = projectRootElementCache; _directory = NativeMethodsShared.GetCurrentDirectory(); IncrementVersion(); XmlDocumentWithLocation document = LoadDocument(xmlReader); ProjectParser.Parse(document, this); }
/// <summary> /// Do the actual loading of the tasks or override tasks file and register the tasks in the task registry /// </summary> private void LoadAndRegisterFromTasksFile(string searchPath, string[] defaultTaskFiles, ILoggingService loggingServices, BuildEventContext buildEventContext, string defaultTasksFilePattern, string taskFileError, ProjectRootElementCache projectRootElementCache, TaskRegistry registry) { foreach (string defaultTasksFile in defaultTaskFiles) { try { // Important to keep the following line since unit tests use the delegate. ProjectRootElement projectRootElement; if (_loadXmlFromPath != null) { XmlDocumentWithLocation defaultTasks = _loadXmlFromPath(defaultTasksFile); projectRootElement = ProjectRootElement.Open(defaultTasks); } else { projectRootElement = ProjectRootElement.Open(defaultTasksFile, projectRootElementCache, false /*The tasks file is not a explicitly loaded file*/); } foreach (ProjectElement elementXml in projectRootElement.Children) { ProjectUsingTaskElement usingTask = elementXml as ProjectUsingTaskElement; if (null == usingTask) { ProjectErrorUtilities.ThrowInvalidProject ( elementXml.Location, "UnrecognizedElement", elementXml.XmlElement.Name ); } TaskRegistry.RegisterTasksFromUsingTaskElement<ProjectPropertyInstance, ProjectItemInstance> ( loggingServices, buildEventContext, Path.GetDirectoryName(defaultTasksFile), usingTask, registry, _expander, ExpanderOptions.ExpandProperties ); } } catch (XmlException e) { // handle XML errors in the default tasks file ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(false, new BuildEventFileInfo(e), taskFileError, e.Message); } catch (Exception e) { if (ExceptionHandling.NotExpectedException(e)) { // Catching Exception, but rethrowing unless it's an IO related exception. throw; } loggingServices.LogError(buildEventContext, new BuildEventFileInfo(defaultTasksFile), taskFileError, e.Message); break; } } }
/// <summary> /// Initialize an in-memory, empty ProjectRootElement instance that can be saved later. /// Uses the specified project root element cache. /// </summary> internal static ProjectRootElement Create(ProjectRootElementCache projectRootElementCache) { return new ProjectRootElement(projectRootElementCache); }
/// <summary> /// Return a task registry for the override tasks in the *.overridetasks file for this toolset /// </summary> /// <param name="loggingServices">The logging services used to log during task registration.</param> /// <param name="buildEventContext">The build event context used to log during task registration.</param> /// <returns>The task registry</returns> internal TaskRegistry GetOverrideTaskRegistry(ILoggingService loggingServices, BuildEventContext buildEventContext, ProjectRootElementCache projectRootElementCache) { RegisterOverrideTasks(loggingServices, buildEventContext, projectRootElementCache); return _overrideTaskRegistry; }
/// <summary> /// Initialize a ProjectRootElement instance by loading from the specified file path. /// Assumes path is already normalized. /// Uses the specified project root element cache. /// May throw InvalidProjectFileException. /// </summary> internal static ProjectRootElement Open(string path, ProjectRootElementCache projectRootElementCache, bool isExplicitlyLoaded) { ErrorUtilities.VerifyThrowInternalRooted(path); ProjectRootElement projectRootElement = projectRootElementCache.Get(path, s_openLoaderDelegate, isExplicitlyLoaded); return projectRootElement; }
/// <summary> /// Used to load information about MSBuild override tasks i.e. tasks that override tasks declared in tasks or project files. /// </summary> private void RegisterOverrideTasks(ILoggingService loggingServices, BuildEventContext buildEventContext, ProjectRootElementCache projectRootElementCache) { if (!_overrideTasksRegistrationAttempted) { try { _overrideTaskRegistry = new TaskRegistry(projectRootElementCache); bool overrideDirectoryExists = false; try { // Make sure the override directory exists and is not empty before trying to find the files if (!String.IsNullOrEmpty(_overrideTasksPath)) { if (Path.IsPathRooted(_overrideTasksPath)) { if (null != _directoryExists) { overrideDirectoryExists = _directoryExists(_overrideTasksPath); } else { overrideDirectoryExists = Directory.Exists(_overrideTasksPath); } } if (!overrideDirectoryExists) { string rootedPathMessage = ResourceUtilities.FormatResourceString("OverrideTaskNotRootedPath", _overrideTasksPath); loggingServices.LogWarning(buildEventContext, null, new BuildEventFileInfo(String.Empty /* this warning truly does not involve any file*/), "OverrideTasksFileFailure", rootedPathMessage); } } } catch (Exception e) { if (ExceptionHandling.NotExpectedException(e)) { // Catching Exception, but rethrowing unless it's an IO related exception. throw; } string rootedPathMessage = ResourceUtilities.FormatResourceString("OverrideTaskProblemWithPath", _overrideTasksPath, e.Message); loggingServices.LogWarning(buildEventContext, null, new BuildEventFileInfo(String.Empty /* this warning truly does not involve any file*/), "OverrideTasksFileFailure", rootedPathMessage); } if (overrideDirectoryExists) { InitializeProperties(loggingServices, buildEventContext); string[] overrideTasksFiles = GetTaskFiles(_getFiles, loggingServices, buildEventContext, OverrideTasksFilePattern, _overrideTasksPath, "OverrideTasksFileLoadFailureWarning"); // Load and register any override tasks LoadAndRegisterFromTasksFile(_overrideTasksPath, overrideTasksFiles, loggingServices, buildEventContext, OverrideTasksFilePattern, "OverrideTasksFileFailure", projectRootElementCache, _overrideTaskRegistry); } } finally { _overrideTasksRegistrationAttempted = true; } } }
/// <summary> /// Initialize an in-memory, empty ProjectRootElement instance that can be saved later. /// Leaves the project dirty, indicating there are unsaved changes. /// </summary> private ProjectRootElement(ProjectRootElementCache projectRootElementCache) { ErrorUtilities.VerifyThrowArgumentNull(projectRootElementCache, "projectRootElementCache"); _projectRootElementCache = projectRootElementCache; _directory = NativeMethodsShared.GetCurrentDirectory(); IncrementVersion(); XmlDocumentWithLocation document = new XmlDocumentWithLocation(); XmlReaderSettings xrs = new XmlReaderSettings(); xrs.DtdProcessing = DtdProcessing.Ignore; using (XmlReader xr = XmlReader.Create(new StringReader(ProjectRootElement.EmptyProjectFileContent), xrs)) { document.Load(xr); } ProjectParser.Parse(document, this); }
/// <summary> /// Evaluates a string representing a condition from a "condition" attribute. /// If the condition is a malformed string, it throws an InvalidProjectFileException. /// This method uses cached expression trees to avoid generating them from scratch every time it's called. /// This method is thread safe and is called from engine and task execution module threads /// Logging service may be null. /// </summary> internal static bool EvaluateConditionCollectingConditionedProperties <P, I> ( string condition, ParserOptions options, Expander <P, I> expander, ExpanderOptions expanderOptions, Dictionary <string, List <string> > conditionedPropertiesTable, string evaluationDirectory, ElementLocation elementLocation, ILoggingService loggingServices, BuildEventContext buildEventContext, ProjectRootElementCache projectRootElementCache = null ) where P : class, IProperty where I : class, IItem { ErrorUtilities.VerifyThrowArgumentNull(condition, "condition"); ErrorUtilities.VerifyThrowArgumentNull(expander, "expander"); ErrorUtilities.VerifyThrowArgumentLength(evaluationDirectory, "evaluationDirectory"); ErrorUtilities.VerifyThrowArgumentNull(buildEventContext, "buildEventContext"); // An empty condition is equivalent to a "true" condition. if (condition.Length == 0) { return(true); } // If the condition wasn't empty, there must be a location for it ErrorUtilities.VerifyThrowArgumentNull(elementLocation, "elementLocation"); Hashtable cachedExpressionTreesForCurrentOptions = s_cachedExpressionTrees[(int)options]; // We only need to lock on writes to the table if (cachedExpressionTreesForCurrentOptions == null) { // Given property functions, casing in conditional expressions isn't necessarily ignored. cachedExpressionTreesForCurrentOptions = new Hashtable(50, StringComparer.Ordinal); lock (s_cachedExpressionTrees) { s_cachedExpressionTrees[(int)options] = cachedExpressionTreesForCurrentOptions; } } // VS stress tests could fill up this cache without end, for example if they use // random configuration names - those appear in conditional expressions. // So if we hit a limit that we should never hit in normal circumstances in VS, // and rarely, periodically hit in normal circumstances in large tree builds, // just clear out the cache. It can start repopulating again. Some kind of prioritized // aging isn't worth it: although the hit rate of these caches is excellent (nearly 100%) // the cost of reparsing expressions should the cache be cleared is not particularly large. // Loading Australian Government in VS, there are 3 of these tables, two with about 50 // entries and one with about 650 entries. So 3000 seems large enough. if (cachedExpressionTreesForCurrentOptions.Count > 3000) // threadsafe { lock (cachedExpressionTreesForCurrentOptions) { cachedExpressionTreesForCurrentOptions.Clear(); } } // Try and see if we have an expression tree for this condition already GenericExpressionNode parsedExpression = (GenericExpressionNode)cachedExpressionTreesForCurrentOptions[condition]; if (parsedExpression == null) { Parser conditionParser = new Parser(); #region REMOVE_COMPAT_WARNING conditionParser.LoggingServices = loggingServices; conditionParser.LogBuildEventContext = buildEventContext; #endregion parsedExpression = conditionParser.Parse(condition, options, elementLocation); // It's possible two threads will add a different tree to the same entry in the hashtable, // but it should be rare and it's not a problem - the previous entry will be thrown away. // We could ensure no dupes with double check locking but it's not really necessary here. // Also, we don't want to lock on every read. lock (cachedExpressionTreesForCurrentOptions) { if (!s_disableExpressionCaching) { cachedExpressionTreesForCurrentOptions[condition] = parsedExpression; } } } bool result; ConditionEvaluationState <P, I> state = new ConditionEvaluationState <P, I>(condition, expander, expanderOptions, conditionedPropertiesTable, evaluationDirectory, elementLocation, projectRootElementCache); // We are evaluating this expression now and it can cache some state for the duration, // so we don't want multiple threads working on the same expression lock (parsedExpression) { try { result = parsedExpression.Evaluate(state); } finally { parsedExpression.ResetState(); } } return(result); }
/// <summary> /// Gets a ProjectRootElement representing an MSBuild file. /// Path provided must be a canonicalized full path. /// May throw InvalidProjectFileException or an IO-related exception. /// </summary> internal static ProjectRootElement OpenProjectOrSolution(string fullPath, IDictionary<string, string> globalProperties, string toolsVersion, ILoggingService loggingService, ProjectRootElementCache projectRootElementCache, BuildEventContext buildEventContext, bool isExplicitlyLoaded) { ErrorUtilities.VerifyThrowInternalRooted(fullPath); ProjectRootElement projectRootElement = projectRootElementCache.Get( fullPath, (path, cache) => CreateProjectFromPath(path, globalProperties, toolsVersion, loggingService, cache, buildEventContext), isExplicitlyLoaded); return projectRootElement; }