using Microsoft.Build.Evaluation;
using System.Linq;
using System.IO;
+using Microsoft.Build.Exceptions;
namespace Microsoft.Build.Internal
{
BuildSubmission submission;
ProjectInstance project;
+ ProjectTargetInstance current_target;
ProjectTaskInstance current_task;
EventSource event_source;
public IDictionary<string,string> TargetOutputs;
public string ToolsVersion;
public BuildTaskFactory BuildTaskFactory;
+
+ public void AddTargetResult (string targetName, TargetResult targetResult)
+ {
+ if (!Result.HasResultsForTarget (targetName))
+ Result.AddResultsForTarget (targetName, targetResult);
+ }
}
void BuildProject (InternalBuildArguments args)
this.project = args.Project;
event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null));
-
+
+ var initialPropertiesFormatted = "Initial Properties:\n" + string.Join (Environment.NewLine, project.Properties.OrderBy (p => p.Name).Select (p => string.Format ("{0} = {1}", p.Name, p.EvaluatedValue)).ToArray ());
+ event_source.FireMessageRaised (this, new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low));
+
// null targets -> success. empty targets -> success(!)
if (request.TargetNames == null)
args.Result.OverallResult = BuildResultCode.Success;
else {
foreach (var targetName in request.TargetNames.Where (t => t != null))
- args.Result.AddResultsForTarget (targetName, BuildTarget (targetName, args));
+ args.AddTargetResult (targetName, BuildTargetByName (targetName, args));
// FIXME: check .NET behavior, whether cancellation always results in failure.
args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Select (p => p.Value).Any (r => r.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success;
event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success));
}
- TargetResult BuildTarget (string targetName, InternalBuildArguments args)
+ TargetResult BuildTargetByName (string targetName, InternalBuildArguments args)
{
var targetResult = new TargetResult ();
else if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target))
targetResult.Failure (new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath)));
else {
- // process DependsOnTargets first.
- foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Where (s => !string.IsNullOrEmpty (s))) {
- var result = BuildTarget (dep, args);
- args.Result.AddResultsForTarget (dep, result);
- if (result.ResultCode == TargetResultCode.Failure) {
- targetResult.Failure (null);
+ current_target = target;
+ try {
+ if (!DoBuildTarget (targetResult, args))
return targetResult;
- }
+ } finally {
+ current_target = null;
}
-
- event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, targetName, project.FullPath, target.FullPath));
-
- // Here we check cancellation (only after TargetStarted event).
- if (args.CheckCancel ()) {
- targetResult.Failure (new OperationCanceledException ("Build has canceled"));
- return targetResult;
+ Func<string,ITaskItem> creator = s => new TargetOutputTaskItem () { ItemSpec = s };
+ var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
+ });
+ targetResult.Success (items);
+ event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, true));
+ }
+ return targetResult;
+ }
+
+ bool DoBuildTarget (TargetResult targetResult, InternalBuildArguments args)
+ {
+ var request = submission.BuildRequest;
+ var target = current_target;
+
+ // process DependsOnTargets first.
+ foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Where (s => !string.IsNullOrEmpty (s)).Select (s => s.Trim ())) {
+ var result = BuildTargetByName (dep, args);
+ args.AddTargetResult (dep, result);
+ if (result.ResultCode == TargetResultCode.Failure) {
+ targetResult.Failure (null);
+ return false;
}
-
+ }
+
+ event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath));
+
+ // Here we check cancellation (only after TargetStarted event).
+ if (args.CheckCancel ()) {
+ targetResult.Failure (new BuildAbortedException ("Build has canceled"));
+ return false;
+ }
+
+ var propsToRestore = new Dictionary<string,string> ();
+ var itemsToRemove = new List<ProjectItemInstance> ();
+ try {
foreach (var c in target.Children.OfType<ProjectPropertyGroupTaskInstance> ()) {
if (!args.Project.EvaluateCondition (c.Condition))
continue;
- throw new NotImplementedException ();
+ foreach (var p in c.Properties) {
+ if (!args.Project.EvaluateCondition (p.Condition))
+ continue;
+ var value = args.Project.ExpandString (p.Value);
+ propsToRestore.Add (p.Name, project.GetPropertyValue (value));
+ project.SetProperty (p.Name, value);
+ }
}
foreach (var c in target.Children.OfType<ProjectItemGroupTaskInstance> ()) {
if (!args.Project.EvaluateCondition (c.Condition))
continue;
- throw new NotImplementedException ();
+ foreach (var item in c.Items) {
+ Func<string,ProjectItemInstance> creator = i => new ProjectItemInstance (project, item.ItemType, item.Metadata.Select (m => new KeyValuePair<string,string> (m.Name, m.Value)), i);
+ foreach (var ti in project.GetAllItems (item.Include, item.Exclude, creator, creator, s => s == item.ItemType, (ti, s) => ti.SetMetadata ("RecurseDir", s)))
+ itemsToRemove.Add (ti);
+ }
}
foreach (var c in target.Children.OfType<ProjectOnErrorInstance> ()) {
if (!args.Project.EvaluateCondition (c.Condition))
throw new NotImplementedException ();
}
foreach (var ti in target.Children.OfType<ProjectTaskInstance> ()) {
- var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, targetName, ti.Name);
- if (!args.Project.EvaluateCondition (ti.Condition))
+ var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, ti.Name);
+ if (!args.Project.EvaluateCondition (ti.Condition)) {
+ event_source.FireMessageRaised (this, new BuildMessageEventArgs (string.Format ("Task '{0}' was skipped because condition '{1}' wasn't met.", ti.Name, ti.Condition), null, null, MessageImportance.Low));
continue;
+ }
current_task = ti;
var factoryIdentityParameters = new Dictionary<string,string> ();
factoryIdentityParameters ["MSBuildArchitecture"] = ti.MSBuildArchitecture;
#endif
var task = args.BuildTaskFactory.CreateTask (ti.Name, factoryIdentityParameters, this);
+ event_source.FireMessageRaised (this, new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", ti.Name, task.GetType ().AssemblyQualifiedName), null, null, MessageImportance.Low));
task.HostObject = host;
task.BuildEngine = this;
// FIXME: this cannot be that simple, value has to be converted to the appropriate target type.
ti.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ())));
foreach (var p in ti.Parameters) {
var prop = task.GetType ().GetProperty (p.Key);
+ var value = project.ExpandString (p.Value);
if (prop == null)
throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", ti.Name, p.Key));
if (!prop.CanWrite)
throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", ti.Name, p.Key));
- prop.SetValue (task, ConvertTo (p.Value, prop.PropertyType), null);
+ prop.SetValue (task, ConvertTo (value, prop.PropertyType), null);
}
event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, ti.FullPath, ti.Name));
if (!task.Execute ()) {
event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, ti.FullPath, ti.Name, false));
targetResult.Failure (null);
if (!ContinueOnError) {
- event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Failed", null, targetName, project.FullPath, target.FullPath, false));
- return targetResult;
+ event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Failed", null, target.Name, project.FullPath, target.FullPath, false));
+ return false;
}
} else {
event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, ti.FullPath, ti.Name, true));
}
}
}
- Func<string,ITaskItem> creator = s => new TargetOutputTaskItem () { ItemSpec = s };
- var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
- });
- targetResult.Success (items);
- event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, true));
+ } finally {
+ // restore temporary property state to the original state.
+ foreach (var p in propsToRestore) {
+ if (p.Value == string.Empty)
+ project.RemoveProperty (p.Key);
+ else
+ project.SetProperty (p.Key, p.Value);
+ }
+ foreach (var item in itemsToRemove)
+ project.RemoveItem (item);
}
- return targetResult;
+ return true;
}
object ConvertTo (string source, Type targetType)
{
- if (targetType.IsSubclassOf (typeof (ITaskItem)))
+ if (targetType == typeof (ITaskItem) || targetType.IsSubclassOf (typeof (ITaskItem)))
return new TargetOutputTaskItem () { ItemSpec = source };
if (targetType.IsArray)
- return new ArrayList (source.Split (';').Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ())
+ return new ArrayList (source.Split (';').Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ())
.ToArray (targetType.GetElementType ());
else
return Convert.ChangeType (source, targetType);
{
if (source == null)
return string.Empty;
- var type = source.GetType ();
- if (type.IsSubclassOf (typeof (ITaskItem)))
+ if (source is ITaskItem)
return ((ITaskItem) source).ItemSpec;
- if (type.IsArray)
+ if (source.GetType ().IsArray)
return string.Join (":", ((Array) source).Cast<object> ().Select (o => ConvertFrom (o)).ToArray ());
else
return (string) Convert.ChangeType (source, typeof (string));
class TargetOutputTaskItem : ITaskItem2
{
+ Hashtable metadata = new Hashtable ();
+
#region ITaskItem2 implementation
public string GetMetadataValueEscaped (string metadataName)
{
- return null;
+ return ProjectCollection.Escape ((string) metadata [metadataName]);
}
public void SetMetadataValueLiteral (string metadataName, string metadataValue)
{
- throw new NotSupportedException ();
+ metadata [metadataName] = ProjectCollection.Unescape (metadataValue);
}
public IDictionary CloneCustomMetadataEscaped ()
{
- return new Hashtable ();
+ var ret = new Hashtable ();
+ foreach (DictionaryEntry e in metadata)
+ ret [e.Key] = ProjectCollection.Escape ((string) e.Value);
+ return ret;
}
public string EvaluatedIncludeEscaped {
get { return ProjectCollection.Escape (ItemSpec); }
#region ITaskItem implementation
public IDictionary CloneCustomMetadata ()
{
- return new Hashtable ();
+ return new Hashtable (metadata);
}
public void CopyMetadataTo (ITaskItem destinationItem)
{
- // do nothing
+ foreach (DictionaryEntry e in metadata)
+ destinationItem.SetMetadata ((string) e.Key, (string) e.Value);
}
public string GetMetadata (string metadataName)
{
- return null;
+ return (string) metadata [metadataName];
}
public void RemoveMetadata (string metadataName)
{
- // do nothing
+ metadata.Remove (metadataName);
}
public void SetMetadata (string metadataName, string metadataValue)
{
- throw new NotSupportedException ();
+ metadata [metadataName] = metadataValue;
}
public string ItemSpec { get; set; }
public int MetadataCount {
- get { return 0; }
+ get { return metadata.Count; }
}
public ICollection MetadataNames {
- get { return new ArrayList (); }
+ get { return metadata.Keys; }
}
#endregion
}
}
public bool ContinueOnError {
- get {
- switch (current_task.ContinueOnError) {
- case "WarnAndContinue":
- case "ErrorAndContinue":
- return true;
- case "ErrorAndStop":
- return false;
- }
- return !string.IsNullOrEmpty (current_task.ContinueOnError) && project.EvaluateCondition (current_task.ContinueOnError);
+ get { return current_task != null && project.EvaluateCondition (current_task.Condition) && EvaluateContinueOnError (current_task.ContinueOnError); }
+ }
+
+ bool EvaluateContinueOnError (string value)
+ {
+ switch (value) {
+ case "WarnAndContinue":
+ case "ErrorAndContinue":
+ return true;
+ case "ErrorAndStop":
+ return false;
}
+ // empty means "stop on error", so don't pass empty string to EvaluateCondition().
+ return !string.IsNullOrEmpty (value) && project.EvaluateCondition (value);
}
public int LineNumberOfTaskNode {