|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// THE ASSEMBLY BUILT FROM THIS SOURCE FILE HAS BEEN DEPRECATED FOR YEARS. IT IS BUILT ONLY TO PROVIDE
// BACKWARD COMPATIBILITY FOR API USERS WHO HAVE NOT YET MOVED TO UPDATED APIS. PLEASE DO NOT SEND PULL
// REQUESTS THAT CHANGE THIS FILE WITHOUT FIRST CHECKING WITH THE MAINTAINERS THAT THE FIX IS REQUIRED.
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.BuildEngine.Shared;
namespace Microsoft.Build.BuildEngine
{
/// <summary>
/// This class is used to construct and contain the state of an inprogress targets. The primary data
/// includes build requests blocked until this target completes and build requests that must complete
/// before this target can make forward process.
/// </summary>
internal class TargetInProgessState
{
#region Constructors
internal TargetInProgessState()
{
}
internal TargetInProgessState
(
EngineCallback engineCallback,
Target target,
List<ProjectBuildState> waitingBuildStates,
ProjectBuildState initiatingRequest,
BuildRequest[] outstandingBuildRequests,
string projectName
)
{
this.targetId = new TargetIdWrapper(target);
this.outstandingBuildRequests = outstandingBuildRequests;
// For each waiting build context try to find the parent target
this.parentBuildRequests = new List<BuildRequest>();
this.parentTargets = new List<TargetIdWrapper>();
this.projectName = projectName;
// Process the waiting contexts if there are any
if (waitingBuildStates != null)
{
for (int i = 0; i < waitingBuildStates.Count; i++)
{
ProcessBuildContext(engineCallback, waitingBuildStates[i], target);
}
}
// Process the initiating context
ProcessBuildContext(engineCallback, initiatingRequest, target);
}
/// <summary>
/// Figure out the parent target or the parent build request for the given context
/// </summary>
private void ProcessBuildContext(EngineCallback engineCallback, ProjectBuildState buildContext, Target target)
{
BuildRequest parentRequest;
TargetIdWrapper parentName = FindParentTarget(engineCallback, buildContext, target, out parentRequest);
if (parentName != null)
{
parentTargets.Add(parentName);
}
if (parentRequest != null)
{
parentBuildRequests.Add(parentRequest);
}
}
#endregion
#region Properties
/// <summary>
/// Unique identifier for the target
/// </summary>
internal TargetIdWrapper TargetId
{
get
{
return this.targetId;
}
}
/// <summary>
/// List of unique identifiers for the targets that are blocked until the current
/// target completes
/// </summary>
internal List<TargetIdWrapper> ParentTargets
{
get
{
return this.parentTargets;
}
}
/// <summary>
/// List of build requests that are blocked until the current
/// target completes
/// </summary>
internal List<BuildRequest> ParentBuildRequests
{
get
{
return this.parentBuildRequests;
}
}
/// <summary>
/// Array of build requests that must complete before the current
/// target can make forward process
internal BuildRequest[] OutstandingBuildRequests
{
get
{
return this.outstandingBuildRequests;
}
}
/// <summary>
/// An array of unique identifiers for the targets that generated the build requests (parentBuildRequests)
/// that are blocked until the current target completes. This array can only be calculated given
/// the target information for all the nodes in the system.
/// </summary>
internal TargetIdWrapper[] ParentTargetsForBuildRequests
{
get
{
return this.parentTargetsForBuildRequests;
}
set
{
this.parentTargetsForBuildRequests = value;
}
}
/// <summary>
/// True if the target was requested by the host. The value is only valid for targets on the
/// parent node.
/// </summary>
internal bool RequestedByHost
{
get
{
if (targetId.nodeId == 0)
{
return requestedByHost;
}
return false;
}
}
/// <summary>
/// Name of the project containing the target (only used for logging)
/// </summary>
internal string ProjectName
{
get
{
return projectName;
}
}
#endregion
#region Methods
/// <summary>
/// Given a build state try to find the parent target that caused this build state to
/// come into being either via dependent, on error relationship or via IBuildEngine call
/// </summary>
internal TargetIdWrapper FindParentTarget
(
EngineCallback engineCallback,
ProjectBuildState buildContext,
Target target,
out BuildRequest parentRequest
)
{
// We need to find the parent target
parentRequest = null;
// Skip build states that have already been filled
if (buildContext.CurrentBuildContextState == ProjectBuildState.BuildContextState.RequestFilled)
{
return null;
}
// Check if the target was called due to a onerror or depends on call
if (buildContext.ContainsBlockingTarget(target.Name))
{
// Figure out the record for the parent target
Project containingProject = target.ParentProject;
Target parentTarget = containingProject.Targets[buildContext.GetParentTarget(target.Name)];
return new TargetIdWrapper(parentTarget);
}
else
{
// The build context must have formed due to IBuildEngine call
ErrorUtilities.VerifyThrow(
String.Equals(EscapingUtilities.UnescapeAll(buildContext.NameOfTargetInProgress), target.Name, StringComparison.OrdinalIgnoreCase),
"The target should be the in progress target for the context");
// This target is called due to IBuildEngine or host request
return FindParentTargetForBuildRequest(engineCallback, buildContext.BuildRequest, out parentRequest);
}
}
/// <summary>
/// Given a build request try to find the target that caused it to come into being
/// </summary>
private TargetIdWrapper FindParentTargetForBuildRequest
(
EngineCallback engineCallback,
BuildRequest triggeringBuildRequest,
out BuildRequest parentTriggeringRequest
)
{
parentTriggeringRequest = null;
// If request is non-external and generated due to IBuildEngine call try
// to find the target that caused the IBuildEngine call
if (triggeringBuildRequest.IsGeneratedRequest && !triggeringBuildRequest.IsExternalRequest)
{
ExecutionContext executionContext =
engineCallback.GetExecutionContextFromHandleId(triggeringBuildRequest.HandleId);
// If the parent context is not a routing context than we can
// get the parent target from it
if (executionContext is TaskExecutionContext)
{
return new TargetIdWrapper(((TaskExecutionContext)executionContext).ParentTarget);
}
// If the parent context if a routing context the parent target is not available
// on the current node, so store the request instead
else
{
parentTriggeringRequest = triggeringBuildRequest;
}
}
// If the request is external to the node - store the request since the parent target
// is not available
else if (triggeringBuildRequest.IsExternalRequest)
{
parentTriggeringRequest = triggeringBuildRequest;
}
else
{
requestedByHost = true;
}
return null;
}
/// <summary>
/// This function checks if the given ProjectBuildState is caused by a given parent target (via
/// a dependency, onerror or IBuildEngine relationship)
/// </summary>
internal bool CheckBuildContextForParentMatch
(
EngineCallback engineCallback,
TargetIdWrapper parentId,
Target target,
ProjectBuildState projectBuildState
)
{
BuildRequest parentRequest;
TargetInProgessState.TargetIdWrapper parentName =
FindParentTarget(engineCallback, projectBuildState, target, out parentRequest);
if (parentName?.Equals(parentId) == true)
{
return true;
}
if (parentRequest != null)
{
for (int j = 0; j < parentBuildRequests.Count; j++)
{
if (parentRequest.HandleId == parentBuildRequests[j].HandleId &&
parentRequest.RequestId == parentBuildRequests[j].RequestId)
{
if (parentTargetsForBuildRequests[j].Equals(parentId))
{
return true;
}
else
{
return false;
}
}
}
}
return false;
}
#endregion
#region Data
// Unique identifier for the target
private TargetIdWrapper targetId;
// List of targets waiting on the current target
private List<TargetIdWrapper> parentTargets;
// List of build requests waiting on the current target
private List<BuildRequest> parentBuildRequests;
// List of the build requests the target is waiting on
private BuildRequest[] outstandingBuildRequests;
// Mapping between list of build requests waiting on the current target and targets
// from which these build reuquests originated
private TargetIdWrapper[] parentTargetsForBuildRequests;
// Name of the project containing the target (only used for logging)
private string projectName;
// Set to true if the target had a been requested by host (direct requests from host only occur on
// parent node)
private bool requestedByHost;
#endregion
#region CustomSerializationToStream
internal void WriteToStream(BinaryWriter writer)
{
#region TargetId
if (targetId == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
targetId.WriteToStream(writer);
}
#endregion
#region ParentTargets
if (parentTargets == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
writer.Write((Int32)parentTargets.Count);
foreach (TargetIdWrapper target in parentTargets)
{
if (target == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
target.WriteToStream(writer);
}
}
}
#endregion
#region ParentBuildRequests
if (parentBuildRequests == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
writer.Write((Int32)parentBuildRequests.Count);
foreach (BuildRequest request in parentBuildRequests)
{
if (request == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
request.WriteToStream(writer);
}
}
}
#endregion
#region OutstandingBuildRequests
if (outstandingBuildRequests == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
writer.Write((Int32)outstandingBuildRequests.Length);
for (int i = 0; i < outstandingBuildRequests.Length; i++)
{
if (outstandingBuildRequests[i] == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
outstandingBuildRequests[i].WriteToStream(writer);
}
}
}
#endregion
#region ParentTargetsForBuildRequests
if (parentTargetsForBuildRequests == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
writer.Write((Int32)parentTargetsForBuildRequests.Length);
for (int i = 0; i < parentTargetsForBuildRequests.Length; i++)
{
if (parentTargetsForBuildRequests[i] == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
parentTargetsForBuildRequests[i].WriteToStream(writer);
}
}
}
#endregion
#region ProjectName
if (projectName == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
writer.Write(projectName);
}
#endregion
}
internal void CreateFromStream(BinaryReader reader)
{
#region TargetId
if (reader.ReadByte() == 0)
{
targetId = null;
}
else
{
targetId = new TargetIdWrapper();
targetId.CreateFromStream(reader);
}
#endregion
#region ParentTargets
if (reader.ReadByte() == 0)
{
parentTargets = null;
}
else
{
int numberOfTargets = reader.ReadInt32();
parentTargets = new List<TargetIdWrapper>(numberOfTargets);
for (int i = 0; i < numberOfTargets; i++)
{
if (reader.ReadByte() == 0)
{
parentTargets.Add(null);
}
else
{
TargetIdWrapper wrapper = new TargetIdWrapper();
wrapper.CreateFromStream(reader);
parentTargets.Add(wrapper);
}
}
}
#endregion
#region ParentBuildRequests
if (reader.ReadByte() == 0)
{
parentBuildRequests = null;
}
else
{
int numberOfRequests = reader.ReadInt32();
parentBuildRequests = new List<BuildRequest>(numberOfRequests);
for (int i = 0; i < numberOfRequests; i++)
{
if (reader.ReadByte() == 0)
{
parentBuildRequests.Add(null);
}
else
{
parentBuildRequests.Add(BuildRequest.CreateFromStream(reader));
}
}
}
#endregion
#region OutstandingBuildRequests
if (reader.ReadByte() == 0)
{
outstandingBuildRequests = null;
}
else
{
int numberOfBuildRequests = reader.ReadInt32();
outstandingBuildRequests = new BuildRequest[numberOfBuildRequests];
for (int i = 0; i < numberOfBuildRequests; i++)
{
if (reader.ReadByte() == 0)
{
outstandingBuildRequests[i] = null;
}
else
{
outstandingBuildRequests[i] = BuildRequest.CreateFromStream(reader);
}
}
}
#endregion
#region ParentTargetsForBuildRequests
if (reader.ReadByte() == 0)
{
parentTargetsForBuildRequests = null;
}
else
{
int numberOfTargetsForBuildRequests = reader.ReadInt32();
parentTargetsForBuildRequests = new TargetIdWrapper[numberOfTargetsForBuildRequests];
for (int i = 0; i < numberOfTargetsForBuildRequests; i++)
{
if (reader.ReadByte() == 0)
{
parentTargetsForBuildRequests[i] = null;
}
else
{
TargetIdWrapper wrapper = new TargetIdWrapper();
wrapper.CreateFromStream(reader);
parentTargetsForBuildRequests[i] = wrapper;
}
}
}
#endregion
#region ProjectName
if (reader.ReadByte() == 0)
{
projectName = null;
}
else
{
projectName = reader.ReadString();
}
#endregion
}
#endregion
/// <summary>
/// A class that contains information to uniquely identify a target
/// </summary>
internal class TargetIdWrapper
{
internal TargetIdWrapper()
{
}
internal TargetIdWrapper(Target target)
{
this.name = target.Name;
this.projectId = target.ParentProject.Id;
this.nodeId = target.ParentEngine.NodeId;
this.id = target.Id;
}
/// <summary>
/// Override the equals operator to give valuetype comparison semantics
/// </summary>
public override bool Equals(object obj)
{
TargetIdWrapper other = obj as TargetIdWrapper;
if (other != null)
{
if (other.projectId == projectId && other.nodeId == nodeId &&
(String.Equals(other.name, name, StringComparison.OrdinalIgnoreCase)))
{
return true;
}
return false;
}
return base.Equals(obj);
}
public override int GetHashCode()
{
return projectId;
}
// Target name
internal string name;
// Id for the parent project
internal int projectId;
// Id for the node where the target exists
internal int nodeId;
// Target Id
internal int id;
#region CustomSerializationToStream
internal void WriteToStream(BinaryWriter writer)
{
if (name == null)
{
writer.Write((byte)0);
}
else
{
writer.Write((byte)1);
writer.Write(name);
}
writer.Write((Int32)projectId);
writer.Write((Int32)nodeId);
writer.Write((Int32)id);
}
internal void CreateFromStream(BinaryReader reader)
{
if (reader.ReadByte() == 0)
{
name = null;
}
else
{
name = reader.ReadString();
}
projectId = reader.ReadInt32();
nodeId = reader.ReadInt32();
id = reader.ReadInt32();
}
#endregion
}
}
}
|