using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
needToReevaluate = false;
Reevaluate ();
}
-
+
+#if NET_4_0
+ ProcessBeforeAndAfterTargets ();
+#endif
+
if (targetNames == null || targetNames.Length == 0) {
if (defaultTargets != null && defaultTargets.Length != 0) {
targetNames = defaultTargets;
return sb.ToString ();
}
+#if NET_4_0
+ void ProcessBeforeAndAfterTargets ()
+ {
+ var beforeTable = Targets.AsIEnumerable ()
+ .SelectMany (target => GetTargetNamesFromString (target.BeforeTargets),
+ (target, before_target) => new {before_target, name = target.Name})
+ .ToLookup (x => x.before_target, x => x.name)
+ .ToDictionary (x => x.Key, x => x.Distinct ().ToList ());
+
+ foreach (var pair in beforeTable) {
+ if (targets.Exists (pair.Key))
+ targets [pair.Key].BeforeThisTargets = pair.Value;
+ else
+ LogWarning (FullFileName, "Target '{0}', not found in the project", pair.Key);
+ }
+
+ var afterTable = Targets.AsIEnumerable ()
+ .SelectMany (target => GetTargetNamesFromString (target.AfterTargets),
+ (target, after_target) => new {after_target, name = target.Name})
+ .ToLookup (x => x.after_target, x => x.name)
+ .ToDictionary (x => x.Key, x => x.Distinct ().ToList ());
+
+ foreach (var pair in afterTable) {
+ if (targets.Exists (pair.Key))
+ targets [pair.Key].AfterThisTargets = pair.Value;
+ else
+ LogWarning (FullFileName, "Target '{0}', not found in the project", pair.Key);
+ }
+ }
+
+ string[] GetTargetNamesFromString (string targets)
+ {
+ Expression expr = new Expression ();
+ expr.Parse (targets, ParseOptions.AllowItemsNoMetadataAndSplit);
+ return (string []) expr.ConvertTo (this, typeof (string []));
+ }
+#endif
+
[MonoTODO]
public string [] GetConditionedPropertyValues (string propertyName)
{
try {
buildState = BuildState.Started;
- result = BuildDependencies (GetDependencies (), out executeOnErrors);
- if (!result && executeOnErrors)
- ExecuteOnErrors ();
-
- if (result)
- // deps built fine, do main build
- result = DoBuild (out executeOnErrors);
+#if NET_4_0
+ result = BuildDependencies (out executeOnErrors) &&
+ BuildBeforeThisTargets (out executeOnErrors) &&
+ DoBuild (out executeOnErrors) && // deps & Before targets built fine, do main build
+ BuildAfterThisTargets (out executeOnErrors);
+#else
+ result = BuildDependencies (out executeOnErrors) && DoBuild (out executeOnErrors);
+#endif
buildState = BuildState.Finished;
} catch (Exception e) {
return result;
}
- List <Target> GetDependencies ()
+ bool BuildDependencies (out bool executeOnErrors)
{
- List <Target> list = new List <Target> ();
- Target t;
- string [] targetNames;
- Expression deps;
-
- if (DependsOnTargets != String.Empty) {
- deps = new Expression ();
- deps.Parse (DependsOnTargets, ParseOptions.AllowItemsNoMetadataAndSplit);
- targetNames = (string []) deps.ConvertTo (Project, typeof (string []));
- foreach (string dep_name in targetNames) {
- t = project.Targets [dep_name.Trim ()];
- if (t == null)
- throw new InvalidProjectFileException (String.Format (
- "Target '{0}', a dependency of target '{1}', not found.",
- dep_name.Trim (), Name));
- list.Add (t);
- }
- }
- return list;
+ executeOnErrors = false;
+
+ if (String.IsNullOrEmpty (DependsOnTargets))
+ return true;
+
+ var expr = new Expression ();
+ expr.Parse (DependsOnTargets, ParseOptions.AllowItemsNoMetadataAndSplit);
+ string [] targetNames = (string []) expr.ConvertTo (Project, typeof (string []));
+
+ bool result = BuildOtherTargets (targetNames,
+ tname => engine.LogError ("Target '{0}', a dependency of target '{1}', not found.",
+ tname, Name),
+ out executeOnErrors);
+ if (!result && executeOnErrors)
+ ExecuteOnErrors ();
+
+ return result;
+ }
+
+#if NET_4_0
+ bool BuildBeforeThisTargets (out bool executeOnErrors)
+ {
+ executeOnErrors = false;
+ bool result = BuildOtherTargets (BeforeThisTargets, null, out executeOnErrors);
+ if (!result && executeOnErrors)
+ ExecuteOnErrors ();
+
+ return result;
}
- bool BuildDependencies (List <Target> deps, out bool executeOnErrors)
+ bool BuildAfterThisTargets (out bool executeOnErrors)
{
executeOnErrors = false;
- foreach (Target t in deps) {
+ //missing_target handler not required as these are picked from actual target's
+ //"Before/AfterTargets attributes!
+ bool result = BuildOtherTargets (AfterThisTargets, null, out executeOnErrors);
+ if (!result && executeOnErrors)
+ ExecuteOnErrors ();
+
+ return result;
+ }
+#endif
+
+ bool BuildOtherTargets (IEnumerable<string> targetNames, Action<string> missing_target, out bool executeOnErrors)
+ {
+ executeOnErrors = false;
+ if (targetNames == null)
+ // nothing to build
+ return true;
+
+ foreach (string target_name in targetNames) {
+ var t = project.Targets [target_name.Trim ()];
+ if (t == null) {
+ if (missing_target != null)
+ missing_target (target_name);
+ return false;
+ }
+
if (t.BuildState == BuildState.NotStarted)
if (!t.Build (null, out executeOnErrors))
return false;
+
if (t.BuildState == BuildState.Started)
throw new InvalidProjectFileException ("Cycle in target dependencies detected");
}
}
}
+#if NET_4_0
+ internal string BeforeTargets {
+ get { return targetElement.GetAttribute ("BeforeTargets"); }
+ }
+
+ internal string AfterTargets {
+ get { return targetElement.GetAttribute ("AfterTargets"); }
+ }
+
+ internal List<string> BeforeThisTargets { get; set; }
+ internal List<string> AfterThisTargets { get; set; }
+#endif
+
internal List<BuildTask> BuildTasks {
get { return buildTasks; }
}
yield return kvp.Value;
}
+ internal IEnumerable<Target> AsIEnumerable ()
+ {
+ foreach (KeyValuePair <string, Target> kvp in targetsByName)
+ yield return kvp.Value;
+ }
+
public void RemoveTarget (Target targetToRemove)
{
if (targetToRemove == null)
Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected extra messages found");
}
+#if NET_4_0
+ [Test]
+ public void TestBeforeAndAfterTargets ()
+ {
+ Engine engine;
+ Project project;
+
+ string projectString = @"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"" ToolsVersion=""4.0"">
+ <Target Name=""DefaultBeforeTarget1"" BeforeTargets=""Default"">
+ <Message Text=""Hello from DefaultBeforeTarget1""/>
+ </Target>
+
+ <Target Name=""DefaultBeforeTarget2"" BeforeTargets=""Default;Default;NonExistant"">
+ <Message Text=""Hello from DefaultBeforeTarget2""/>
+ </Target>
+
+
+ <Target Name=""DefaultAfterTarget"" AfterTargets=""Default ; Foo"">
+ <Message Text=""Hello from DefaultAfterTarget""/>
+ </Target>
+
+ <Target Name=""Default"" DependsOnTargets=""DefaultDependsOn"">
+ <Message Text=""Hello from Default""/>
+ </Target>
+
+ <Target Name=""DefaultDependsOn"">
+ <Message Text=""Hello from DefaultDependsOn""/>
+ </Target>
+ </Project>";
+
+ engine = new Engine ();
+ project = engine.CreateNewProject ();
+ project.LoadXml (projectString);
+
+ MonoTests.Microsoft.Build.Tasks.TestMessageLogger logger =
+ new MonoTests.Microsoft.Build.Tasks.TestMessageLogger ();
+ engine.RegisterLogger (logger);
+
+ if (!project.Build ("Default")) {
+ logger.DumpMessages ();
+ Assert.Fail ("Build failed");
+ }
+
+ logger.CheckLoggedMessageHead ("Hello from DefaultDependsOn", "A1");
+ logger.CheckLoggedMessageHead ("Hello from DefaultBeforeTarget1", "A1");
+ logger.CheckLoggedMessageHead ("Hello from DefaultBeforeTarget2", "A1");
+ logger.CheckLoggedMessageHead ("Hello from Default", "A1");
+ logger.CheckLoggedMessageHead ("Hello from DefaultAfterTarget", "A1");
+
+ Assert.AreEqual (0, logger.NormalMessageCount, "Unexpected messages found");
+
+ //warnings for referencing unknown targets: NonExistant and Foo
+ Assert.AreEqual (2, logger.WarningsCount, "Expected warnings not raised");
+ }
+#endif
+
}
}
set { task_finished = value; }
}
+ public int WarningsCount { get; set; }
+ public int ErrorsCount { get; set; }
+
public int Count
{
get { return messages.Count; }
public void Initialize (IEventSource eventSource)
{
eventSource.MessageRaised += new BuildMessageEventHandler (MessageHandler);
+
eventSource.ErrorRaised += new BuildErrorEventHandler (AllMessagesHandler);
+ eventSource.ErrorRaised += (e,o) => ErrorsCount ++;
+
eventSource.WarningRaised += new BuildWarningEventHandler(AllMessagesHandler);
+ eventSource.WarningRaised += (e,o) => WarningsCount ++;
+
eventSource.TargetStarted += delegate { target_started++; };
eventSource.TargetFinished += delegate { target_finished++; };
eventSource.TaskStarted += delegate { task_started++; };