/// <summary> /// This method creates a new routing context. This method is not thread safe and must be called /// only from the engine thread. /// </summary> internal int CreateRoutingContext ( int nodeIndex, int parentHandleId, int parentNodeIndex, int parentRequestId, CacheScope cacheScope, BuildRequest triggeringBuildRequest, BuildEventContext buildEventContext ) { int handleId = nextContextId; nextContextId = nextContextId + 1; RequestRoutingContext executionContext = new RequestRoutingContext(handleId, nodeIndex, parentHandleId, parentNodeIndex, parentRequestId, cacheScope, triggeringBuildRequest, buildEventContext); executionContexts.Add(handleId, executionContext); return(handleId); }
/// <summary> /// This method is used by the child node to post results of a build request back to the /// parent node. The parent node then decides if need to re-route the results to another node /// that requested the evaluation or if it will consume the result locally /// </summary> /// <param name="buildResult"></param> public void PostBuildResultToHost(BuildResult buildResult) { RequestRoutingContext routingContext = GetRoutingContextFromHandleId(buildResult.HandleId); ErrorUtilities.VerifyThrow(routingContext.CacheScope != null, "Cache scope should be created for this context"); // Cache the results routingContext.CacheScope.AddCacheEntryForBuildResults(buildResult); if (Engine.debugMode) { Console.WriteLine("Received result for HandleId " + buildResult.HandleId + ":" + buildResult.RequestId + " mapped to " + routingContext.ParentHandleId + ":" + routingContext.ParentRequestId); } // Update the results with the original handle id and request id, so that buildResult.HandleId = routingContext.ParentHandleId; // If the build result is created from a generated build request a done notice should be posted as other targets could be waiting for this target to finish if (buildResult.HandleId != invalidEngineHandle) { buildResult.RequestId = routingContext.ParentRequestId; parentEngine.Router.PostDoneNotice(routingContext.ParentNodeIndex, buildResult); } else // The build results need to be stored into the build request so they can be sent back to the host that requested the build { routingContext.TriggeringBuildRequest.OutputsByTarget = buildResult.OutputsByTarget; routingContext.TriggeringBuildRequest.BuildSucceeded = buildResult.EvaluationResult; routingContext.TriggeringBuildRequest.BuildCompleted = true; parentEngine.PostEngineCommand(new HostBuildRequestCompletionEngineCommand()); } // At this point the execution context we created for the execution of this build request can be deleted lock (freedContexts) { freedContexts.Add(routingContext); } }
private void ProcessBuildRequest(BuildRequest buildRequest) { ExecutionContext executionContext = GetExecutionContextFromHandleId(buildRequest.HandleId); // Restore the requests non-serialized data to the correct state buildRequest.RestoreNonSerializedDefaults(); buildRequest.NodeIndex = executionContext.NodeIndex; ErrorUtilities.VerifyThrow(buildRequest.ParentBuildEventContext != null, "Should not have a null parentBuildEventContext"); ErrorUtilities.VerifyThrow(buildRequest.IsGeneratedRequest == true, "Should not be sending a non generated request from the child node to the parent node"); // For buildRequests originating from the TEM - additional initialization is necessary TaskExecutionContext taskExecutionContext = executionContext as TaskExecutionContext; if (taskExecutionContext != null) { Project parentProject = taskExecutionContext.ParentProject; buildRequest.ParentHandleId = taskExecutionContext.TriggeringBuildRequest.HandleId; buildRequest.ParentRequestId = taskExecutionContext.TriggeringBuildRequest.RequestId; if (buildRequest.ToolsetVersion == null && parentProject.OverridingToolsVersion) { // If the MSBuild task (or whatever) didn't give us a specific tools version, // but the parent project is using an overridden tools version, then use that one buildRequest.ToolsetVersion = parentProject.ToolsVersion; } try { if (buildRequest.GlobalProperties == null) { try { // Make sure we have a blank global properties because if there is a problem merging them we wont have a crash when we try and cache the build result. buildRequest.GlobalProperties = new BuildPropertyGroup(); buildRequest.GlobalProperties = parentEngine.MergeGlobalProperties(parentProject.GlobalProperties, null, buildRequest.ProjectFileName, buildRequest.GlobalPropertiesPassedByTask); } catch (ArgumentException e) { ConvertToInvalidProjectException(buildRequest, parentProject, e); } catch (InvalidOperationException e) { ConvertToInvalidProjectException(buildRequest, parentProject, e); } } // We need to figure out which project object this request is refering to if (buildRequest.ProjectFileName == null) { ErrorUtilities.VerifyThrow(parentProject != null, "Parent project must be non-null"); // This means the caller (the MSBuild task) wants us to use the same project as the calling // project. This allows people to avoid passing in the Projects parameter on the MSBuild task. Project projectToBuild = parentProject; // If the parent project (the calling project) already has the same set of global properties // as what is being requested, just re-use it. Otherwise, we need to instantiate a new // project object that has the same project contents but different global properties. if (!projectToBuild.GlobalProperties.IsEquivalent(buildRequest.GlobalProperties) && (String.Compare(parentProject.ToolsVersion, buildRequest.ToolsetVersion, StringComparison.OrdinalIgnoreCase) == 0)) { projectToBuild = parentEngine.GetMatchingProject(parentProject, parentProject.FullFileName, buildRequest.GlobalProperties, buildRequest.ToolsetVersion, buildRequest.TargetNames, buildRequest.ParentBuildEventContext, buildRequest.ToolsVersionPeekedFromProjectFile); } buildRequest.ProjectToBuild = projectToBuild; buildRequest.ProjectFileName = projectToBuild.FullFileName; buildRequest.FireProjectStartedFinishedEvents = false; } } catch (InvalidProjectFileException e) { buildRequest.BuildCompleted = true; // Store message so it can be logged by the engine build loop buildRequest.BuildException = e; } } else { RequestRoutingContext requestRoutingContext = executionContext as RequestRoutingContext; buildRequest.ParentHandleId = requestRoutingContext.ParentHandleId; buildRequest.ParentRequestId = requestRoutingContext.ParentRequestId; } }
/// <summary> /// For each target that has a cross node build request waiting for it to complete, iterate /// over the list of outstanding requests and find the matching out going request. Once /// the matching request is found - link the parent and child targets. /// </summary> private void LinkCrossNodeBuildRequests() { foreach (GraphNode node in dependencyGraph.Values) { TargetInProgessState.TargetIdWrapper[] parentsForBuildRequests = new TargetInProgessState.TargetIdWrapper[node.targetState.ParentBuildRequests.Count]; for (int j = 0; j < node.targetState.ParentBuildRequests.Count; j++) { BuildRequest buildRequest = node.targetState.ParentBuildRequests[j]; int nodeIndex = buildRequest.NodeIndex; int handleId = buildRequest.HandleId; int requestId = buildRequest.RequestId; bool foundParent = false; // Skip requests that originated from the host if (handleId == EngineCallback.invalidEngineHandle) { node.isRoot = true; continue; } // If the request being analyzed came from one of the child nodes, its incoming external request's // handleId will point at a routing context on the parent engine. If the outgoing request // orginated from another child the two requests (outgoing and incoming) point at different // routing contexts. In that case it is necessary to unwind the incoming request to the routing // context of the outgoing request. If outgoing request originated from the parent node - // there will be only one routing request. if (node.targetState.TargetId.nodeId != 0) { ExecutionContext executionContext = engineCallback.GetExecutionContextFromHandleId(buildRequest.HandleId); RequestRoutingContext routingContext = executionContext as RequestRoutingContext; if (routingContext != null && routingContext.ParentHandleId != EngineCallback.invalidEngineHandle) { ExecutionContext nextExecutionContext = engineCallback.GetExecutionContextFromHandleId(routingContext.ParentHandleId); if (nextExecutionContext is RequestRoutingContext) { nodeIndex = nextExecutionContext.NodeIndex; handleId = routingContext.ParentHandleId; requestId = routingContext.ParentRequestId; } } else { // Skip requests that originated from the host node.isRoot = true; continue; } } // Iterate over all outstanding requests until a match is found foreach (DictionaryEntry entry in outstandingExternalRequests) { BuildRequest[] externalRequests = (BuildRequest[])entry.Value; for (int i = 0; i < externalRequests.Length && !foundParent; i++) { if (handleId == externalRequests[i].HandleId && requestId == externalRequests[i].RequestId && nodeIndex == externalRequests[i].NodeIndex) { // Verify that the project name is the same ErrorUtilities.VerifyThrow( String.Compare(buildRequest.ProjectFileName, externalRequests[i].ProjectFileName, StringComparison.OrdinalIgnoreCase) == 0, "The two requests should have the same project name"); // Link the two graph nodes together GraphNode parentNode = (GraphNode)dependencyGraph[entry.Key]; parentNode.children.Add(node); parentsForBuildRequests[j] = parentNode.targetState.TargetId; foundParent = true; } } if (foundParent) { break; } } } node.targetState.ParentTargetsForBuildRequests = parentsForBuildRequests; } }