/// <summary> /// Loads a project file from disk, and builds the specified list of targets. This overload /// takes a set of global properties to use for the build, returns the target outputs, and also /// allows the caller to specify additional build flags. /// </summary> /// <remarks> /// If this project file is already in our list of in-progress projects, we use the /// existing Project object instead of instantiating a new one. Always use this method to /// build projects within projects, otherwise the build won't be optimized. /// </remarks> internal void BuildProjectFileInternal ( BuildRequest buildRequest ) { string projectFile = buildRequest.ProjectFileName; error.VerifyThrowArgumentNull(projectFile, "projectFileName"); error.VerifyThrowArgument(projectFile.Length > 0, "EmptyProjectFileName"); // When we get to this point the global properties should not be null, as they are used to determine if a project has already been loaded / built before. error.VerifyThrow(buildRequest.GlobalProperties != null, "Global Properties should not be null"); // Convert the project filename to a fully qualified path. FileInfo projectFileInfo = new FileInfo(projectFile); // If the project file doesn't actually exist on disk, it's a failure. ErrorUtilities.VerifyThrowArgument(projectFileInfo.Exists, "ProjectFileNotFound", projectFile); try { ArrayList actuallyBuiltTargets; // If the tools version is empty take a quick peek at the project file to determine if it has a tools version defined if(String.IsNullOrEmpty(buildRequest.ToolsetVersion)) { buildRequest.ToolsetVersion = XmlUtilities.GetAttributeValueForElementFromFile(buildRequest.ProjectFileName, XMakeAttributes.project, XMakeAttributes.toolsVersion); buildRequest.ToolsVersionPeekedFromProjectFile = true; } // Check if there is a cached result available for this build BuildResult cachedResult = cacheManager.GetCachedBuildResult(buildRequest, out actuallyBuiltTargets); if (cachedResult != null) { // Notify the scheduler of the dependecy and indicate that it will be evaluated (aka retrieved from the cache locally) Scheduler.NotifyOfSchedulingDecision(buildRequest, this.nodeId); ProcessCachedResult(buildRequest, projectFileInfo, actuallyBuiltTargets, cachedResult); } else { // There's no cached result: we have to build it. Figure out which node to build it on. Project matchingProjectCurrentlyLoaded = null; Project projectCurrentlyLoaded = null; // See if we have a project loaded by the host already that matches the full path, in the // list of projects which were loaded at the beginning of the build. projectCurrentlyLoaded = (Project)this.projectsLoadedByHost[projectFileInfo.FullName]; if (projectCurrentlyLoaded != null) { // See if the global properties and tools version match. if (projectCurrentlyLoaded.IsEquivalentToProject ( projectCurrentlyLoaded.FullFileName, buildRequest.GlobalProperties, buildRequest.ToolsetVersion ) ) { // If so, use it. matchingProjectCurrentlyLoaded = projectCurrentlyLoaded; } } // Decide to build the project on either the current node or remote node string toolsVersionToUse = buildRequest.ToolsetVersion == null ? DefaultToolsVersion : buildRequest.ToolsetVersion; // If a matching project is currently loaded, we will build locally. bool isLocal = (matchingProjectCurrentlyLoaded != null); // If not, we need to search our cache of building projects to see if we have built this project // locally already. if (!isLocal) { // Determine if the project was previously loaded, but is now unloaded. bool projectWasPreviouslyLoaded = this.cacheOfBuildingProjects.HasProjectBeenLoaded(projectFileInfo.FullName, buildRequest.GlobalProperties, toolsVersionToUse); // We do this check because we need to know if the project is already building on this node. // Unlike the check of projectsLoadedByHost, this will also find projects which were added // after the start of the build, such as MSBuild task-generated build requests. bool projectIsLoaded = this.cacheOfBuildingProjects.GetProject(projectFileInfo.FullName, buildRequest.GlobalProperties, toolsVersionToUse) != null; isLocal = projectWasPreviouslyLoaded || projectIsLoaded; } int nodeIndex = EngineCallback.inProcNode; // If the project, properties and tools version is not known locally, it is either being services by a remote node // or we need to let the scheduler pick a node for it to be serviced by using its algorithm. if (!isLocal) { nodeIndex = cacheOfBuildingProjects.GetRemoteProject(projectFileInfo.FullName, buildRequest.GlobalProperties, toolsVersionToUse); } int evaluationNode = Scheduler.CalculateNodeForBuildRequest(buildRequest, nodeIndex); if (matchingProjectCurrentlyLoaded == null && evaluationNode == EngineCallback.inProcNode) { // We haven't already got this project loaded in this Engine, or it was previously unloaded from this Engine, // and we've been scheduled to build it on this node. So create a new project if necessary. // If we peeked at the project file then we need to make sure that if the tools version in the project is not marked as an override then // the project's tools version is the same. If they are the same then override should be false. try { matchingProjectCurrentlyLoaded = GetMatchingProject(projectCurrentlyLoaded, projectFileInfo.FullName, buildRequest.GlobalProperties, buildRequest.ToolsetVersion, buildRequest.TargetNames, buildRequest.ParentBuildEventContext, buildRequest.ToolsVersionPeekedFromProjectFile); } catch (InvalidProjectFileException e) { primaryLoggingServices.LogInvalidProjectFileError(buildRequest.ParentBuildEventContext, e); throw; } } if (evaluationNode != EngineCallback.inProcNode) { // The project will be evaluated remotely so add a record // indicating where this project is being evaluated if (evaluationNode != EngineCallback.parentNode) { cacheOfBuildingProjects.AddRemoteProject(projectFileInfo.FullName, buildRequest.GlobalProperties, toolsVersionToUse, evaluationNode); } } if (Engine.debugMode) { Console.WriteLine("###Missing cached result for " + buildRequest.GetTargetNamesList() + " in " + buildRequest.ProjectFileName + " - building"); } if (evaluationNode == EngineCallback.inProcNode) { ErrorUtilities.VerifyThrow(cacheOfBuildingProjects.GetRemoteProject(projectFileInfo.FullName, buildRequest.GlobalProperties, toolsVersionToUse) == EngineCallback.invalidNode, "Should not build remote projects"); buildRequest.ProjectToBuild = matchingProjectCurrentlyLoaded; this.BuildProjectInternal(buildRequest, null, null, true); } else { // Increment number of projects in progress if (!buildRequest.IsGeneratedRequest) { IncrementProjectsInProgress(); } Router.PostBuildRequest(buildRequest, evaluationNode); } } } catch (InvalidProjectFileException) { // eat the exception because it has already been logged HandleProjectFileInternalException(buildRequest); } }
/// <summary> /// Pretend we're actually building a project when really we're just retrieving the results from the cache. /// </summary> /// <param name="buildRequest"></param> /// <param name="projectFileInfo"></param> /// <param name="actuallyBuiltTargets"></param> /// <param name="cachedResult"></param> private void ProcessCachedResult ( BuildRequest buildRequest, FileInfo projectFileInfo, ArrayList actuallyBuiltTargets, BuildResult cachedResult ) { buildRequest.InitializeFromCachedResult(cachedResult); if (Engine.debugMode) { Console.WriteLine("===Reusing cached result for " + buildRequest.GetTargetNamesList() + " in " + buildRequest.ProjectFileName + " result is " + buildRequest.BuildSucceeded + " EngineNodeID: " + this.nodeId); } BuildEventContext requestContext = buildRequest.ParentBuildEventContext; BuildEventContext currentContext = new BuildEventContext(this.nodeId, BuildEventContext.InvalidTargetId, GetNextProjectId(), BuildEventContext.InvalidTaskId); primaryLoggingServices.LogProjectStarted(cachedResult.ProjectId, requestContext, currentContext, projectFileInfo.FullName, buildRequest.GetTargetNamesList(), new BuildPropertyGroupProxy(new BuildPropertyGroup()), new BuildItemGroupProxy(new BuildItemGroup())); primaryLoggingServices.LogComment(currentContext, MessageImportance.Low, "ToolsVersionInEffectForBuild", buildRequest.ToolsetVersion); for (int i = 0; i < actuallyBuiltTargets.Count; i++) { string builtTargetName = EscapingUtilities.UnescapeAll((string)actuallyBuiltTargets[i]); Target.BuildState buildState = (Target.BuildState)cachedResult.ResultByTarget[builtTargetName]; buildRequest.ResultByTarget[builtTargetName] = buildState; primaryLoggingServices.LogComment(currentContext, ((buildState == Target.BuildState.CompletedSuccessfully) ? "TargetAlreadyCompleteSuccess" : "TargetAlreadyCompleteFailure"), builtTargetName); if (buildState == Target.BuildState.CompletedUnsuccessfully) { break; } } primaryLoggingServices.LogProjectFinished(currentContext, projectFileInfo.FullName, cachedResult.EvaluationResult); if (!buildRequest.IsGeneratedRequest) { CheckForBuildCompletion(); } else { Router.PostDoneNotice(buildRequest); } }
public void GetTargetNamesList() { int nodeProxyId = 1; string projectFileName = "ProjectFileName"; string[] targetNames = null; Dictionary<string, string> dictionary = null; int requestId = 1; // Test the case where we pass in null targets BuildRequest buildRequest = new BuildRequest(nodeProxyId, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false); Assert.IsNull(buildRequest.GetTargetNamesList(), "Expected GetTargetNamesList to be null"); // Test the case where we pass in one target targetNames = new string[] { "Build" }; buildRequest = new BuildRequest(nodeProxyId, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false); Assert.IsTrue(string.Compare("Build", buildRequest.GetTargetNamesList(),StringComparison.OrdinalIgnoreCase)==0, "Expected to see Build as the targetNamesList"); //Test the case where we pass in multiple targets targetNames = new string[] {"Build","Build2"}; buildRequest = new BuildRequest(nodeProxyId, projectFileName, targetNames, (IDictionary)dictionary, null, requestId, false, false); Assert.IsTrue(string.Compare("Build;Build2;", buildRequest.GetTargetNamesList(),StringComparison.OrdinalIgnoreCase)==0, "Expected to see Build;Build2; as the targetNamesList"); }