summaryrefslogtreecommitdiff
path: root/mcs/class/Microsoft.Build
diff options
context:
space:
mode:
authorJo Shields <directhex@apebox.org>2014-02-19 22:12:43 +0000
committerJo Shields <directhex@apebox.org>2014-02-19 22:12:43 +0000
commit9972bf87b4f27d9c8f358ef8414ac1ab957a2f0f (patch)
tree5bb230c1d698659115f918e243c1d4b0aa4c7f51 /mcs/class/Microsoft.Build
parentd0a215f5626219ff7927f576588a777e5331c7be (diff)
downloadmono-upstream/3.2.8+dfsg.tar.gz
Imported Upstream version 3.2.8+dfsgupstream/3.2.8+dfsg
Diffstat (limited to 'mcs/class/Microsoft.Build')
-rw-r--r--mcs/class/Microsoft.Build/Assembly/AssemblyInfo.cs8
-rw-r--r--mcs/class/Microsoft.Build/Makefile28
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ElementLocation.cs25
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectChooseElement.cs4
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectCommentElement.cs1
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElement.cs53
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElementContainer.cs19
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectExtensionsElement.cs1
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportElement.cs10
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportGroupElement.cs3
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionElement.cs5
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionGroupElement.cs5
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemElement.cs49
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemGroupElement.cs7
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOnErrorElement.cs5
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOtherwiseElement.cs7
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOutputElement.cs7
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectPropertyGroupElement.cs10
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectRootElement.cs24
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTargetElement.cs29
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTaskElement.cs27
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskBodyElement.cs1
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskElement.cs7
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectWhenElement.cs7
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Construction/UsingTaskParameterGroupElement.cs5
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Project.cs1012
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectChangedEventArgs.cs (renamed from mcs/class/Microsoft.Build/Microsoft.Build.Logging/ColorResetter.cs)24
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollection.cs514
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedEventArgs.cs16
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedState.cs19
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItem.cs201
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItemDefinition.cs47
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectMetadata.cs80
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectProperty.cs254
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectXmlChangedEventArgs.cs (renamed from mcs/class/Microsoft.Build/Microsoft.Build.Logging/ColorSetter.cs)27
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ResolvedImport.cs37
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/SubToolset.cs22
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Toolset.cs76
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/BuildAbortedException.cs43
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InternalLoggerException.cs57
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidProjectFileException.cs32
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidToolsetDefinitionException.cs42
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildManager.cs176
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildParameters.cs239
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildRequestData.cs92
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildResult.cs126
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmission.cs96
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmissionCompleteCallback.cs5
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/HostServices.cs109
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/NodeEngineShutdownReason.cs (renamed from mcs/class/Microsoft.Build/Microsoft.Build.Logging/WriteHandler.cs)29
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/OutOfProcNode.cs48
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectInstance.cs308
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemDefinitionInstance.cs30
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskInstance.cs82
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskItemInstance.cs95
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskMetadataInstance.cs58
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemInstance.cs324
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectMetadataInstance.cs30
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectOnErrorInstance.cs82
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskInstance.cs82
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskPropertyInstance.cs61
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyInstance.cs39
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstance.cs85
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstanceChild.cs52
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstance.cs108
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstanceChild.cs16
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputItemInstance.cs42
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputPropertyInstance.cs43
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Execution/TargetResult.cs44
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs634
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs209
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskDatabase.cs138
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskFactory.cs81
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionConstructs.cs197
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionEvaluator.cs521
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParser.jay264
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParserManual.cs271
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionTokenizer.cs309
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/ProjectTaskItem.cs92
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Internal/WindowsCompatibilityExtensions.cs (renamed from mcs/class/Microsoft.Build/Microsoft.Build.Logging/FileLogger.cs)21
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConfigurableForwardingLogger.cs43
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConsoleLogger.cs89
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.Logging/LoggerDescription.cs39
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build.dll.sources51
-rw-r--r--mcs/class/Microsoft.Build/Microsoft.Build_test.dll.sources19
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectItemElementTest.cs42
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectRootElementTest.cs222
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectCollectionTest.cs150
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemDefinitionTest.cs104
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemTest.cs212
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectPropertyTest.cs130
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectTest.cs253
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ResolvedImportTest.cs181
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ToolsetTest.cs53
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildManagerTest.cs201
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildParametersTest.cs66
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildSubmissionTest.cs115
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectInstanceTest.cs103
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectMetadataInstanceTest.cs78
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectTargetInstanceTest.cs172
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs287
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/ConsoleLoggerTest.cs102
-rw-r--r--mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/LoggerDescriptionTest.cs49
103 files changed, 9505 insertions, 1344 deletions
diff --git a/mcs/class/Microsoft.Build/Assembly/AssemblyInfo.cs b/mcs/class/Microsoft.Build/Assembly/AssemblyInfo.cs
index edc155a8af..e14ad00346 100644
--- a/mcs/class/Microsoft.Build/Assembly/AssemblyInfo.cs
+++ b/mcs/class/Microsoft.Build/Assembly/AssemblyInfo.cs
@@ -46,9 +46,9 @@ using System.Runtime.Versioning;
[assembly: AssemblyCompany (Consts.MonoCompany)]
[assembly: AssemblyProduct (Consts.MonoProduct)]
[assembly: AssemblyCopyright (Consts.MonoCopyright)]
-[assembly: AssemblyVersion (Consts.FxVersion)]
-[assembly: SatelliteContractVersion (Consts.FxVersion)]
-[assembly: AssemblyInformationalVersion (Consts.FxFileVersion)]
+[assembly: AssemblyVersion (XBuildConsts.AssemblyVersion)]
+[assembly: SatelliteContractVersion (XBuildConsts.AssemblyVersion)]
+[assembly: AssemblyInformationalVersion (XBuildConsts.FileVersion)]
[assembly: NeutralResourcesLanguage ("en-US")]
@@ -57,6 +57,6 @@ using System.Runtime.Versioning;
[assembly: AssemblyDelaySign (true)]
[assembly: AssemblyKeyFile("../msfinal.pub")]
-[assembly: AssemblyFileVersion (Consts.FxFileVersion)]
+[assembly: AssemblyFileVersion (XBuildConsts.FileVersion)]
[assembly: CompilationRelaxations (CompilationRelaxations.NoStringInterning)]
diff --git a/mcs/class/Microsoft.Build/Makefile b/mcs/class/Microsoft.Build/Makefile
index 173638c983..931b9fc7e5 100644
--- a/mcs/class/Microsoft.Build/Makefile
+++ b/mcs/class/Microsoft.Build/Makefile
@@ -2,14 +2,10 @@ thisdir = class/Microsoft.Build
SUBDIRS =
include ../../build/rules.make
-LIBRARY = Microsoft.Build.dll
+XBUILD_DIR=$(topdir)/tools/xbuild
+include $(XBUILD_DIR)/xbuild.make
-ifneq (4, $(FRAMEWORK_VERSION_MAJOR))
-LIBRARY_NAME = dummy-Microsoft.Build.dll
-NO_INSTALL = yes
-NO_TEST = yes
-NO_SIGN_ASSEMBLY = yes
-endif
+LIBRARY = Microsoft.Build.dll
LIB_MCS_FLAGS = \
/r:$(corlib) \
@@ -17,18 +13,26 @@ LIB_MCS_FLAGS = \
/r:System.Core.dll \
/r:System.Xml.dll \
/r:Microsoft.Build.Engine.dll \
- /r:Microsoft.Build.Framework.dll
+ /r:Microsoft.Build.Framework.dll \
+ /d:MICROSOFT_BUILD_DLL
-TEST_MCS_FLAGS = /r:System.Core.dll
+TEST_MCS_FLAGS = $(LIB_MCS_FLAGS)
EXTRA_DISTFILES = \
+ Microsoft.Build.Internal/ExpressionParser.jay \
Test/FunctionalTestReferenceProject.csproj \
Test/FunctionalTestReferenceProject3.csproj \
Test/Microsoft.Build.Test.csproj \
Test/Microsoft.Build.csproj
+EXPR_PARSER = Microsoft.Build.Internal/ExpressionParser
+
+$(EXPR_PARSER).cs: $(EXPR_PARSER).jay $(topdir)/jay/skeleton.cs
+ (cd Microsoft.Build.Internal; $(topdir)/../jay/jay -ctv < $(topdir)/../jay/skeleton.cs ExpressionParser.jay > ExpressionParser.cs)
+
+BUILT_SOURCES = $(EXPR_PARSER).cs
+
include ../../build/library.make
-export TESTING_MONO=a
-XBUILD_DIR=../../tools/xbuild
-include $(XBUILD_DIR)/xbuild_targets.make
+XBUILD_FRAMEWORK_FOLDERS_PATH=xbuild-testing
+include $(XBUILD_DIR)/xbuild_test.make
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ElementLocation.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ElementLocation.cs
index 43f20dad9b..15fe431cec 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ElementLocation.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ElementLocation.cs
@@ -1,10 +1,10 @@
//
-// ProjectItemDefinitionInstance.cs
+// ElementLocation.cs
//
// Author:
-// Atsushi Enomoto (atsushi@veritas-vos-liberabit.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2012 Xamarin Inc.
+// Copyright (C) 2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -26,7 +26,6 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-#if NET_4_5
using Microsoft.Build.Framework;
using System;
@@ -35,7 +34,10 @@ using System.Collections.Generic;
namespace Microsoft.Build.Construction
{
[Serializable]
- public abstract class ElementLocation
+#if NET_4_5
+ public
+#endif
+ abstract class ElementLocation
{
public abstract int Column { get; }
public abstract string File { get; }
@@ -44,7 +46,16 @@ namespace Microsoft.Build.Construction
public string LocationString {
get { return Line == 0 ? File : String.Format ("{0} ({1}{2})", File, Line, Column != 0 ? "," + Column : String.Empty); }
}
+
+ public override bool Equals (object other)
+ {
+ var o = other as ElementLocation;
+ return (object) o != null && o.File == File && o.Line == Line && o.Column == Column;
+ }
+
+ public override int GetHashCode ()
+ {
+ return (File.GetHashCode () << 16) + (Line << 8) + Column;
+ }
}
}
-
-#endif
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectChooseElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectChooseElement.cs
index 551a67b4c5..d5a52d9b78 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectChooseElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectChooseElement.cs
@@ -29,6 +29,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Internal;
@@ -54,8 +55,9 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return "Choose"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
+ var name = reader.LocalName;
switch (name) {
case "Otherwise":
var other = ContainingProject.CreateOtherwiseElement ();
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectCommentElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectCommentElement.cs
index 23666183d1..36dc17a4fb 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectCommentElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectCommentElement.cs
@@ -47,6 +47,7 @@ namespace Microsoft.Build.Construction
internal override void Load (XmlReader reader)
{
+ FillLocation (reader);
LoadValue (reader);
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElement.cs
index 7454665b7e..6a3cd8f2f3 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElement.cs
@@ -35,6 +35,8 @@ namespace Microsoft.Build.Construction
{
public abstract class ProjectElement
{
+ internal const string MSBuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
+
internal ProjectElement ()
{
linkedListNode = new LinkedListNode<ProjectElement> (this);
@@ -67,9 +69,11 @@ namespace Microsoft.Build.Construction
internal virtual void Load (XmlReader reader)
{
reader.ReadToFollowing (XmlName);
+ FillLocation (reader);
while (reader.MoveToNextAttribute ()) {
LoadAttribute (reader.Name, reader.Value);
}
+ reader.MoveToElement ();
LoadValue (reader);
}
internal virtual void LoadAttribute (string name, string value)
@@ -84,7 +88,7 @@ namespace Microsoft.Build.Construction
Condition = value;
break;
default:
- throw new InvalidProjectFileException (string.Format (
+ throw new InvalidProjectFileException (Location, null, string.Format (
"Attribute \"{0}\" is not known on node \"{1}\" [type {2}].", name, XmlName,
GetType ()));
}
@@ -109,5 +113,52 @@ namespace Microsoft.Build.Construction
if (!string.IsNullOrWhiteSpace (attributeValue))
writer.WriteAttributeString (attributeName, attributeValue);
}
+
+#if NET_4_5
+ public ElementLocation Location { get; private set; }
+ public ElementLocation LabelLocation { get; private set; }
+ public ElementLocation ConditionLocation { get; private set; }
+#else
+ internal ElementLocation Location { get; private set; }
+ internal ElementLocation LabelLocation { get; private set; }
+ internal ElementLocation ConditionLocation { get; private set; }
+#endif
+
+ internal void FillLocation (XmlReader reader)
+ {
+ var l = reader as IXmlLineInfo;
+ if (l != null && l.HasLineInfo ())
+ Location = new ProjectElementLocation (reader.BaseURI, l);
+ if (reader.MoveToAttribute ("Condition") && l.HasLineInfo ())
+ ConditionLocation = new ProjectElementLocation (reader.BaseURI, l);
+ if (reader.MoveToAttribute ("Label") && l.HasLineInfo ())
+ LabelLocation = new ProjectElementLocation (reader.BaseURI, l);
+ reader.MoveToElement ();
+ }
+
+ class ProjectElementLocation : ElementLocation
+ {
+ public ProjectElementLocation (string file, IXmlLineInfo li)
+ {
+ this.file = file;
+ this.line = li.LineNumber;
+ this.column = li.LinePosition;
+ }
+
+ string file;
+ int line;
+ int column;
+
+ public override string File { get { return file; } }
+ public override int Line { get { return line; } }
+ public override int Column { get { return column; } }
+ }
+
+ internal InvalidProjectFileException CreateError (XmlReader reader, string message, int columnOffset = 0)
+ {
+ var li = reader as IXmlLineInfo;
+ bool valid = li != null && li.HasLineInfo ();
+ throw new InvalidProjectFileException (reader.BaseURI, valid ? li.LineNumber : 0, valid ? li.LinePosition + columnOffset : 0, 0, 0, message, null, null, null);
+ }
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElementContainer.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElementContainer.cs
index eb9f59e875..1f1619bbd5 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElementContainer.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectElementContainer.cs
@@ -29,6 +29,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
+using Microsoft.Build.Exceptions;
using Microsoft.Build.Internal;
namespace Microsoft.Build.Construction
@@ -123,11 +124,25 @@ namespace Microsoft.Build.Construction
child.Save (writer);
}
+ internal override void Load (XmlReader reader)
+ {
+ reader.Read ();
+ reader.MoveToContent ();
+ FillLocation (reader);
+ if (reader.LocalName != XmlName || reader.NamespaceURI != MSBuildNamespace)
+ throw CreateError (reader, string.Format ("Unexpected XML {0} \"{1}\" in namespace \"{2}\" appeared, while \"{3}\" in namespace \"{4}\" is expected.",
+ reader.NodeType, reader.LocalName, reader.NamespaceURI, XmlName, MSBuildNamespace), -1);
+ while (reader.MoveToNextAttribute ()) {
+ LoadAttribute (reader.Name, reader.Value);
+ }
+ LoadValue (reader);
+ }
+
internal override void LoadValue (XmlReader reader)
{
while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.Element) {
- var child = LoadChildElement (reader.Name);
+ var child = LoadChildElement (reader);
child.Load (reader.ReadSubtree ());
} else if (reader.NodeType == XmlNodeType.Comment) {
var commentElement = new ProjectCommentElement (ContainingProject);
@@ -137,6 +152,6 @@ namespace Microsoft.Build.Construction
}
}
- internal abstract ProjectElement LoadChildElement (string name);
+ internal abstract ProjectElement LoadChildElement (XmlReader reader);
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectExtensionsElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectExtensionsElement.cs
index d6bcbbb12b..48d35f4f0f 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectExtensionsElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectExtensionsElement.cs
@@ -71,6 +71,7 @@ namespace Microsoft.Build.Construction
{
while (reader.Read () && reader.NodeType != XmlNodeType.Element)
;
+ FillLocation (reader);
using (XmlReader subReader = reader.ReadSubtree ()) {
document = new XmlDocument ();
document.Load (subReader);
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportElement.cs
index d824cc7ee3..8895348390 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportElement.cs
@@ -28,6 +28,9 @@
using System;
using System.Xml;
+using Microsoft.Build.Exceptions;
+
+
namespace Microsoft.Build.Construction
{
[System.Diagnostics.DebuggerDisplayAttribute ("Project={Project} Condition={Condition}")]
@@ -56,6 +59,13 @@ namespace Microsoft.Build.Construction
SaveAttribute (writer, "Project", Project);
base.SaveValue (writer);
}
+
+ internal override void LoadValue (XmlReader reader)
+ {
+ if (string.IsNullOrWhiteSpace (Project))
+ throw new InvalidProjectFileException (Location, null, "Project attribute is null or empty on an Import element");
+ base.LoadValue (reader);
+ }
internal override void LoadAttribute (string name, string value)
{
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportGroupElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportGroupElement.cs
index 9223b5dc4b..bd346f772a 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportGroupElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectImportGroupElement.cs
@@ -29,6 +29,7 @@
using System;
using System.Collections.Generic;
using Microsoft.Build.Internal;
+using System.Xml;
namespace Microsoft.Build.Construction
{
@@ -50,7 +51,7 @@ namespace Microsoft.Build.Construction
return import;
}
internal override string XmlName { get { return "ImportGroup"; } }
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
return AddImport (null);
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionElement.cs
index 283e8b700c..497f631a07 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionElement.cs
@@ -29,6 +29,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Internal;
namespace Microsoft.Build.Construction
@@ -56,9 +57,9 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return ItemType; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- return AddMetadata (name, null);
+ return AddMetadata (reader.LocalName, null);
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionGroupElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionGroupElement.cs
index 43570e828e..1d74932b2c 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionGroupElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemDefinitionGroupElement.cs
@@ -29,6 +29,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Internal;
namespace Microsoft.Build.Construction
@@ -53,9 +54,9 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return "ItemDefinitionGroup"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- return AddItemDefinition (name);
+ return AddItemDefinition (reader.LocalName);
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemElement.cs
index 02a1a6ec49..5b13e20240 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemElement.cs
@@ -31,6 +31,7 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.Build.Internal;
using System.Xml;
+using Microsoft.Build.Exceptions;
namespace Microsoft.Build.Construction
{
@@ -61,6 +62,14 @@ namespace Microsoft.Build.Construction
}
string @remove;
public string Remove { get { return @remove ?? String.Empty; } set { @remove = value; } }
+ #if NET_4_5
+ string keepDuplicates;
+ public string KeepDuplicates { get { return keepDuplicates ?? String.Empty; } set { keepDuplicates = value; } }
+ string keepMetadata;
+ public string KeepMetadata { get { return keepMetadata ?? String.Empty; } set { keepMetadata = value; } }
+ string removeMetadata;
+ public string RemoveMetadata { get { return removeMetadata ?? String.Empty; } set { removeMetadata = value; } }
+ #endif
public ProjectMetadataElement AddMetadata (string name, string unevaluatedValue)
{
var metadata = ContainingProject.CreateMetadataElement (name, unevaluatedValue);
@@ -74,9 +83,15 @@ namespace Microsoft.Build.Construction
{
SaveAttribute (writer, "Include", Include);
SaveAttribute (writer, "Exclude", Exclude);
+#if NET_4_5
+ SaveAttribute (writer, "KeepDuplicates", KeepDuplicates);
+ SaveAttribute (writer, "KeepMetadata", KeepMetadata);
+ SaveAttribute (writer, "RemoveMetadata", RemoveMetadata);
+#endif
SaveAttribute (writer, "Remove", Remove);
base.SaveValue (writer);
}
+
internal override void LoadAttribute (string name, string value)
{
switch (name) {
@@ -86,6 +101,17 @@ namespace Microsoft.Build.Construction
case "Exclude":
Exclude = value;
break;
+#if NET_4_5
+ case "KeepDuplicates":
+ KeepDuplicates = value;
+ break;
+ case "KeepMetadata":
+ KeepMetadata = value;
+ break;
+ case "RemoveMetadata":
+ RemoveMetadata = value;
+ break;
+#endif
case "Remove":
Remove = value;
break;
@@ -94,11 +120,30 @@ namespace Microsoft.Build.Construction
break;
}
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override void LoadValue (XmlReader reader)
+ {
+ if (string.IsNullOrWhiteSpace (Include) && string.IsNullOrEmpty (Remove))
+ throw new InvalidProjectFileException (Location, null, string.Format ("Both Include and Remove attribute are null or empty on '{0}' item", ItemType));
+ base.LoadValue (reader);
+ }
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- var metadata = ContainingProject.CreateMetadataElement (name);
+ var metadata = ContainingProject.CreateMetadataElement (reader.LocalName);
AppendChild (metadata);
return metadata;
}
+#if NET_4_5
+ public ElementLocation ExcludeLocation { get; private set; }
+ public ElementLocation IncludeLocation { get; private set; }
+ public ElementLocation KeepDuplicatesLocation { get; private set; }
+ public ElementLocation RemoveLocation { get; private set; }
+ public ElementLocation RemoveMetadataLocation { get; private set; }
+#else
+ ElementLocation ExcludeLocation { get; set; }
+ ElementLocation IncludeLocation { get; set; }
+ ElementLocation KeepDuplicatesLocation { get; set; }
+ ElementLocation RemoveLocation { get; set; }
+ ElementLocation RemoveMetadataLocation { get; set; }
+#endif
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemGroupElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemGroupElement.cs
index 5cece6419c..f0bce4361b 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemGroupElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectItemGroupElement.cs
@@ -26,10 +26,11 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Internal;
-using System;
namespace Microsoft.Build.Construction
{
@@ -84,9 +85,9 @@ namespace Microsoft.Build.Construction
get { return "ItemGroup"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- var item = ContainingProject.CreateItemElement (name);
+ var item = ContainingProject.CreateItemElement (reader.LocalName);
AppendChild (item);
return item;
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOnErrorElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOnErrorElement.cs
index 5d6b75a59f..1590189e75 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOnErrorElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOnErrorElement.cs
@@ -61,5 +61,10 @@ namespace Microsoft.Build.Construction
break;
}
}
+
+ #if NET_4_5
+ public
+ #endif
+ ElementLocation ExecuteTargetsAttributeLocation { get; set; }
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOtherwiseElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOtherwiseElement.cs
index 9c25e50658..b8a2d6bec0 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOtherwiseElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOtherwiseElement.cs
@@ -29,6 +29,7 @@
using System.Collections.Generic;
using System;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Internal;
@@ -62,9 +63,9 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return "Otherwise"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- switch (name) {
+ switch (reader.LocalName) {
case "PropertyGroup":
var property = ContainingProject.CreatePropertyGroupElement ();
AppendChild (property);
@@ -79,7 +80,7 @@ namespace Microsoft.Build.Construction
return when;
default:
throw new InvalidProjectFileException (string.Format (
- "Child \"{0}\" is not a known node type.", name));
+ "Child \"{0}\" is not a known node type.", reader.LocalName));
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOutputElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOutputElement.cs
index bb5693758d..98d9cb849b 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOutputElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectOutputElement.cs
@@ -57,6 +57,13 @@ namespace Microsoft.Build.Construction
get { return taskParameter ?? String.Empty; }
set { taskParameter = value; }
}
+ #if NET_4_5
+ ElementLocation taskParameterLocation;
+ public ElementLocation TaskParameterLocation {
+ get { return taskParameterLocation; }
+ set { taskParameterLocation = value; }
+ }
+ #endif
internal override string XmlName {
get { return "Output"; }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectPropertyGroupElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectPropertyGroupElement.cs
index 0050ff5fee..4cee2f9d98 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectPropertyGroupElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectPropertyGroupElement.cs
@@ -76,9 +76,15 @@ namespace Microsoft.Build.Construction
get { return "PropertyGroup"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- return AddProperty (name, null);
+ switch (reader.LocalName) {
+ case "ItemGroup":
+ case "PropertyGroup":
+ throw CreateError (reader, string.Format ("{0} is a reserved name that cannot be used for a property.", reader.LocalName));
+ // others need to be checked too, but things like "Project" are somehow allowed...
+ }
+ return AddProperty (reader.LocalName, null);
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectRootElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectRootElement.cs
index ddbed1f42c..d7d5d77249 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectRootElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectRootElement.cs
@@ -63,8 +63,8 @@ namespace Microsoft.Build.Construction
string directoryPath;
public string DirectoryPath {
- get { return directoryPath ?? String.Empty; }
- internal set { directoryPath = value; }
+ get { return directoryPath ?? Directory.GetCurrentDirectory (); }
+ set { directoryPath = value; }
}
public ICollection<ProjectPropertyElement> Properties {
@@ -170,7 +170,7 @@ namespace Microsoft.Build.Construction
string toolsVersion;
public string ToolsVersion {
- get { return toolsVersion ?? "4.0"; }
+ get { return toolsVersion ?? string.Empty; }
set { toolsVersion = value; }
}
@@ -185,6 +185,7 @@ namespace Microsoft.Build.Construction
ProjectRootElement (ProjectCollection projectCollection)
{
+ ToolsVersion = "4.0";
}
public static ProjectRootElement Create ()
@@ -216,8 +217,9 @@ namespace Microsoft.Build.Construction
public static ProjectRootElement Create (XmlReader xmlReader, ProjectCollection projectCollection)
{
- // yes, this should create en empty project
var result = Create (projectCollection);
+ result.ToolsVersion = null;
+ result.Load (xmlReader);
return result;
}
@@ -467,6 +469,8 @@ namespace Microsoft.Build.Construction
public void Save ()
{
+ if (FullPath == null)
+ throw new InvalidOperationException ("This project was not given the file path to write to.");
Save (Encoding);
}
@@ -518,9 +522,9 @@ namespace Microsoft.Build.Construction
}
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- switch (name) {
+ switch (reader.LocalName) {
case "PropertyGroup":
var prop = CreatePropertyGroupElement ();
AppendChild (prop);
@@ -538,7 +542,8 @@ namespace Microsoft.Build.Construction
AppendChild (def);
return def;
case "UsingTask":
- return AddUsingTask (null, null, null);
+ var ut = AddUsingTask (null, null, null);
+ return ut;
case "Choose":
var choose = CreateChooseElement ();
AppendChild (choose);
@@ -548,8 +553,7 @@ namespace Microsoft.Build.Construction
AppendChild (ext);
return ext;
default:
- throw new InvalidProjectFileException (string.Format (
- "Child \"{0}\" is not a known node type.", name));
+ throw CreateError (reader, string.Format ("Child \"{0}\" is not a known node type.", reader.LocalName), -1);
}
}
@@ -573,7 +577,7 @@ namespace Microsoft.Build.Construction
internal override void Save (XmlWriter writer)
{
- writer.WriteStartElement (XmlName, "http://schemas.microsoft.com/developer/msbuild/2003");
+ writer.WriteStartElement (XmlName, MSBuildNamespace);
SaveValue (writer);
writer.WriteEndElement ();
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTargetElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTargetElement.cs
index 66129eb7da..cfaaa60c18 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTargetElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTargetElement.cs
@@ -29,6 +29,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Internal;
namespace Microsoft.Build.Construction
@@ -111,9 +112,30 @@ namespace Microsoft.Build.Construction
get { return "Target"; }
}
- internal override ProjectElement LoadChildElement (string name)
+#if NET_4_5
+ public ElementLocation AfterTargetsLocation { get; private set; }
+ public ElementLocation BeforeTargetsLocation { get; private set; }
+ public ElementLocation DependsOnTargetsLocation { get; private set; }
+ public ElementLocation InputsLocation { get; private set; }
+ public ElementLocation KeepDuplicateOutputsLocation { get; private set; }
+ public ElementLocation NameLocation { get; private set; }
+ public ElementLocation OutputsLocation { get; private set; }
+ public ElementLocation ReturnsLocation { get; private set; }
+#else
+ internal ElementLocation AfterTargetsLocation { get; set; }
+ internal ElementLocation BeforeTargetsLocation { get; set; }
+ internal ElementLocation DependsOnTargetsLocation { get; set; }
+ internal ElementLocation InputsLocation { get; set; }
+ internal ElementLocation KeepDuplicateOutputsLocation { get; set; }
+ internal ElementLocation LabelLocation { get; set; }
+ internal ElementLocation NameLocation { get; set; }
+ internal ElementLocation OutputsLocation { get; set; }
+ internal ElementLocation ReturnsLocation { get; set; }
+#endif
+
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- switch (name) {
+ switch (reader.LocalName) {
case "OnError":
var error = new ProjectOnErrorElement (ContainingProject);
AppendChild (error);
@@ -123,9 +145,10 @@ namespace Microsoft.Build.Construction
case "ItemGroup":
return AddItemGroup ();
default:
- return AddTask (name);
+ return AddTask (reader.LocalName);
}
}
+ // This seriously needs to change to become able to fill ElementLocation...
internal override void LoadAttribute (string name, string value)
{
switch (name) {
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTaskElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTaskElement.cs
index 00e0654526..fffe68eb1f 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTaskElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectTaskElement.cs
@@ -109,16 +109,16 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return Name; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- switch (name) {
+ switch (reader.LocalName) {
case "Output":
var output = ContainingProject.CreateOutputElement (null, null, null);
AppendChild (output);
return output;
default:
throw new InvalidProjectFileException (string.Format (
- "Child \"{0}\" is not a known node type.", name));
+ "Child \"{0}\" is not a known node type.", reader.LocalName));
}
}
internal override void LoadAttribute (string name, string value)
@@ -127,6 +127,17 @@ namespace Microsoft.Build.Construction
case "ContinueOnError":
ContinueOnError = value;
break;
+#if NET_4_5
+ case "ExecuteTargets":
+ ExecuteTargets = value;
+ break;
+ case "MSBuildArchitecture":
+ MSBuildArchitecture = value;
+ break;
+ case "MSBuildRuntime":
+ MSBuildRuntime = value;
+ break;
+#endif
case "xmlns":
break;
case "Label":
@@ -149,5 +160,15 @@ namespace Microsoft.Build.Construction
base.SaveValue (writer);
}
private Dictionary<string, string> parameters = new Dictionary<string, string> ();
+
+ public string ExecuteTargets { get; set; }
+ #if NET_4_5
+ public ElementLocation ExecuteTargetsLocation { get; set; }
+ public ElementLocation ContinueOnErrorLocation { get; set; }
+ public string MSBuildArchitecture { get; set; }
+ public ElementLocation MSBuildArchitectureLocation { get; set; }
+ public string MSBuildRuntime { get; set; }
+ public ElementLocation MSBuildRuntimeLocation { get; set; }
+ #endif
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskBodyElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskBodyElement.cs
index eecc92aa27..36c889dad5 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskBodyElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskBodyElement.cs
@@ -68,7 +68,6 @@ namespace Microsoft.Build.Construction
}
internal override void LoadValue (XmlReader reader)
{
- reader.MoveToElement ();
TaskBody = reader.ReadInnerXml ();
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskElement.cs
index 83a5d60749..52a1dad01b 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectUsingTaskElement.cs
@@ -27,6 +27,7 @@
//
using System;
+using System.Xml;
using Microsoft.Build.Exceptions;
namespace Microsoft.Build.Construction
@@ -107,16 +108,16 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return "UsingTask"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- switch (name) {
+ switch (reader.LocalName) {
case "ParameterGroup":
return AddParameterGroup ();
case "Task":
return AddUsingTaskBody (null, null);
default:
throw new InvalidProjectFileException (string.Format (
- "Child \"{0}\" is not a known node type.", name));
+ "Child \"{0}\" is not a known node type.", reader.LocalName));
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectWhenElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectWhenElement.cs
index 68040f52c2..0d7331afcb 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectWhenElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/ProjectWhenElement.cs
@@ -29,6 +29,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Internal;
@@ -57,9 +58,9 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return "When"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- switch (name) {
+ switch (reader.LocalName) {
case "PropertyGroup":
var property = ContainingProject.CreatePropertyGroupElement ();
AppendChild (property);
@@ -74,7 +75,7 @@ namespace Microsoft.Build.Construction
return when;
default:
throw new InvalidProjectFileException (string.Format (
- "Child \"{0}\" is not a known node type.", name));
+ "Child \"{0}\" is not a known node type.", reader.LocalName));
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/UsingTaskParameterGroupElement.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/UsingTaskParameterGroupElement.cs
index 8dd13943aa..ee808a170b 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Construction/UsingTaskParameterGroupElement.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Construction/UsingTaskParameterGroupElement.cs
@@ -29,6 +29,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Xml;
using Microsoft.Build.Internal;
namespace Microsoft.Build.Construction
@@ -61,9 +62,9 @@ namespace Microsoft.Build.Construction
internal override string XmlName {
get { return "ParameterGroup"; }
}
- internal override ProjectElement LoadChildElement (string name)
+ internal override ProjectElement LoadChildElement (XmlReader reader)
{
- return AddParameter (name);
+ return AddParameter (reader.LocalName);
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Project.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Project.cs
index c87ae5c3df..d384456d52 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Project.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Project.cs
@@ -4,9 +4,10 @@
// Author:
// Leszek Ciesielski (skolima@gmail.com)
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
// (C) 2011 Leszek Ciesielski
-// Copyright (C) 2011 Xamarin Inc. (http://www.xamarin.com)
+// Copyright (C) 2011,2013 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -36,383 +37,674 @@ using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.Build.Construction;
-using Microsoft.Build.Internal;
+using Microsoft.Build.Exceptions;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
+using Microsoft.Build.Internal;
+using Microsoft.Build.Internal.Expressions;
using Microsoft.Build.Logging;
+using System.Collections;
+
+// Basically there are two semantic Project object models and their relationship is not obvious
+// (apart from Microsoft.Build.Construction.ProjectRootElement which is a "construction rule").
+//
+// Microsoft.Build.Evaluation.Project holds some "editable" project model, and it supports
+// detailed loader API (such as Items and AllEvaluatedItems).
+// ProjectPoperty holds UnevaluatedValue and gives EvaluatedValue too.
+//
+// Microsoft.Build.Execution.ProjectInstance holds "snapshot" of a project, and it lacks
+// detailed loader API. It does not give us Unevaluated property value.
+// On the other hand, it supports Targets object model. What Microsoft.Build.Evaluation.Project
+// offers there is actually a list of Microsoft.Build.Execution.ProjectInstance objects.
+// It should be also noted that only ProjectInstance has Evaluate() method (Project doesn't).
+//
+// And both API holds different set of descendant types for each and cannot really share the
+// loader code. That is lame.
+//
+// So, can either of them be used to construct the other model? Both API models share the same
+// "governor", which is Microsoft.Build.Evaluation.ProjectCollection/ Project is added to
+// its LoadedProjects list, while ProjectInstance isn't. Project cannot be loaded to load
+// a ProjectInstance, at least within the same ProjectCollection.
+//
+// On the other hand, can ProjectInstance be used to load a Project? Maybe. Since Project and
+// its descendants need Microsoft.Build.Construction.ProjectElement family as its API model
+// is part of the public API. Then I still have to understand how those AllEvaluatedItems/
+// AllEvaluatedProperties members make sense. EvaluationCounter is another propery in question.
namespace Microsoft.Build.Evaluation
{
- [DebuggerDisplay("{FullPath} EffectiveToolsVersion={ToolsVersion} #GlobalProperties="
- +"{data.globalProperties.Count} #Properties={data.Properties.Count} #ItemTypes="
- +"{data.ItemTypes.Count} #ItemDefinitions={data.ItemDefinitions.Count} #Items="
- +"{data.Items.Count} #Targets={data.Targets.Count}")]
- public class Project
- {
+ [DebuggerDisplay ("{FullPath} EffectiveToolsVersion={ToolsVersion} #GlobalProperties="
+ + "{data.globalProperties.Count} #Properties={data.Properties.Count} #ItemTypes="
+ + "{data.ItemTypes.Count} #ItemDefinitions={data.ItemDefinitions.Count} #Items="
+ + "{data.Items.Count} #Targets={data.Targets.Count}")]
+ public class Project
+ {
public Project (XmlReader xml)
: this (ProjectRootElement.Create (xml))
{
}
- public Project (XmlReader xml, IDictionary<string, string> globalProperties,
- string toolsVersion)
+
+ public Project (XmlReader xml, IDictionary<string, string> globalProperties,
+ string toolsVersion)
: this (ProjectRootElement.Create (xml), globalProperties, toolsVersion)
{
}
- public Project (XmlReader xml, IDictionary<string, string> globalProperties,
- string toolsVersion, ProjectCollection projectCollection)
+
+ public Project (XmlReader xml, IDictionary<string, string> globalProperties,
+ string toolsVersion, ProjectCollection projectCollection)
: this (ProjectRootElement.Create (xml), globalProperties, toolsVersion, projectCollection)
{
}
- public Project (XmlReader xml, IDictionary<string, string> globalProperties,
- string toolsVersion, ProjectCollection projectCollection,
- ProjectLoadSettings loadSettings)
+
+ public Project (XmlReader xml, IDictionary<string, string> globalProperties,
+ string toolsVersion, ProjectCollection projectCollection,
+ ProjectLoadSettings loadSettings)
: this (ProjectRootElement.Create (xml), globalProperties, toolsVersion, projectCollection, loadSettings)
{
}
- public Project (ProjectRootElement xml) : this(xml, null, null)
- {
- }
- public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
- string toolsVersion)
- : this(xml, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection)
- {
- }
- public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
- string toolsVersion, ProjectCollection projectCollection)
- : this(xml, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
- {
- }
-
- public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
- string toolsVersion, ProjectCollection projectCollection,
- ProjectLoadSettings loadSettings)
- {
- ProjectCollection = projectCollection;
- Xml = xml;
- GlobalProperties = globalProperties;
- ToolsVersion = toolsVersion;
- }
-
- public Project (string projectFile) : this(projectFile, null, null)
- {
- }
-
- public Project (string projectFile, IDictionary<string, string> globalProperties,
- string toolsVersion)
- : this(projectFile, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection, ProjectLoadSettings.Default)
- {
- }
-
- public Project (string projectFile, IDictionary<string, string> globalProperties,
- string toolsVersion, ProjectCollection projectCollection)
- : this(projectFile, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
- {
- }
-
- public Project (string projectFile, IDictionary<string, string> globalProperties,
- string toolsVersion, ProjectCollection projectCollection,
- ProjectLoadSettings loadSettings)
- {
- throw new NotImplementedException ();
- }
-
- public IDictionary<string, string> GlobalProperties { get; private set; }
- public ProjectCollection ProjectCollection { get; private set; }
- public string ToolsVersion { get; private set; }
- public ProjectRootElement Xml { get; private set; }
-
- public ICollection<ProjectItem> GetItemsIgnoringCondition (string itemType)
- {
- return new CollectionFromEnumerable<ProjectItem> (
- new FilteredEnumerable<ProjectItemElement> (Xml.Items).
- Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)).
- Select (p => new ProjectItem(p)));
- }
- public void RemoveItems (IEnumerable<ProjectItem> items)
- {
- var removal = new List<ProjectItem> (items);
- foreach (var item in removal) {
- var parent = item.Xml.Parent;
- parent.RemoveChild (item.Xml);
- if (parent.Count == 0)
- parent.Parent.RemoveChild (parent);
- }
- }
-
- public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude)
- {
- throw new NotImplementedException ();
- }
-
- public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude,
- IEnumerable<KeyValuePair<string, string>> metadata)
- {
- throw new NotImplementedException ();
- }
-
- public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude)
- {
- throw new NotImplementedException ();
- }
-
- public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude,
- IEnumerable<KeyValuePair<string, string>> metadata)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build ()
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (IEnumerable<ILogger> loggers)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (string target)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (string[] targets)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (ILogger logger)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (string[] targets, IEnumerable<ILogger> loggers)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (string target, IEnumerable<ILogger> loggers)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (string[] targets, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
- {
- throw new NotImplementedException ();
- }
-
- public bool Build (string target, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
- {
- throw new NotImplementedException ();
- }
-
- public ProjectInstance CreateProjectInstance ()
- {
- throw new NotImplementedException ();
- }
-
- public string ExpandString (string unexpandedValue)
- {
- throw new NotImplementedException ();
- }
-
- public static string GetEvaluatedItemIncludeEscaped (ProjectItem item)
- {
- throw new NotImplementedException ();
- }
-
- public static string GetEvaluatedItemIncludeEscaped (ProjectItemDefinition item)
- {
- throw new NotImplementedException ();
- }
-
- public ICollection<ProjectItem> GetItems (string itemType)
- {
- throw new NotImplementedException ();
- }
-
- public ICollection<ProjectItem> GetItemsByEvaluatedInclude (string evaluatedInclude)
- {
- throw new NotImplementedException ();
- }
-
- public IEnumerable<ProjectElement> GetLogicalProject ()
- {
- throw new NotImplementedException ();
- }
-
- public static string GetMetadataValueEscaped (ProjectMetadata metadatum)
- {
- throw new NotImplementedException ();
- }
-
- public static string GetMetadataValueEscaped (ProjectItem item, string name)
- {
- throw new NotImplementedException ();
- }
-
- public static string GetMetadataValueEscaped (ProjectItemDefinition item, string name)
- {
- throw new NotImplementedException ();
- }
-
- public string GetPropertyValue (string name)
- {
- throw new NotImplementedException ();
- }
-
- public static string GetPropertyValueEscaped (ProjectProperty property)
- {
- throw new NotImplementedException ();
- }
-
- public ProjectProperty GetProperty (string name)
- {
- throw new NotImplementedException ();
- }
-
- public void MarkDirty ()
- {
- throw new NotImplementedException ();
- }
-
- public void ReevaluateIfNecessary ()
- {
- throw new NotImplementedException ();
- }
-
- public bool RemoveGlobalProperty (string name)
- {
- throw new NotImplementedException ();
- }
-
- public bool RemoveItem (ProjectItem item)
- {
- throw new NotImplementedException ();
- }
-
- public bool RemoveProperty (ProjectProperty property)
- {
- throw new NotImplementedException ();
- }
-
- public void Save ()
- {
- throw new NotImplementedException ();
- }
-
- public void Save (TextWriter writer)
- {
- throw new NotImplementedException ();
- }
-
- public void Save (string path)
- {
- throw new NotImplementedException ();
- }
-
- public void Save (Encoding encoding)
- {
- throw new NotImplementedException ();
- }
-
- public void Save (string path, Encoding encoding)
- {
- throw new NotImplementedException ();
- }
-
- public void SaveLogicalProject (TextWriter writer)
- {
- throw new NotImplementedException ();
- }
-
- public bool SetGlobalProperty (string name, string escapedValue)
- {
- throw new NotImplementedException ();
- }
-
- public ProjectProperty SetProperty (string name, string unevaluatedValue)
- {
- throw new NotImplementedException ();
- }
-
- public ICollection<ProjectMetadata> AllEvaluatedItemDefinitionMetadata {
- get { throw new NotImplementedException (); }
- }
-
- public ICollection<ProjectItem> AllEvaluatedItems {
- get { throw new NotImplementedException (); }
- }
-
- public ICollection<ProjectProperty> AllEvaluatedProperties {
- get { throw new NotImplementedException (); }
- }
-
- public IDictionary<string, List<string>> ConditionedProperties {
- get { throw new NotImplementedException (); }
- }
-
- public string DirectoryPath {
- get { throw new NotImplementedException (); }
- }
-
- public bool DisableMarkDirty { get; set; }
-
- public int EvaluationCounter {
- get { throw new NotImplementedException (); }
- }
-
- public string FullPath {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public IList<ResolvedImport> Imports {
- get { throw new NotImplementedException (); }
- }
-
- public IList<ResolvedImport> ImportsIncludingDuplicates {
- get { throw new NotImplementedException (); }
- }
-
- public bool IsBuildEnabled {
- get { throw new NotImplementedException (); }
- }
-
- public bool IsDirty {
- get { throw new NotImplementedException (); }
- }
-
- public IDictionary<string, ProjectItemDefinition> ItemDefinitions {
- get { throw new NotImplementedException (); }
- }
-
- public ICollection<ProjectItem> Items {
- get { throw new NotImplementedException (); }
- }
-
- public ICollection<ProjectItem> ItemsIgnoringCondition {
- get { throw new NotImplementedException (); }
- }
-
- public ICollection<string> ItemTypes {
- get { throw new NotImplementedException (); }
- }
-
- public ICollection<ProjectProperty> Properties {
- get { throw new NotImplementedException (); }
- }
-
- public bool SkipEvaluation { get; set; }
-
- public IDictionary<string, ProjectTargetInstance> Targets {
- get { throw new NotImplementedException (); }
- }
- }
+ public Project (ProjectRootElement xml) : this (xml, null, null)
+ {
+ }
+
+ public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
+ string toolsVersion)
+ : this (xml, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection)
+ {
+ }
+
+ public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
+ string toolsVersion, ProjectCollection projectCollection)
+ : this (xml, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
+ {
+ }
+
+ public Project (ProjectRootElement xml, IDictionary<string, string> globalProperties,
+ string toolsVersion, ProjectCollection projectCollection,
+ ProjectLoadSettings loadSettings)
+ {
+ if (projectCollection == null)
+ throw new ArgumentNullException ("projectCollection");
+ this.Xml = xml;
+ this.GlobalProperties = globalProperties ?? new Dictionary<string, string> ();
+ this.ToolsVersion = toolsVersion;
+ this.ProjectCollection = projectCollection;
+ this.load_settings = loadSettings;
+
+ Initialize (null);
+ }
+
+ Project (ProjectRootElement imported, Project parent)
+ {
+ this.Xml = imported;
+ this.GlobalProperties = parent.GlobalProperties;
+ this.ToolsVersion = parent.ToolsVersion;
+ this.ProjectCollection = parent.ProjectCollection;
+ this.load_settings = parent.load_settings;
+
+ Initialize (parent);
+ }
+
+ public Project (string projectFile)
+ : this (projectFile, null, null)
+ {
+ }
+
+ public Project (string projectFile, IDictionary<string, string> globalProperties,
+ string toolsVersion)
+ : this (projectFile, globalProperties, toolsVersion, ProjectCollection.GlobalProjectCollection, ProjectLoadSettings.Default)
+ {
+ }
+
+ public Project (string projectFile, IDictionary<string, string> globalProperties,
+ string toolsVersion, ProjectCollection projectCollection)
+ : this (projectFile, globalProperties, toolsVersion, projectCollection, ProjectLoadSettings.Default)
+ {
+ }
+
+ public Project (string projectFile, IDictionary<string, string> globalProperties,
+ string toolsVersion, ProjectCollection projectCollection,
+ ProjectLoadSettings loadSettings)
+ : this (ProjectRootElement.Create (projectFile), globalProperties, toolsVersion, projectCollection, loadSettings)
+ {
+ }
+
+ ProjectLoadSettings load_settings;
+
+ public IDictionary<string, string> GlobalProperties { get; private set; }
+
+ public ProjectCollection ProjectCollection { get; private set; }
+
+ public string ToolsVersion { get; private set; }
+
+ public ProjectRootElement Xml { get; private set; }
+
+ string dir_path;
+ Dictionary<string, ProjectItemDefinition> item_definitions;
+ List<ResolvedImport> raw_imports;
+ List<ProjectItem> raw_items;
+ List<ProjectItem> all_evaluated_items;
+ List<ProjectProperty> properties;
+ Dictionary<string, ProjectTargetInstance> targets;
+
+ void Initialize (Project parent)
+ {
+ dir_path = Directory.GetCurrentDirectory ();
+ raw_imports = new List<ResolvedImport> ();
+ item_definitions = new Dictionary<string, ProjectItemDefinition> ();
+ targets = new Dictionary<string, ProjectTargetInstance> ();
+ raw_items = new List<ProjectItem> ();
+
+ // FIXME: this is likely hack. Test ImportedProject.Properties to see what exactly should happen.
+ if (parent != null) {
+ properties = parent.properties;
+ } else {
+ properties = new List<ProjectProperty> ();
+
+ foreach (DictionaryEntry p in Environment.GetEnvironmentVariables ())
+ // FIXME: this is kind of workaround for unavoidable issue that PLATFORM=* is actually given
+ // on some platforms and that prevents setting default "PLATFORM=AnyCPU" property.
+ if (!string.Equals ("PLATFORM", (string) p.Key, StringComparison.OrdinalIgnoreCase))
+ this.properties.Add (new EnvironmentProjectProperty (this, (string)p.Key, (string)p.Value));
+ foreach (var p in GlobalProperties)
+ this.properties.Add (new GlobalProjectProperty (this, p.Key, p.Value));
+ var tools = ProjectCollection.GetToolset (this.ToolsVersion) ?? ProjectCollection.GetToolset (this.ProjectCollection.DefaultToolsVersion);
+ foreach (var p in ProjectCollection.GetReservedProperties (tools, this))
+ this.properties.Add (p);
+ foreach (var p in ProjectCollection.GetWellKnownProperties (this))
+ this.properties.Add (p);
+ }
+
+ ProcessXml (parent);
+
+ ProjectCollection.AddProject (this);
+ }
+
+ void ProcessXml (Project parent)
+ {
+ // this needs to be initialized here (regardless of that items won't be evaluated at property evaluation;
+ // Conditions could incorrectly reference items and lack of this list causes NRE.
+ all_evaluated_items = new List<ProjectItem> ();
+
+ // property evaluation happens couple of times.
+ // At first step, all non-imported properties are evaluated TOO, WHILE those properties are being evaluated.
+ // This means, Include and IncludeGroup elements with Condition attribute MAY contain references to
+ // properties and they will be expanded.
+ var elements = EvaluatePropertiesAndImports (Xml.Children).ToArray (); // ToArray(): to not lazily evaluate elements.
+
+ // next, evaluate items
+ EvaluateItems (elements);
+
+ // finally, evaluate targets and tasks
+ EvaluateTargets (elements);
+ }
+
+ IEnumerable<ProjectElement> EvaluatePropertiesAndImports (IEnumerable<ProjectElement> elements)
+ {
+ // First step: evaluate Properties
+ foreach (var child in elements) {
+ yield return child;
+ var pge = child as ProjectPropertyGroupElement;
+ if (pge != null && Evaluate (pge.Condition))
+ foreach (var p in pge.Properties)
+ // do not allow overwriting reserved or well-known properties by user
+ if (!this.properties.Any (_ => (_.IsReservedProperty || _.IsWellKnownProperty) && _.Name.Equals (p.Name, StringComparison.InvariantCultureIgnoreCase)))
+ if (Evaluate (p.Condition))
+ this.properties.Add (new XmlProjectProperty (this, p, PropertyType.Normal, ProjectCollection.OngoingImports.Any ()));
+
+ var ige = child as ProjectImportGroupElement;
+ if (ige != null && Evaluate (ige.Condition)) {
+ foreach (var incc in ige.Imports) {
+ foreach (var e in Import (incc))
+ yield return e;
+ }
+ }
+ var inc = child as ProjectImportElement;
+ if (inc != null && Evaluate (inc.Condition))
+ foreach (var e in Import (inc))
+ yield return e;
+ }
+ }
+
+ internal IEnumerable<T> GetAllItems<T> (string include, string exclude, Func<string,T> creator, Func<string,ITaskItem> taskItemCreator, Func<string,bool> itemTypeCheck, Action<T,string> assignRecurse)
+ {
+ return ProjectCollection.GetAllItems<T> (ExpandString, include, exclude, creator, taskItemCreator, DirectoryPath, assignRecurse,
+ t => all_evaluated_items.Any (i => i.EvaluatedInclude == t.ItemSpec && itemTypeCheck (i.ItemType)));
+ }
+
+ void EvaluateItems (IEnumerable<ProjectElement> elements)
+ {
+ foreach (var child in elements) {
+ var ige = child as ProjectItemGroupElement;
+ if (ige != null) {
+ foreach (var p in ige.Items) {
+ if (!Evaluate (ige.Condition) || !Evaluate (p.Condition))
+ continue;
+ Func<string,ProjectItem> creator = s => new ProjectItem (this, p, s);
+ foreach (var item in GetAllItems<ProjectItem> (p.Include, p.Exclude, creator, s => new ProjectTaskItem (p, s), it => string.Equals (it, p.ItemType, StringComparison.OrdinalIgnoreCase), (t, s) => t.RecursiveDir = s)) {
+ raw_items.Add (item);
+ all_evaluated_items.Add (item);
+ }
+ }
+ }
+ var def = child as ProjectItemDefinitionGroupElement;
+ if (def != null) {
+ foreach (var p in def.ItemDefinitions) {
+ if (Evaluate (p.Condition)) {
+ ProjectItemDefinition existing;
+ if (!item_definitions.TryGetValue (p.ItemType, out existing))
+ item_definitions.Add (p.ItemType, (existing = new ProjectItemDefinition (this, p.ItemType)));
+ existing.AddItems (p);
+ }
+ }
+ }
+ }
+ all_evaluated_items.Sort ((p1, p2) => string.Compare (p1.ItemType, p2.ItemType, StringComparison.OrdinalIgnoreCase));
+ }
+
+ void EvaluateTargets (IEnumerable<ProjectElement> elements)
+ {
+ foreach (var child in elements) {
+ var te = child as ProjectTargetElement;
+ if (te != null)
+ this.targets.Add (te.Name, new ProjectTargetInstance (te));
+ }
+ }
+
+ IEnumerable<ProjectElement> Import (ProjectImportElement import)
+ {
+ string dir = ProjectCollection.GetEvaluationTimeThisFileDirectory (() => FullPath);
+ string path = WindowsCompatibilityExtensions.NormalizeFilePath (ExpandString (import.Project));
+ path = Path.IsPathRooted (path) ? path : dir != null ? Path.Combine (dir, path) : Path.GetFullPath (path);
+ if (ProjectCollection.OngoingImports.Contains (path)) {
+ switch (load_settings) {
+ case ProjectLoadSettings.RejectCircularImports:
+ throw new InvalidProjectFileException (import.Location, null, string.Format ("Circular imports was detected: {0} (resolved as \"{1}\") is already on \"importing\" stack", import.Project, path));
+ }
+ return new ProjectElement [0]; // do not import circular references
+ }
+ ProjectCollection.OngoingImports.Push (path);
+ try {
+ using (var reader = XmlReader.Create (path)) {
+ var root = ProjectRootElement.Create (reader, ProjectCollection);
+ raw_imports.Add (new ResolvedImport (import, root, true));
+ return this.EvaluatePropertiesAndImports (root.Children).ToArray ();
+ }
+ } finally {
+ ProjectCollection.OngoingImports.Pop ();
+ }
+ }
+
+ public ICollection<ProjectItem> GetItemsIgnoringCondition (string itemType)
+ {
+ return new CollectionFromEnumerable<ProjectItem> (raw_items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
+ }
+
+ public void RemoveItems (IEnumerable<ProjectItem> items)
+ {
+ var removal = new List<ProjectItem> (items);
+ foreach (var item in removal) {
+ var parent = item.Xml.Parent;
+ parent.RemoveChild (item.Xml);
+ if (parent.Count == 0)
+ parent.Parent.RemoveChild (parent);
+ }
+ }
+
+ static readonly Dictionary<string, string> empty_metadata = new Dictionary<string, string> ();
+
+ public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude)
+ {
+ return AddItem (itemType, unevaluatedInclude, empty_metadata);
+ }
+
+ public IList<ProjectItem> AddItem (string itemType, string unevaluatedInclude,
+ IEnumerable<KeyValuePair<string, string>> metadata)
+ {
+ // FIXME: needs several check that AddItemFast() does not process (see MSDN for details).
+
+ return AddItemFast (itemType, unevaluatedInclude, metadata);
+ }
+
+ public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude)
+ {
+ return AddItemFast (itemType, unevaluatedInclude, empty_metadata);
+ }
+
+ public IList<ProjectItem> AddItemFast (string itemType, string unevaluatedInclude,
+ IEnumerable<KeyValuePair<string, string>> metadata)
+ {
+ throw new NotImplementedException ();
+ }
+
+ static readonly char [] target_sep = new char[] {';'};
+
+ public bool Build ()
+ {
+ return Build (Xml.DefaultTargets.Split (target_sep, StringSplitOptions.RemoveEmptyEntries));
+ }
+
+ public bool Build (IEnumerable<ILogger> loggers)
+ {
+ return Build (Xml.DefaultTargets.Split (target_sep, StringSplitOptions.RemoveEmptyEntries), loggers);
+ }
+
+ public bool Build (string target)
+ {
+ return string.IsNullOrWhiteSpace (target) ? Build () : Build (new string [] {target});
+ }
+
+ public bool Build (string[] targets)
+ {
+ return Build (targets, new ILogger [0]);
+ }
+
+ public bool Build (ILogger logger)
+ {
+ return Build (Xml.DefaultTargets.Split (target_sep, StringSplitOptions.RemoveEmptyEntries), new ILogger [] {logger});
+ }
+
+ public bool Build (string[] targets, IEnumerable<ILogger> loggers)
+ {
+ return Build (targets, loggers, new ForwardingLoggerRecord [0]);
+ }
+
+ public bool Build (IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
+ {
+ return Build (Xml.DefaultTargets.Split (target_sep, StringSplitOptions.RemoveEmptyEntries), loggers, remoteLoggers);
+ }
+
+ public bool Build (string target, IEnumerable<ILogger> loggers)
+ {
+ return Build (new string [] { target }, loggers);
+ }
+
+ public bool Build (string[] targets, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
+ {
+ // Unlike ProjectInstance.Build(), there is no place to fill outputs by targets, so ignore them
+ // (i.e. we don't use the overload with output).
+ //
+ // This does not check FullPath, so don't call GetProjectInstanceForBuild() directly.
+ return new BuildManager ().GetProjectInstanceForBuildInternal (this).Build (targets, loggers, remoteLoggers);
+ }
+
+ public bool Build (string target, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
+ {
+ return Build (new string [] { target }, loggers, remoteLoggers);
+ }
+
+ public ProjectInstance CreateProjectInstance ()
+ {
+ var ret = new ProjectInstance (Xml, GlobalProperties, ToolsVersion, ProjectCollection);
+ // FIXME: maybe fill other properties to the result.
+ return ret;
+ }
+
+ bool Evaluate (string unexpandedValue)
+ {
+ return string.IsNullOrWhiteSpace (unexpandedValue) || new ExpressionEvaluator (this, null).EvaluateAsBoolean (unexpandedValue);
+ }
+
+ public string ExpandString (string unexpandedValue)
+ {
+ return ExpandString (unexpandedValue, null);
+ }
+
+ string ExpandString (string unexpandedValue, string replacementForMissingStuff)
+ {
+ return new ExpressionEvaluator (this, replacementForMissingStuff).Evaluate (unexpandedValue);
+ }
+
+ public static string GetEvaluatedItemIncludeEscaped (ProjectItem item)
+ {
+ return ProjectCollection.Escape (item.EvaluatedInclude);
+ }
+
+ public static string GetEvaluatedItemIncludeEscaped (ProjectItemDefinition item)
+ {
+ // ?? ItemDefinition does not have Include attribute. What's the point here?
+ throw new NotImplementedException ();
+ }
+
+ public ICollection<ProjectItem> GetItems (string itemType)
+ {
+ return new CollectionFromEnumerable<ProjectItem> (Items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
+ }
+
+ public ICollection<ProjectItem> GetItemsByEvaluatedInclude (string evaluatedInclude)
+ {
+ return new CollectionFromEnumerable<ProjectItem> (Items.Where (p => p.EvaluatedInclude.Equals (evaluatedInclude, StringComparison.OrdinalIgnoreCase)));
+ }
+
+ public IEnumerable<ProjectElement> GetLogicalProject ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public static string GetMetadataValueEscaped (ProjectMetadata metadatum)
+ {
+ return ProjectCollection.Escape (metadatum.EvaluatedValue);
+ }
+
+ public static string GetMetadataValueEscaped (ProjectItem item, string name)
+ {
+ var md = item.Metadata.FirstOrDefault (m => m.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
+ }
+
+ public static string GetMetadataValueEscaped (ProjectItemDefinition item, string name)
+ {
+ var md = item.Metadata.FirstOrDefault (m => m.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
+ }
+
+ public string GetPropertyValue (string name)
+ {
+ var prop = GetProperty (name);
+ return prop != null ? prop.EvaluatedValue : string.Empty;
+ }
+
+ public static string GetPropertyValueEscaped (ProjectProperty property)
+ {
+ // WTF happens here.
+ //return ProjectCollection.Escape (property.EvaluatedValue);
+ return property.EvaluatedValue;
+ }
+
+ public ProjectProperty GetProperty (string name)
+ {
+ return properties.FirstOrDefault (p => p.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ }
+
+ public void MarkDirty ()
+ {
+ if (!DisableMarkDirty)
+ is_dirty = true;
+ }
+
+ public void ReevaluateIfNecessary ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public bool RemoveGlobalProperty (string name)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public bool RemoveItem (ProjectItem item)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public bool RemoveProperty (ProjectProperty property)
+ {
+ var removed = properties.FirstOrDefault (p => p.Name.Equals (property.Name, StringComparison.OrdinalIgnoreCase));
+ if (removed == null)
+ return false;
+ properties.Remove (removed);
+ return true;
+ }
+
+ public void Save ()
+ {
+ Xml.Save ();
+ }
+
+ public void Save (TextWriter writer)
+ {
+ Xml.Save (writer);
+ }
+
+ public void Save (string path)
+ {
+ Save (path, Encoding.Default);
+ }
+
+ public void Save (Encoding encoding)
+ {
+ Save (FullPath, encoding);
+ }
+
+ public void Save (string path, Encoding encoding)
+ {
+ using (var writer = new StreamWriter (path, false, encoding))
+ Save (writer);
+ }
+
+ public void SaveLogicalProject (TextWriter writer)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public bool SetGlobalProperty (string name, string escapedValue)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public ProjectProperty SetProperty (string name, string unevaluatedValue)
+ {
+ var p = new ManuallyAddedProjectProperty (this, name, unevaluatedValue);
+ properties.Add (p);
+ return p;
+ }
+
+ public ICollection<ProjectMetadata> AllEvaluatedItemDefinitionMetadata {
+ get { throw new NotImplementedException (); }
+ }
+
+ public ICollection<ProjectItem> AllEvaluatedItems {
+ get { return all_evaluated_items; }
+ }
+
+ public ICollection<ProjectProperty> AllEvaluatedProperties {
+ get { return properties; }
+ }
+
+ public IDictionary<string, List<string>> ConditionedProperties {
+ get {
+ // this property returns different instances every time.
+ var dic = new Dictionary<string, List<string>> ();
+
+ // but I dunno HOW this evaluates
+
+ throw new NotImplementedException ();
+ }
+ }
+
+ public string DirectoryPath {
+ get { return dir_path; }
+ }
+
+ public bool DisableMarkDirty { get; set; }
+
+ public int EvaluationCounter {
+ get { throw new NotImplementedException (); }
+ }
+
+ public string FullPath {
+ get { return Xml.FullPath; }
+ set { Xml.FullPath = value; }
+ }
+
+ class ResolvedImportComparer : IEqualityComparer<ResolvedImport>
+ {
+ public static ResolvedImportComparer Instance = new ResolvedImportComparer ();
+
+ public bool Equals (ResolvedImport x, ResolvedImport y)
+ {
+ return x.ImportedProject.FullPath.Equals (y.ImportedProject.FullPath);
+ }
+ public int GetHashCode (ResolvedImport obj)
+ {
+ return obj.ImportedProject.FullPath.GetHashCode ();
+ }
+ }
+
+ public IList<ResolvedImport> Imports {
+ get { return raw_imports.Distinct (ResolvedImportComparer.Instance).ToList (); }
+ }
+
+ public IList<ResolvedImport> ImportsIncludingDuplicates {
+ get { return raw_imports; }
+ }
+
+ public bool IsBuildEnabled {
+ get { return ProjectCollection.IsBuildEnabled; }
+ }
+
+ bool is_dirty;
+ public bool IsDirty {
+ get { return is_dirty; }
+ }
+
+ public IDictionary<string, ProjectItemDefinition> ItemDefinitions {
+ get { return item_definitions; }
+ }
+
+ [MonoTODO ("should be different from AllEvaluatedItems")]
+ public ICollection<ProjectItem> Items {
+ get { return AllEvaluatedItems; }
+ }
+
+ public ICollection<ProjectItem> ItemsIgnoringCondition {
+ get { return raw_items; }
+ }
+
+ public ICollection<string> ItemTypes {
+ get { return new CollectionFromEnumerable<string> (raw_items.Select (i => i.ItemType).Distinct ()); }
+ }
+
+ [MonoTODO ("should be different from AllEvaluatedProperties")]
+ public ICollection<ProjectProperty> Properties {
+ get { return AllEvaluatedProperties; }
+ }
+
+ public bool SkipEvaluation { get; set; }
+
+ public IDictionary<string, ProjectTargetInstance> Targets {
+ get { return targets; }
+ }
+
+ // These are required for reserved property, represents dynamically changing property values.
+ // This should resolve to either the project file path or that of the imported file.
+ internal string GetEvaluationTimeThisFileDirectory ()
+ {
+ var file = GetEvaluationTimeThisFile ();
+ var dir = Path.IsPathRooted (file) ? Path.GetDirectoryName (file) : Directory.GetCurrentDirectory ();
+ return dir + Path.DirectorySeparatorChar;
+ }
+
+ internal string GetEvaluationTimeThisFile ()
+ {
+ return ProjectCollection.OngoingImports.Count > 0 ? ProjectCollection.OngoingImports.Peek () : FullPath ?? string.Empty;
+ }
+
+ internal string GetFullPath (string pathRelativeToProject)
+ {
+ if (Path.IsPathRooted (pathRelativeToProject))
+ return pathRelativeToProject;
+ return Path.GetFullPath (Path.Combine (DirectoryPath, pathRelativeToProject));
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ColorResetter.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectChangedEventArgs.cs
index 5bd3da3e7c..5372642ae0 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ColorResetter.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectChangedEventArgs.cs
@@ -1,9 +1,10 @@
-// ColorResetter.cs
+//
+// ProjectChangedEventArgs.cs
//
// Author:
-// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto <atsushi@xamarin.com>
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -15,7 +16,7 @@
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
+//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -25,8 +26,17 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-namespace Microsoft.Build.Logging
+using System;
+using System.Linq;
+
+namespace Microsoft.Build.Evaluation
{
- public delegate void ColorResetter ();
+ public class ProjectChangedEventArgs : EventArgs
+ {
+ internal ProjectChangedEventArgs (Project project)
+ {
+ Project = project;
+ }
+ public Project Project { get; private set; }
+ }
}
-
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollection.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollection.cs
index f5b2826961..ab3ee06638 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollection.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollection.cs
@@ -4,9 +4,10 @@
// Author:
// Leszek Ciesielski (skolima@gmail.com)
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
// (C) 2011 Leszek Ciesielski
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -29,97 +30,468 @@
//
using Microsoft.Build.Construction;
+using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
-
+using Microsoft.Build.Utilities;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Reflection;
+using System.Globalization;
namespace Microsoft.Build.Evaluation
{
- public class ProjectCollection : IDisposable
- {
- public ProjectCollection ()
- {
- }
-
- public ProjectCollection (IDictionary<string, string> globalProperties)
- : this (globalProperties, null, ToolsetDefinitionLocations.Registry | ToolsetDefinitionLocations.ConfigurationFile)
- {
- }
-
- public ProjectCollection (ToolsetDefinitionLocations toolsetDefinitionLocations)
- : this (null, null, toolsetDefinitionLocations)
- {
- }
-
- public ProjectCollection (IDictionary<string, string> globalProperties, IEnumerable<ILogger> loggers,
- ToolsetDefinitionLocations toolsetDefinitionLocations)
- : this (globalProperties, loggers, null, toolsetDefinitionLocations, 1, false)
- {
- }
-
- public ProjectCollection (IDictionary<string, string> globalProperties,
- IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers,
- ToolsetDefinitionLocations toolsetDefinitionLocations,
- int maxNodeCount, bool onlyLogCriticalEvents)
- {
- throw new NotImplementedException ();
- }
-
- public static string Escape (string unescapedString)
- {
- return unescapedString;
- }
-
- public static ProjectCollection GlobalProjectCollection {
- get { return globalProjectCollection; }
- }
-
- public void Dispose ()
- {
- Dispose (true);
- GC.SuppressFinalize (this);
- }
-
- protected virtual void Dispose (bool disposing)
- {
- if (disposing) {
- }
- }
-
- static ProjectCollection globalProjectCollection = new ProjectCollection ();
-
- public ICollection<Project> GetLoadedProjects (string fullPath)
- {
- throw new NotImplementedException ();
- }
-
- public ToolsetDefinitionLocations ToolsetLocations {
- get { throw new NotImplementedException (); }
- }
+ public class ProjectCollection : IDisposable
+ {
+ public delegate void ProjectAddedEventHandler (object target, ProjectAddedToProjectCollectionEventArgs args);
+
+ public class ProjectAddedToProjectCollectionEventArgs : EventArgs
+ {
+ public ProjectAddedToProjectCollectionEventArgs (ProjectRootElement project)
+ {
+ if (project == null)
+ throw new ArgumentNullException ("project");
+ ProjectRootElement = project;
+ }
+
+ public ProjectRootElement ProjectRootElement { get; private set; }
+ }
+
+ // static members
+
+ static readonly ProjectCollection global_project_collection;
+
+ static ProjectCollection ()
+ {
+ #if NET_4_5
+ global_project_collection = new ProjectCollection (new ReadOnlyDictionary<string, string> (new Dictionary<string, string> ()));
+ #else
+ global_project_collection = new ProjectCollection (new Dictionary<string, string> ());
+ #endif
+ }
+
+ public static string Escape (string unescapedString)
+ {
+ return Mono.XBuild.Utilities.MSBuildUtils.Escape (unescapedString);
+ }
+
+ public static string Unescape (string escapedString)
+ {
+ return Mono.XBuild.Utilities.MSBuildUtils.Unescape (escapedString);
+ }
+
+ public static ProjectCollection GlobalProjectCollection {
+ get { return global_project_collection; }
+ }
+
+ // semantic model part
+
+ public ProjectCollection ()
+ : this (null)
+ {
+ }
+
+ public ProjectCollection (IDictionary<string, string> globalProperties)
+ : this (globalProperties, null, ToolsetDefinitionLocations.Registry | ToolsetDefinitionLocations.ConfigurationFile)
+ {
+ }
+ public ProjectCollection (ToolsetDefinitionLocations toolsetDefinitionLocations)
+ : this (null, null, toolsetDefinitionLocations)
+ {
+ }
+
+ public ProjectCollection (IDictionary<string, string> globalProperties, IEnumerable<ILogger> loggers,
+ ToolsetDefinitionLocations toolsetDefinitionLocations)
+ : this (globalProperties, loggers, null, toolsetDefinitionLocations, 1, false)
+ {
+ }
+
+ public ProjectCollection (IDictionary<string, string> globalProperties,
+ IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers,
+ ToolsetDefinitionLocations toolsetDefinitionLocations,
+ int maxNodeCount, bool onlyLogCriticalEvents)
+ {
+ global_properties = globalProperties ?? new Dictionary<string, string> ();
+ this.loggers = loggers != null ? loggers.ToList () : new List<ILogger> ();
+ toolset_locations = toolsetDefinitionLocations;
+ MaxNodeCount = maxNodeCount;
+ OnlyLogCriticalEvents = onlyLogCriticalEvents;
+
+ LoadDefaultToolsets ();
+ }
+
+ [MonoTODO ("not fired yet")]
+ public event ProjectAddedEventHandler ProjectAdded;
+ [MonoTODO ("not fired yet")]
+ public event EventHandler<ProjectChangedEventArgs> ProjectChanged;
+ [MonoTODO ("not fired yet")]
+ public event EventHandler<ProjectCollectionChangedEventArgs> ProjectCollectionChanged;
+ [MonoTODO ("not fired yet")]
+ public event EventHandler<ProjectXmlChangedEventArgs> ProjectXmlChanged;
+
+ public void AddProject (Project project)
+ {
+ this.loaded_projects.Add (project);
+ if (ProjectAdded != null)
+ ProjectAdded (this, new ProjectAddedToProjectCollectionEventArgs (project.Xml));
+ }
+
+ public int Count {
+ get { return loaded_projects.Count; }
+ }
+
+ string default_tools_version;
+ public string DefaultToolsVersion {
+ get { return default_tools_version; }
+ set {
+ if (GetToolset (value) == null)
+ throw new InvalidOperationException (string.Format ("Toolset '{0}' does not exist", value));
+ default_tools_version = value;
+ }
+ }
+
+ public void Dispose ()
+ {
+ Dispose (true);
+ GC.SuppressFinalize (this);
+ }
+
+ protected virtual void Dispose (bool disposing)
+ {
+ if (disposing) {
+ }
+ }
+
+ public ICollection<Project> GetLoadedProjects (string fullPath)
+ {
+ return LoadedProjects.Where (p => p.FullPath != null && Path.GetFullPath (p.FullPath) == Path.GetFullPath (fullPath)).ToList ();
+ }
+
+ readonly IDictionary<string, string> global_properties;
+
+ public IDictionary<string, string> GlobalProperties {
+ get { return global_properties; }
+ }
+
+ readonly List<Project> loaded_projects = new List<Project> ();
+
+ public Project LoadProject (string fileName)
+ {
+ return LoadProject (fileName, DefaultToolsVersion);
+ }
+
+ public Project LoadProject (string fileName, string toolsVersion)
+ {
+ return LoadProject (fileName, null, toolsVersion);
+ }
+
+ public Project LoadProject (string fileName, IDictionary<string,string> globalProperties, string toolsVersion)
+ {
+ var ret = new Project (fileName, globalProperties, toolsVersion);
+ loaded_projects.Add (ret);
+ return ret;
+ }
+
+ // These methods somehow don't add the project to ProjectCollection...
+ public Project LoadProject (XmlReader xmlReader)
+ {
+ return LoadProject (xmlReader, DefaultToolsVersion);
+ }
+
+ public Project LoadProject (XmlReader xmlReader, string toolsVersion)
+ {
+ return LoadProject (xmlReader, null, toolsVersion);
+ }
+
+ public Project LoadProject (XmlReader xmlReader, IDictionary<string,string> globalProperties, string toolsVersion)
+ {
+ return new Project (xmlReader, globalProperties, toolsVersion);
+ }
+
+ public ICollection<Project> LoadedProjects {
+ get { return loaded_projects; }
+ }
+
+ readonly List<ILogger> loggers = new List<ILogger> ();
+ [MonoTODO]
+ public ICollection<ILogger> Loggers {
+ get { return loggers; }
+ }
+
+ [MonoTODO]
+ public bool OnlyLogCriticalEvents { get; set; }
+
+ [MonoTODO]
+ public bool SkipEvaluation { get; set; }
+
+ readonly ToolsetDefinitionLocations toolset_locations;
+ public ToolsetDefinitionLocations ToolsetLocations {
+ get { return toolset_locations; }
+ }
+
+ readonly List<Toolset> toolsets = new List<Toolset> ();
+ // so what should we do without ToolLocationHelper in Microsoft.Build.Utilities.dll? There is no reference to it in this dll.
public ICollection<Toolset> Toolsets {
- get { throw new NotImplementedException (); }
- }
+ // For ConfigurationFile and None, they cannot be added externally.
+ get { return (ToolsetLocations & ToolsetDefinitionLocations.Registry) != 0 ? toolsets : toolsets.ToList (); }
+ }
+
+ public Toolset GetToolset (string toolsVersion)
+ {
+ return Toolsets.FirstOrDefault (t => t.ToolsVersion == toolsVersion);
+ }
+
+ //FIXME: should also support config file, depending on ToolsetLocations
+ void LoadDefaultToolsets ()
+ {
+ AddToolset (new Toolset ("2.0",
+ ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20), this, null));
+ AddToolset (new Toolset ("3.0",
+ ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version30), this, null));
+ AddToolset (new Toolset ("3.5",
+ ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version35), this, null));
+#if NET_4_0
+ AddToolset (new Toolset ("4.0",
+ ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version40), this, null));
+#endif
+#if XBUILD_12
+ AddToolset (new Toolset ("12.0", ToolLocationHelper.GetPathToBuildTools ("12.0"), this, null));
+#endif
+ default_tools_version = toolsets.First ().ToolsVersion;
+ }
+
+ [MonoTODO ("not verified at all")]
+ public void AddToolset (Toolset toolset)
+ {
+ toolsets.Add (toolset);
+ }
+
+ [MonoTODO ("not verified at all")]
+ public void RemoveAllToolsets ()
+ {
+ toolsets.Clear ();
+ }
+
+ [MonoTODO ("not verified at all")]
+ public void RegisterLogger (ILogger logger)
+ {
+ loggers.Add (logger);
+ }
+
+ [MonoTODO ("not verified at all")]
+ public void RegisterLoggers (IEnumerable<ILogger> loggers)
+ {
+ foreach (var logger in loggers)
+ this.loggers.Add (logger);
+ }
public void UnloadAllProjects ()
{
- throw new NotImplementedException ();
+ throw new NotImplementedException ();
}
+ [MonoTODO ("Not verified at all")]
public void UnloadProject (Project project)
{
- throw new NotImplementedException ();
+ this.loaded_projects.Remove (project);
}
+ [MonoTODO ("Not verified at all")]
public void UnloadProject (ProjectRootElement projectRootElement)
{
- throw new NotImplementedException ();
+ foreach (var proj in loaded_projects.Where (p => p.Xml == projectRootElement).ToArray ())
+ UnloadProject (proj);
}
public static Version Version {
- get { throw new NotImplementedException (); }
- }
- }
+ get { throw new NotImplementedException (); }
+ }
+
+ // Execution part
+
+ [MonoTODO]
+ public bool DisableMarkDirty { get; set; }
+
+ [MonoTODO]
+ public HostServices HostServices { get; set; }
+
+ [MonoTODO]
+ public bool IsBuildEnabled { get; set; }
+
+ internal string BuildStartupDirectory { get; set; }
+
+ internal int MaxNodeCount { get; private set; }
+
+ Stack<string> ongoing_imports = new Stack<string> ();
+
+ internal Stack<string> OngoingImports {
+ get { return ongoing_imports; }
+ }
+
+ // common part
+ internal static IEnumerable<EnvironmentProjectProperty> GetWellKnownProperties (Project project)
+ {
+ Func<string,string,EnvironmentProjectProperty> create = (name, value) => new EnvironmentProjectProperty (project, name, value, true);
+ return GetWellKnownProperties (create);
+ }
+
+ internal static IEnumerable<ProjectPropertyInstance> GetWellKnownProperties (ProjectInstance project)
+ {
+ Func<string,string,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, value);
+ return GetWellKnownProperties (create);
+ }
+
+ static IEnumerable<T> GetWellKnownProperties<T> (Func<string,string,T> create)
+ {
+ var ext = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath") ?? DefaultExtensionsPath;
+ yield return create ("MSBuildExtensionsPath", ext);
+ var ext32 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath32") ?? DefaultExtensionsPath;
+ yield return create ("MSBuildExtensionsPath32", ext32);
+ var ext64 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath64") ?? DefaultExtensionsPath;
+ yield return create ("MSBuildExtensionsPath64", ext64);
+ }
+
+ static string extensions_path;
+ internal static string DefaultExtensionsPath {
+ get {
+ if (extensions_path == null) {
+ // NOTE: code from mcs/tools/gacutil/driver.cs
+ PropertyInfo gac = typeof (System.Environment).GetProperty (
+ "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
+
+ if (gac != null) {
+ MethodInfo get_gac = gac.GetGetMethod (true);
+ string gac_path = (string) get_gac.Invoke (null, null);
+ extensions_path = Path.GetFullPath (Path.Combine (
+ gac_path, Path.Combine ("..", "xbuild")));
+ }
+ }
+ return extensions_path;
+ }
+ }
+
+ internal IEnumerable<ReservedProjectProperty> GetReservedProperties (Toolset toolset, Project project)
+ {
+ Func<string,Func<string>,ReservedProjectProperty> create = (name, value) => new ReservedProjectProperty (project, name, value);
+ return GetReservedProperties<ReservedProjectProperty> (toolset, project.Xml, create, () => project.FullPath);
+ }
+
+ internal IEnumerable<ProjectPropertyInstance> GetReservedProperties (Toolset toolset, ProjectInstance project, ProjectRootElement xml)
+ {
+ Func<string,Func<string>,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, null, value);
+ return GetReservedProperties<ProjectPropertyInstance> (toolset, xml, create, () => project.FullPath);
+ }
+
+ // seealso http://msdn.microsoft.com/en-us/library/ms164309.aspx
+ IEnumerable<T> GetReservedProperties<T> (Toolset toolset, ProjectRootElement project, Func<string,Func<string>,T> create, Func<string> projectFullPath)
+ {
+ yield return create ("MSBuildBinPath", () => toolset.ToolsPath);
+ // FIXME: add MSBuildLastTaskResult
+ // FIXME: add MSBuildNodeCount
+ // FIXME: add MSBuildProgramFiles32
+ yield return create ("MSBuildProjectDefaultTargets", () => project.DefaultTargets);
+ yield return create ("MSBuildProjectDirectory", () => project.DirectoryPath + Path.DirectorySeparatorChar);
+ yield return create ("MSBuildProjectDirectoryNoRoot", () => project.DirectoryPath.Substring (Path.GetPathRoot (project.DirectoryPath).Length));
+ yield return create ("MSBuildProjectExtension", () => Path.GetExtension (project.FullPath));
+ yield return create ("MSBuildProjectFile", () => Path.GetFileName (project.FullPath));
+ yield return create ("MSBuildProjectFullPath", () => project.FullPath);
+ yield return create ("MSBuildProjectName", () => Path.GetFileNameWithoutExtension (project.FullPath));
+ yield return create ("MSBuildStartupDirectory", () => BuildStartupDirectory);
+ yield return create ("MSBuildThisFile", () => Path.GetFileName (GetEvaluationTimeThisFile (projectFullPath)));
+ yield return create ("MSBuildThisFileFullPath", () => GetEvaluationTimeThisFile (projectFullPath));
+ yield return create ("MSBuildThisFileName", () => Path.GetFileNameWithoutExtension (GetEvaluationTimeThisFile (projectFullPath)));
+ yield return create ("MSBuildThisFileExtension", () => Path.GetExtension (GetEvaluationTimeThisFile (projectFullPath)));
+
+ yield return create ("MSBuildThisFileDirectory", () => Path.GetDirectoryName (GetEvaluationTimeThisFileDirectory (projectFullPath)));
+ yield return create ("MSBuildThisFileDirectoryNoRoot", () => {
+ string dir = GetEvaluationTimeThisFileDirectory (projectFullPath) + Path.DirectorySeparatorChar;
+ return dir.Substring (Path.GetPathRoot (dir).Length);
+ });
+ yield return create ("MSBuildToolsPath", () => toolset.ToolsPath);
+ yield return create ("MSBuildToolsVersion", () => toolset.ToolsVersion);
+ }
+
+ // These are required for reserved property, represents dynamically changing property values.
+ // This should resolve to either the project file path or that of the imported file.
+ internal string GetEvaluationTimeThisFileDirectory (Func<string> nonImportingTimeFullPath)
+ {
+ var file = GetEvaluationTimeThisFile (nonImportingTimeFullPath);
+ var dir = Path.IsPathRooted (file) ? Path.GetDirectoryName (file) : Directory.GetCurrentDirectory ();
+ return dir + Path.DirectorySeparatorChar;
+ }
+
+ internal string GetEvaluationTimeThisFile (Func<string> nonImportingTimeFullPath)
+ {
+ return OngoingImports.Count > 0 ? OngoingImports.Peek () : (nonImportingTimeFullPath () ?? string.Empty);
+ }
+
+ static readonly char [] item_target_sep = {';'};
+
+ internal static IEnumerable<T> GetAllItems<T> (Func<string,string> expandString, string include, string exclude, Func<string,T> creator, Func<string,ITaskItem> taskItemCreator, string directory, Action<T,string> assignRecurse, Func<ITaskItem,bool> isDuplicate)
+ {
+ var includes = expandString (include).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
+ var excludes = expandString (exclude).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
+
+ if (includes.Length == 0)
+ yield break;
+ if (includes.Length == 1 && includes [0].IndexOf ('*') < 0 && excludes.Length == 0) {
+ // for most case - shortcut.
+ var item = creator (includes [0]);
+ yield return item;
+ } else {
+ var ds = new Microsoft.Build.BuildEngine.DirectoryScanner () {
+ BaseDirectory = new DirectoryInfo (directory),
+ Includes = includes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (i => taskItemCreator (i)).ToArray (),
+ Excludes = excludes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (e => taskItemCreator (e)).ToArray (),
+ };
+ ds.Scan ();
+ foreach (var taskItem in ds.MatchedItems) {
+ if (isDuplicate (taskItem))
+ continue; // skip duplicate
+ var item = creator (taskItem.ItemSpec);
+ string recurse = taskItem.GetMetadata ("RecursiveDir");
+ assignRecurse (item, recurse);
+ yield return item;
+ }
+ }
+ }
+
+ static readonly char [] path_sep = {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
+
+ internal static string GetWellKnownMetadata (string name, string file, Func<string,string> getFullPath, string recursiveDir)
+ {
+ switch (name.ToLower (CultureInfo.InvariantCulture)) {
+ case "fullpath":
+ return getFullPath (file);
+ case "rootdir":
+ return Path.GetPathRoot (getFullPath (file));
+ case "filename":
+ return Path.GetFileNameWithoutExtension (file);
+ case "extension":
+ return Path.GetExtension (file);
+ case "relativedir":
+ var idx = file.LastIndexOfAny (path_sep);
+ return idx < 0 ? string.Empty : file.Substring (0, idx + 1);
+ case "directory":
+ var fp = getFullPath (file);
+ return Path.GetDirectoryName (fp).Substring (Path.GetPathRoot (fp).Length);
+ case "recursivedir":
+ return recursiveDir;
+ case "identity":
+ return file;
+ case "modifiedtime":
+ return new FileInfo (getFullPath (file)).LastWriteTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
+ case "createdtime":
+ return new FileInfo (getFullPath (file)).CreationTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
+ case "accessedtime":
+ return new FileInfo (getFullPath (file)).LastAccessTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
+ }
+ return null;
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedEventArgs.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedEventArgs.cs
new file mode 100644
index 0000000000..a4e8ee90d7
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedEventArgs.cs
@@ -0,0 +1,16 @@
+using System;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Evaluation
+{
+ public class ProjectCollectionChangedEventArgs : EventArgs
+ {
+ public ProjectCollectionChangedEventArgs (ProjectCollectionChangedState state)
+ {
+ State = state;
+ }
+
+ public ProjectCollectionChangedState State { get; private set; }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedState.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedState.cs
new file mode 100644
index 0000000000..683080f13e
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectCollectionChangedState.cs
@@ -0,0 +1,19 @@
+using System;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Evaluation
+{
+ public enum ProjectCollectionChangedState
+ {
+ DefaultToolsVersion,
+ DisableMarkDirty,
+ GlobalProperties,
+ HostServices,
+ IsBuildEnabled,
+ Loggers,
+ OnlyLogCriticalEvents,
+ SkipEvaluation,
+ Toolsets
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItem.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItem.cs
index 393995e80c..cc187f9b94 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItem.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItem.cs
@@ -4,9 +4,10 @@
// Author:
// Leszek Ciesielski (skolima@gmail.com)
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
// (C) 2011 Leszek Ciesielski
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -31,88 +32,126 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-
+using System.Linq;
using Microsoft.Build.Construction;
+using System.IO;
+using Microsoft.Build.Framework;
namespace Microsoft.Build.Evaluation
{
- [DebuggerDisplay("{ItemType}={EvaluatedInclude} [{UnevaluatedInclude}] #DirectMetadata={DirectMetadataCount}")]
- public class ProjectItem
- {
- internal ProjectItem (ProjectItemElement xml)
- {
- Xml = xml;
- }
-
- public ProjectMetadata GetMetadata (string name)
- {
- throw new NotImplementedException ();
- }
-
- public string GetMetadataValue (string name)
- {
- throw new NotImplementedException ();
- }
-
- public bool HasMetadata (string name)
- {
- throw new NotImplementedException ();
- }
-
- public bool RemoveMetadata (string name)
- {
- throw new NotImplementedException ();
- }
-
- public void Rename (string name)
- {
- throw new NotImplementedException ();
- }
-
- public void SetMetadataValue (string name, string unevaluatedValue)
- {
- throw new NotImplementedException ();
- }
-
- public IEnumerable<ProjectMetadata> DirectMetadata {
- get { throw new NotImplementedException (); }
- }
-
- public int DirectMetadataCount {
- get { throw new NotImplementedException (); }
- }
-
- public string EvaluatedInclude {
- get { throw new NotImplementedException (); }
- }
-
- public bool IsImported {
- get { throw new NotImplementedException (); }
- }
-
- public string ItemType {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public ICollection<ProjectMetadata> Metadata {
- get { throw new NotImplementedException (); }
- }
-
- public int MetadataCount {
- get { throw new NotImplementedException (); }
- }
-
- public Project Project {
- get { throw new NotImplementedException (); }
- }
-
- public string UnevaluatedInclude {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public ProjectItemElement Xml { get; private set; }
-
- }
+ [DebuggerDisplay ("{ItemType}={EvaluatedInclude} [{UnevaluatedInclude}] #DirectMetadata={DirectMetadataCount}")]
+ public class ProjectItem
+ {
+ internal ProjectItem (Project project, ProjectItemElement xml, string evaluatedInclude)
+ {
+ this.project = project;
+ this.xml = xml;
+ if (project.ItemDefinitions.ContainsKey (ItemType))
+ foreach (var md in project.ItemDefinitions [ItemType].Metadata)
+ metadata.Add (md);
+ foreach (var md in xml.Metadata)
+ metadata.Add (new ProjectMetadata (project, ItemType, metadata, m => metadata.Remove (m), md));
+ this.evaluated_include = evaluatedInclude;
+ is_imported = project.ProjectCollection.OngoingImports.Any ();
+ }
+
+ readonly Project project;
+ readonly ProjectItemElement xml;
+ readonly List<ProjectMetadata> metadata = new List<ProjectMetadata> ();
+ readonly bool is_imported;
+ readonly string evaluated_include;
+
+ internal string RecursiveDir { get; set; }
+
+ public ProjectMetadata GetMetadata (string name)
+ {
+ return metadata.FirstOrDefault (m => m.Name == name);
+ }
+
+ public string GetMetadataValue (string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+ var wk = ProjectCollection.GetWellKnownMetadata (name, EvaluatedInclude, project.GetFullPath, RecursiveDir);
+ if (wk != null)
+ return wk;
+ var m = GetMetadata (name);
+ return m != null ? m.EvaluatedValue : string.Empty;
+ }
+
+ public bool HasMetadata (string name)
+ {
+ return GetMetadata (name) != null;
+ }
+
+ public bool RemoveMetadata (string name)
+ {
+ var m = GetMetadata (name);
+ if (m == null)
+ return false;
+ return metadata.Remove (m);
+ }
+
+ public void Rename (string name)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public ProjectMetadata SetMetadataValue (string name, string unevaluatedValue)
+ {
+ // This has to do several tasks:
+ // - it cannot directly change Xml.Metadata because the ProjectItemElement might be shared
+ // among multiple ProjectItems.
+ // - hence it has to create another ProjectItemElement instance and add it to the project
+ // XML construction, with specific Include value that is assigned to this instance, and
+ // metadata values that are assigned to this instance.
+ throw new NotImplementedException ();
+ }
+
+ public IEnumerable<ProjectMetadata> DirectMetadata {
+ get {
+ var list = new List<ProjectMetadata> ();
+ foreach (var xm in xml.Metadata)
+ yield return new ProjectMetadata (project, ItemType, list, p => list.Remove (p), xm);
+ }
+ }
+
+ public int DirectMetadataCount {
+ get { return xml.Metadata.Count; }
+ }
+
+ public string EvaluatedInclude {
+ get { return evaluated_include; }
+ }
+
+ public bool IsImported {
+ get { return is_imported; }
+ }
+
+ public string ItemType {
+ get { return Xml.ItemType; }
+ set { Xml.ItemType = value; }
+ }
+
+ public ICollection<ProjectMetadata> Metadata {
+ get { return metadata; }
+ }
+
+ public int MetadataCount {
+ get { return metadata.Count; }
+ }
+
+ public Project Project {
+ get { return project; }
+ }
+
+ public string UnevaluatedInclude {
+ get { return Xml.Include; }
+ set { Xml.Include = value; }
+ }
+
+ public ProjectItemElement Xml {
+ get { return xml; }
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItemDefinition.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItemDefinition.cs
index 13ef541bc6..faa880603b 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItemDefinition.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectItemDefinition.cs
@@ -2,8 +2,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -26,15 +27,43 @@
//
using System;
+using System.Collections.Generic;
+using Microsoft.Build.Construction;
namespace Microsoft.Build.Evaluation
{
- public class ProjectItemDefinition
- {
- private ProjectItemDefinition ()
- {
- throw new NotImplementedException ();
- }
- }
-}
+ public class ProjectItemDefinition
+ {
+ internal ProjectItemDefinition (Project project, string itemType)
+ {
+ this.project = project;
+ this.item_type = itemType;
+ }
+
+ Project project;
+ string item_type;
+ List<ProjectMetadata> metadata = new List<ProjectMetadata> ();
+
+ public string ItemType {
+ get { return item_type; }
+ }
+ public IEnumerable<ProjectMetadata> Metadata {
+ get { return metadata; }
+ }
+
+ public int MetadataCount {
+ get { return metadata.Count; }
+ }
+
+ public Project Project {
+ get { return project; }
+ }
+
+ internal void AddItems (ProjectItemDefinitionElement xml)
+ {
+ foreach (var item in xml.Metadata)
+ metadata.Add (new ProjectMetadata (project, ItemType, metadata, m => metadata.Remove (m), item));
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectMetadata.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectMetadata.cs
index 45001f61a2..880a0e336b 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectMetadata.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectMetadata.cs
@@ -2,8 +2,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -26,49 +27,62 @@
//
using Microsoft.Build.Construction;
-
using System;
+using System.Collections.Generic;
+using System.Linq;
namespace Microsoft.Build.Evaluation
{
- public class ProjectMetadata
- {
- private ProjectMetadata ()
- {
- throw new NotImplementedException ();
- }
+ public class ProjectMetadata
+ {
+ internal ProjectMetadata (Project project, string itemType, IEnumerable<ProjectMetadata> existingMetadata, Action<ProjectMetadata> remover, ProjectMetadataElement xml)
+ {
+ this.xml = xml;
+ this.project = project;
+ item_type = itemType;
+ predecessor = existingMetadata.FirstOrDefault (m => m.Name == xml.Name);
+ if (predecessor != null)
+ remover (predecessor);
+ is_imported = Project.ProjectCollection.OngoingImports.Any ();
+ }
+
+ readonly Project project;
+ readonly string item_type;
+ readonly ProjectMetadataElement xml;
+ readonly ProjectMetadata predecessor;
+ readonly bool is_imported;
- public string EvaluatedValue {
- get { throw new NotImplementedException (); }
- }
+ public string EvaluatedValue {
+ get { return project.ExpandString (xml.Value); }
+ }
- public bool IsImported {
- get { throw new NotImplementedException (); }
- }
+ public bool IsImported {
+ get { return is_imported; }
+ }
- public string ItemType {
- get { throw new NotImplementedException (); }
- }
+ public string ItemType {
+ get { return item_type; }
+ }
- public string Name {
- get { throw new NotImplementedException (); }
- }
+ public string Name {
+ get { return xml.Name; }
+ }
- public ProjectMetadata Predecessor {
- get { throw new NotImplementedException (); }
- }
+ public ProjectMetadata Predecessor {
+ get { return predecessor; }
+ }
- public Project Project {
- get { throw new NotImplementedException (); }
- }
+ public Project Project {
+ get { return project; }
+ }
- public string UnevaluatedValue {
- get { throw new NotImplementedException (); }
- }
+ public string UnevaluatedValue {
+ get { return xml.Value; }
+ }
- public ProjectMetadataElement Xml {
- get { throw new NotImplementedException (); }
- }
- }
+ public ProjectMetadataElement Xml {
+ get { return xml; }
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectProperty.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectProperty.cs
index e9d557f3e0..757b40f3ce 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectProperty.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectProperty.cs
@@ -2,8 +2,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -26,7 +27,12 @@
//
using System;
+using System.Linq;
using Microsoft.Build.Construction;
+using Microsoft.Build.Internal;
+using System.Collections.Generic;
+using System.Reflection;
+using System.IO;
namespace Microsoft.Build.Evaluation
{
@@ -34,39 +40,249 @@ namespace Microsoft.Build.Evaluation
// members are abstract and had been there since 4.0.
// I take this as doc bug, as non-abstract to abstract is a
// breaking change and I'd rather believe API designer's sanity.
- public abstract class ProjectProperty
- {
- internal ProjectProperty () // hide default ctor
+ public abstract class ProjectProperty
+ {
+ internal ProjectProperty (Project project) // hide default ctor
{
+ Project = project;
}
- public string EvaluatedValue {
- get {
- throw new NotImplementedException ();
- }
- }
+ public string EvaluatedValue {
+ get { return InternalEvaluatedValue; }
+ }
public abstract bool IsEnvironmentProperty { get; }
+
public abstract bool IsGlobalProperty { get; }
+
+ [MonoTODO]
public abstract bool IsImported { get; }
+
public abstract bool IsReservedProperty { get; }
- public string Name {
- get {
- throw new NotImplementedException ();
- }
+ internal virtual bool IsWellKnownProperty {
+ get { return false; }
}
+ public abstract string Name { get; }
+
public abstract ProjectProperty Predecessor { get; }
- public Project Project {
+ public Project Project { get; private set; }
+
+ public abstract string UnevaluatedValue { get; set; }
+
+ public abstract ProjectPropertyElement Xml { get; }
+
+ internal abstract string InternalEvaluatedValue { get; }
+ }
+
+ // copy from MS.Build.Engine/BuildProperty.cs
+ internal enum PropertyType {
+ Reserved,
+ Global,
+ Normal,
+ Environment
+ }
+
+ internal abstract class BaseProjectProperty : ProjectProperty
+ {
+ public BaseProjectProperty (Project project, PropertyType propertyType, string name)
+ : base (project)
+ {
+ property_type = propertyType;
+ this.name = name;
+ predecessor = project.Properties.FirstOrDefault (p => p.Name == name);
+ if (predecessor != null)
+ project.RemoveProperty (predecessor);
+ }
+
+ PropertyType property_type;
+
+ readonly string name;
+ public override string Name {
+ get { return name; }
+ }
+
+ public override bool IsEnvironmentProperty {
+ get { return property_type == PropertyType.Environment; }
+ }
+ public override bool IsGlobalProperty {
+ get { return property_type == PropertyType.Global; }
+ }
+ public override bool IsImported {
+ get { return false; }
+ }
+ public override bool IsReservedProperty {
+ get { return property_type == PropertyType.Reserved; }
+ }
+ readonly ProjectProperty predecessor;
+ public override ProjectProperty Predecessor {
+ get { return predecessor; }
+ }
+ }
+
+ internal abstract class ImmutableProjectProperty : BaseProjectProperty
+ {
+ public ImmutableProjectProperty (Project project, PropertyType propertyType, string name)
+ : base (project, propertyType, name)
+ {
+ }
+
+ internal override string InternalEvaluatedValue {
+ get { return UnevaluatedValue; }
+ }
+ }
+
+ internal abstract class MutableProjectProperty : BaseProjectProperty
+ {
+ public MutableProjectProperty (Project project, PropertyType propertyType, string name)
+ : base (project, propertyType, name)
+ {
+ }
+
+ string evaluated_value; // see UpdateEvaluatedValue().
+ internal void UpdateEvaluatedValue ()
+ {
+ evaluated_value = Project.ExpandString (UnevaluatedValue);
+ }
+
+ internal override string InternalEvaluatedValue {
+ get { return evaluated_value; }
+ }
+ }
+
+ internal class XmlProjectProperty : MutableProjectProperty
+ {
+ public XmlProjectProperty (Project project, ProjectPropertyElement xml, PropertyType propertyType, bool isImported)
+ : base (project, propertyType, xml.Name)
+ {
+ this.xml = xml;
+ this.is_imported = isImported;
+ UpdateEvaluatedValue ();
+ }
+
+ readonly ProjectPropertyElement xml;
+ readonly bool is_imported;
+
+ public override bool IsImported {
+ get { return is_imported; }
+ }
+
+ public override string UnevaluatedValue {
+ get { return xml.Value; }
+ set { xml.Value = value; }
+ }
+
+ public override ProjectPropertyElement Xml {
+ get { return xml; }
+ }
+ }
+
+ internal class EnvironmentProjectProperty : ImmutableProjectProperty
+ {
+ static string extensions_path;
+ internal static string DefaultExtensionsPath {
get {
- throw new NotImplementedException ();
+ if (extensions_path == null) {
+ // NOTE: code from mcs/tools/gacutil/driver.cs
+ PropertyInfo gac = typeof (System.Environment).GetProperty (
+ "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
+
+ if (gac != null) {
+ MethodInfo get_gac = gac.GetGetMethod (true);
+ string gac_path = (string) get_gac.Invoke (null, null);
+ extensions_path = Path.GetFullPath (Path.Combine (
+ gac_path, Path.Combine ("..", "xbuild")));
+ }
+ }
+ return extensions_path;
}
}
+
+ public EnvironmentProjectProperty (Project project, string name, string value, bool wellknown = false)
+ : base (project, PropertyType.Environment, name)
+ {
+ this.value = value;
+ this.wellknown = wellknown;
+ }
+
+ readonly string value;
+ readonly bool wellknown;
- public abstract string UnevaluatedValue { get; set; }
- public abstract ProjectPropertyElement Xml { get; }
- }
-}
+ internal override bool IsWellKnownProperty {
+ get { return wellknown; }
+ }
+ // It can override possible another environment vairable property BUT never gives Predecessor.
+ public override ProjectProperty Predecessor {
+ get { return null; }
+ }
+
+ public override string UnevaluatedValue {
+ get { return value; }
+ set { throw new InvalidOperationException (string.Format ("You cannot change value of environment property '{0}'.", Name)); }
+ }
+ public override ProjectPropertyElement Xml {
+ get { return null; }
+ }
+ }
+
+ internal class GlobalProjectProperty : ImmutableProjectProperty
+ {
+ public GlobalProjectProperty (Project project, string name, string value)
+ : base (project, PropertyType.Global, name)
+ {
+ this.value = value;
+ }
+
+ readonly string value;
+
+ public override string UnevaluatedValue {
+ get { return value; }
+ set { throw new InvalidOperationException (string.Format ("You cannot change value of global property '{0}'.", Name)); }
+ }
+ public override ProjectPropertyElement Xml {
+ get { return null; }
+ }
+ }
+
+ internal class ManuallyAddedProjectProperty : MutableProjectProperty
+ {
+ public ManuallyAddedProjectProperty (Project project, string name, string value)
+ : base (project, PropertyType.Normal, name)
+ {
+ this.UnevaluatedValue = value;
+ }
+
+ public override string UnevaluatedValue { get; set; }
+
+ public override ProjectPropertyElement Xml {
+ get { return null; }
+ }
+ }
+
+ internal class ReservedProjectProperty : ImmutableProjectProperty
+ {
+ public ReservedProjectProperty (Project project, string name, Func<string> value)
+ : base (project, PropertyType.Reserved, name)
+ {
+ this.value = value;
+ }
+
+ // make sure it does not give access to any possible attempted overrrides.
+ public override ProjectProperty Predecessor {
+ get { return null; }
+ }
+
+ readonly Func<string> value;
+ public override string UnevaluatedValue {
+ get { return value (); }
+ set { throw new InvalidOperationException (string.Format ("You cannot change value of reserved property '{0}'.", Name)); }
+ }
+
+ public override ProjectPropertyElement Xml {
+ get { return null; }
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ColorSetter.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectXmlChangedEventArgs.cs
index 7e510e7bbf..a0497a505a 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ColorSetter.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ProjectXmlChangedEventArgs.cs
@@ -1,9 +1,10 @@
-// ColorSetter.cs
+//
+// ProjectXmlChangedEventArgs.cs
//
// Author:
-// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto <atsushi@xamarin.com>
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -15,7 +16,7 @@
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
+//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -25,8 +26,20 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-namespace Microsoft.Build.Logging
+using Microsoft.Build.Construction;
+using System;
+using System.Linq;
+
+namespace Microsoft.Build.Evaluation
{
- public delegate void ColorSetter (System.ConsoleColor color);
+ public class ProjectXmlChangedEventArgs : EventArgs
+ {
+ internal ProjectXmlChangedEventArgs (ProjectRootElement projectXml, string reason)
+ {
+ ProjectXml = projectXml;
+ Reason = reason;
+ }
+ public ProjectRootElement ProjectXml { get; private set; }
+ public string Reason { get; private set; }
+ }
}
-
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ResolvedImport.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ResolvedImport.cs
index 1e82c01a4c..8a5bed7437 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ResolvedImport.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/ResolvedImport.cs
@@ -26,24 +26,35 @@
//
using Microsoft.Build.Construction;
-
using System;
namespace Microsoft.Build.Evaluation
{
- [System.Runtime.InteropServices.StructLayout (System.Runtime.InteropServices.LayoutKind.Sequential)]
- public struct ResolvedImport
- {
- private ProjectImportElement import;
- private ProjectRootElement root;
+ [System.Runtime.InteropServices.StructLayout (System.Runtime.InteropServices.LayoutKind.Sequential)]
+ public struct ResolvedImport
+ {
+ internal ResolvedImport (ProjectImportElement import, ProjectRootElement root, bool isImported)
+ {
+ this.import = import;
+ this.root = root;
+ this.imported = isImported;
+ }
+
+ readonly ProjectImportElement import;
+ readonly ProjectRootElement root;
+ readonly bool imported;
- public ProjectImportElement ImportingElement {
- get { return import; }
- }
+ public ProjectImportElement ImportingElement {
+ get { return import; }
+ }
- public ProjectRootElement ImportedProject {
- get { return root; }
- }
- }
+ public ProjectRootElement ImportedProject {
+ get { return root; }
+ }
+
+ public bool IsImported {
+ get { return imported; }
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/SubToolset.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/SubToolset.cs
new file mode 100644
index 0000000000..0b4ad39224
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/SubToolset.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Build.Execution;
+
+namespace Microsoft.Build.Evaluation
+{
+ #if NET_4_5
+ public
+ #endif
+ class SubToolset
+ {
+ internal SubToolset (IDictionary<string, ProjectPropertyInstance> properties, string subToolsetVersion)
+ {
+ Properties = properties;
+ SubToolsetVersion = subToolsetVersion;
+ }
+
+ public IDictionary<string, ProjectPropertyInstance> Properties { get; private set; }
+ public string SubToolsetVersion { get; private set; }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Toolset.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Toolset.cs
index 9da377be35..b0182c53bb 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Toolset.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Evaluation/Toolset.cs
@@ -2,8 +2,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -27,37 +28,54 @@
using System;
using System.Collections.Generic;
-
+using System.Linq;
using Microsoft.Build.Execution;
namespace Microsoft.Build.Evaluation
{
- public class Toolset
- {
- public Toolset (string toolsVersion, string toolsPath,
- ProjectCollection projectCollection, string msbuildOverrideTasksPath)
- {
- throw new NotImplementedException ();
- }
-
- public Toolset (string toolsVersion, string toolsPath,
- IDictionary<string, string> buildProperties, ProjectCollection projectCollection,
- string msbuildOverrideTasksPath)
- {
- throw new NotImplementedException ();
- }
-
- public IDictionary<string, ProjectPropertyInstance> Properties {
- get { throw new NotImplementedException (); }
- }
-
- public string ToolsPath {
- get { throw new NotImplementedException (); }
- }
-
- public string ToolsVersion {
- get { throw new NotImplementedException (); }
- }
- }
+ public class Toolset
+ {
+ public Toolset (string toolsVersion, string toolsPath,
+ ProjectCollection projectCollection, string msbuildOverrideTasksPath)
+ : this (toolsVersion, toolsPath, null, projectCollection, msbuildOverrideTasksPath)
+ {
+ }
+
+ public Toolset (string toolsVersion, string toolsPath,
+ IDictionary<string, string> buildProperties, ProjectCollection projectCollection,
+ string msbuildOverrideTasksPath)
+ : this (toolsVersion, toolsPath, buildProperties, projectCollection, null, msbuildOverrideTasksPath)
+ {
+ }
+
+#if NET_4_5
+ public
+#endif
+ Toolset (string toolsVersion, string toolsPath, IDictionary<string, string> buildProperties,
+ ProjectCollection projectCollection, IDictionary<string, SubToolset> subToolsets,
+ string msbuildOverrideTasksPath)
+ {
+ ToolsVersion = toolsVersion;
+ ToolsPath = toolsPath;
+ Properties =
+ buildProperties == null ?
+ new Dictionary<string, ProjectPropertyInstance> () :
+ buildProperties.Select (p => new ProjectPropertyInstance (p.Key, true, p.Value)).ToDictionary (e => e.Name);
+#if NET_4_5
+ SubToolsets = subToolsets ?? new Dictionary<string, SubToolset> ();
+#endif
+ }
+
+#if NET_4_5
+ public string DefaultSubToolsetVersion { get; private set; }
+ public IDictionary<string, SubToolset> SubToolsets { get; private set; }
+#endif
+
+ public IDictionary<string, ProjectPropertyInstance> Properties { get; private set; }
+
+ public string ToolsPath { get; private set; }
+
+ public string ToolsVersion { get; private set; }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/BuildAbortedException.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/BuildAbortedException.cs
new file mode 100644
index 0000000000..70a2969260
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/BuildAbortedException.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Build.Exceptions
+{
+ public class BuildAbortedException : Exception
+ {
+ public BuildAbortedException ()
+ : this ("Build aborted")
+ {
+ }
+
+ public BuildAbortedException (string message)
+ : base (message)
+ {
+ }
+
+ public BuildAbortedException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ }
+ protected BuildAbortedException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ ErrorCode = info.GetString ("errorCode");
+ }
+
+ internal BuildAbortedException (string message, string errorCode)
+ : base (message + " error code: " + errorCode)
+ {
+ ErrorCode = errorCode;
+ }
+
+ public string ErrorCode { get; private set; }
+
+ public override void GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData (info, context);
+ info.AddValue ("errorCode", ErrorCode);
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InternalLoggerException.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InternalLoggerException.cs
new file mode 100644
index 0000000000..3aecdfdbb0
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InternalLoggerException.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Runtime.Serialization;
+using Microsoft.Build.Framework;
+
+namespace Microsoft.Build.Exceptions
+{
+ public class InternalLoggerException : Exception
+ {
+ public InternalLoggerException ()
+ : this ("Build aborted")
+ {
+ }
+
+ public InternalLoggerException (string message)
+ : base (message)
+ {
+ }
+
+ public InternalLoggerException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ }
+
+ internal InternalLoggerException (string message, Exception innerException, BuildEventArgs buildEventArgs, string errorCode, string helpKeyword, bool initializationException)
+ : base (message, innerException)
+ {
+ BuildEventArgs = buildEventArgs;
+ ErrorCode = errorCode;
+ HelpKeyword = helpKeyword;
+ InitializationException = initializationException;
+ }
+
+ internal InternalLoggerException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ BuildEventArgs = (BuildEventArgs) info.GetValue ("buildEventArgs", typeof (BuildEventArgs));
+ ErrorCode = info.GetString ("errorCode");
+ HelpKeyword = info.GetString ("helpKeyword");
+ InitializationException = info.GetBoolean ("initializationException");
+ }
+
+ public BuildEventArgs BuildEventArgs { get; private set; }
+ public string ErrorCode { get; private set; }
+ public string HelpKeyword { get; private set; }
+ public bool InitializationException { get; private set; }
+
+ public override void GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData (info, context);
+ info.AddValue ("buildEventArgs", BuildEventArgs);
+ info.AddValue ("errorCode", ErrorCode);
+ info.AddValue ("helpKeyword", HelpKeyword);
+ info.AddValue ("initializationException", InitializationException);
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidProjectFileException.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidProjectFileException.cs
index ea2a51dc40..ad05ff6403 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidProjectFileException.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidProjectFileException.cs
@@ -28,6 +28,8 @@
using System;
using System.Runtime.Serialization;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Internal.Expressions;
namespace Microsoft.Build.Exceptions
{
@@ -57,10 +59,31 @@ namespace Microsoft.Build.Exceptions
: base(message, innerException)
{
}
+ internal InvalidProjectFileException (ILocation start, string message,
+ string errorSubcategory = null, string errorCode = null, string helpKeyword = null)
+ : this (start != null ? start.File : null, 0, start != null ? start.Column : 0, 0, 0, message, errorSubcategory, errorCode, helpKeyword)
+ {
+ }
+ internal InvalidProjectFileException (ElementLocation start, ElementLocation end, string message,
+ string errorSubcategory = null, string errorCode = null, string helpKeyword = null)
+ : this (start != null ? start.File : null, start != null ? start.Line : 0, start != null ? start.Column : 0,
+ end != null ? end.Line : 0, end != null ? end.Column : 0,
+ message, errorSubcategory, errorCode, helpKeyword)
+ {
+ }
public InvalidProjectFileException (string projectFile, int lineNumber, int columnNumber,
int endLineNumber, int endColumnNumber, string message,
string errorSubcategory, string errorCode, string helpKeyword)
+ : base(message)
{
+ ProjectFile = projectFile;
+ LineNumber = lineNumber;
+ ColumnNumber = columnNumber;
+ EndLineNumber = endLineNumber;
+ EndColumnNumber = endColumnNumber;
+ ErrorSubcategory = errorSubcategory;
+ ErrorCode = errorCode;
+ HelpKeyword = helpKeyword;
}
public override void GetObjectData (SerializationInfo info, StreamingContext context)
{
@@ -87,8 +110,15 @@ namespace Microsoft.Build.Exceptions
public string HelpKeyword { get; private set; }
public int LineNumber { get; private set; }
public override string Message {
- get { return ProjectFile == null ? base.Message : base.Message + " " + ProjectFile; }
+ get { return ProjectFile == null ? base.Message : base.Message + " " + GetLocation (); }
}
public string ProjectFile { get; private set; }
+
+ string GetLocation ()
+ {
+ string start = LineNumber == 0 ? string.Empty : ColumnNumber > 0 ? string.Format ("{0},{1}", LineNumber, ColumnNumber) : string.Format ("{0}", LineNumber);
+ string end = EndLineNumber == 0 ? string.Empty : EndColumnNumber > 0 ? string.Format (" - {0},{1}", EndLineNumber, EndColumnNumber) : string.Format (" - {0}", EndLineNumber);
+ return LineNumber == 0 ? ProjectFile : String.Format (" at: {0} ({1}{2})", ProjectFile, start, end);
+ }
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidToolsetDefinitionException.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidToolsetDefinitionException.cs
new file mode 100644
index 0000000000..b518d182ac
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Exceptions/InvalidToolsetDefinitionException.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Microsoft.Build.Exceptions
+{
+ public class InvalidToolsetDefinitionException : Exception
+ {
+ public InvalidToolsetDefinitionException ()
+ : this ("Invalid toolset definition")
+ {
+ }
+
+ public InvalidToolsetDefinitionException (string message)
+ : base (message)
+ {
+ }
+
+ public InvalidToolsetDefinitionException (string message, Exception innerException)
+ : base (message, innerException)
+ {
+ }
+ protected InvalidToolsetDefinitionException (SerializationInfo info, StreamingContext context)
+ : base (info, context)
+ {
+ ErrorCode = info.GetString ("errorCode");
+ }
+
+ internal InvalidToolsetDefinitionException (string message, string errorCode)
+ : base (message + " error code: " + errorCode)
+ {
+ ErrorCode = errorCode;
+ }
+
+ public string ErrorCode { get; private set; }
+
+ public override void GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData (info, context);
+ info.AddValue ("errorCode", ErrorCode);
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildManager.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildManager.cs
index 3b4384b2f6..0c179afa48 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildManager.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildManager.cs
@@ -2,8 +2,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -26,66 +27,143 @@
//
using Microsoft.Build.Evaluation;
-
using System;
+using System.Collections.Generic;
+using System.Threading;
+using Microsoft.Build.Internal;
+using System.Linq;
namespace Microsoft.Build.Execution
{
- public class BuildManager
- {
- public BuildManager ()
- {
- throw new NotImplementedException ();
- }
+ public class BuildManager
+ {
+ static BuildManager default_manager = new BuildManager ();
- public BuildManager (string hostName)
- {
- throw new NotImplementedException ();
- }
+ public static BuildManager DefaultBuildManager {
+ get { return default_manager; }
+ }
+
+ public BuildManager ()
+ {
+ }
- public void BeginBuild (BuildParameters parameters)
- {
- throw new NotImplementedException ();
- }
+ public BuildManager (string hostName)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public void Dispose ()
+ {
+ WaitHandle.WaitAll (submissions.Select (s => s.WaitHandle).ToArray ());
+ BuildNodeManager.Stop ();
+ }
- public BuildResult Build (BuildParameters parameters, BuildRequestData requestData)
- {
- throw new NotImplementedException ();
- }
+ ~BuildManager ()
+ {
+ // maybe processes created by out-of-process nodes should be signaled.
+ BuildNodeManager.Stop ();
+ }
- public BuildResult BuildRequest (BuildRequestData requestData)
- {
- throw new NotImplementedException ();
- }
+ readonly List<BuildSubmission> submissions = new List<BuildSubmission> ();
+
+ BuildParameters ongoing_build_parameters;
+
+ internal BuildParameters OngoingBuildParameters {
+ get { return ongoing_build_parameters; }
+ }
- public void CancelAllSubmissions ()
- {
- throw new NotImplementedException ();
- }
+ public void BeginBuild (BuildParameters parameters)
+ {
+ if (ongoing_build_parameters != null)
+ throw new InvalidOperationException ("There is already ongoing build");
+ ongoing_build_parameters = parameters.Clone ();
+ }
- public void EndBuild ()
- {
- throw new NotImplementedException ();
- }
+ public BuildResult Build (BuildParameters parameters, BuildRequestData requestData)
+ {
+ BeginBuild (parameters);
+ var ret = BuildRequest (requestData);
+ EndBuild ();
+ return ret;
+ }
- public ProjectInstance GetProjectInstanceForBuild (Project project)
- {
- throw new NotImplementedException ();
- }
+ public BuildResult BuildRequest (BuildRequestData requestData)
+ {
+ var sub = PendBuildRequest (requestData);
+ sub.Execute ();
+ return sub.BuildResult;
+ }
+
+ public void CancelAllSubmissions ()
+ {
+ foreach (var sub in submissions) {
+ try {
+ if (!sub.IsCompleted)
+ sub.Cancel ();
+ } catch (InvalidOperationException) {
+ // some submissions could be already done during this iteration. Ignore that.
+ }
+ }
+ submissions.Clear ();
+ }
- public BuildSubmission PendBuildRequest (BuildRequestData requestData)
- {
- throw new NotImplementedException ();
- }
+ public void EndBuild ()
+ {
+ if (ongoing_build_parameters == null)
+ throw new InvalidOperationException ("Build has not started");
+ if (submissions.Count > 0)
+ WaitHandle.WaitAll (submissions.Select (s => s.WaitHandle).ToArray ());
+ BuildNodeManager.Stop ();
+ ongoing_build_parameters = null;
+ }
+
+ Dictionary<Project,ProjectInstance> instances = new Dictionary<Project, ProjectInstance> ();
- public void ResetCaches ()
- {
- throw new NotImplementedException ();
- }
+ public ProjectInstance GetProjectInstanceForBuild (Project project)
+ {
+ if (project == null)
+ throw new ArgumentNullException ("project");
+ if (project.FullPath == null)
+ throw new ArgumentNullException ("project", "FullPath parameter in the project cannot be null.");
+ if (project.FullPath == string.Empty)
+ throw new ArgumentException ("FullPath parameter in the project cannot be empty.", "project");
+ // other than that, any invalid path character is accepted...
+
+ return GetProjectInstanceForBuildInternal (project);
+ }
+
+ internal ProjectInstance GetProjectInstanceForBuildInternal (Project project)
+ {
+ if (!instances.ContainsKey (project))
+ instances [project] = project.CreateProjectInstance ();
+ return instances [project];
+ }
- public static BuildManager DefaultBuildManager {
- get { throw new NotImplementedException (); }
- }
- }
-}
+ public BuildSubmission PendBuildRequest (BuildRequestData requestData)
+ {
+ if (ongoing_build_parameters == null)
+ throw new InvalidOperationException ("This method cannot be called before calling BeginBuild method.");
+ var sub = new BuildSubmission (this, requestData);
+ submissions.Add (sub);
+ return sub;
+ }
+ public void ResetCaches ()
+ {
+ if (OngoingBuildParameters != null)
+ throw new InvalidOperationException ("Cannot reset caches while builds are in progress.");
+
+ BuildNodeManager.ResetCaches ();
+ }
+
+ BuildNodeManager build_node_manager;
+
+ internal BuildNodeManager BuildNodeManager {
+ get {
+ if (build_node_manager == null)
+ build_node_manager = new BuildNodeManager (this);
+ return build_node_manager;
+ }
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildParameters.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildParameters.cs
index 26944910f9..61f8393cda 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildParameters.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildParameters.cs
@@ -2,8 +2,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -28,132 +29,128 @@
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
-
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using System.Threading;
+using System.Collections;
namespace Microsoft.Build.Execution
{
- public class BuildParameters
- {
- public BuildParameters ()
- {
- throw new NotImplementedException ();
- }
-
- public BuildParameters (ProjectCollection projectCollection)
- {
- throw new NotImplementedException ();
- }
-
- public BuildParameters Clone ()
- {
- throw new NotImplementedException ();
- }
-
- public Toolset GetToolset (string toolsVersion)
- {
- throw new NotImplementedException ();
- }
-
- public ThreadPriority BuildThreadPriority {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public CultureInfo Culture {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public string DefaultToolsVersion {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public bool DetailedSummary {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public bool EnableNodeReuse {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public IDictionary<string, string> EnvironmentProperties {
- get { throw new NotImplementedException (); }
- }
-
- public IEnumerable<ForwardingLoggerRecord> ForwardingLoggers {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public IDictionary<string, string> GlobalProperties {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public HostServices HostServices {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public bool LegacyThreadingSemantics { get; set; }
-
- public IEnumerable<ILogger> Loggers {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public int MaxNodeCount {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public int MemoryUseLimit {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public string NodeExeLocation {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public bool OnlyLogCriticalEvents {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public bool ResetCaches { get; set; }
-
- public bool SaveOperatingEnvironment {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public ToolsetDefinitionLocations ToolsetDefinitionLocations {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public ICollection<Toolset> Toolsets {
- get { throw new NotImplementedException (); }
- }
-
- public CultureInfo UICulture {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public bool UseSynchronousLogging {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
- }
+ public class BuildParameters
+ {
+ public BuildParameters ()
+ : this (new ProjectCollection ())
+ {
+ }
+
+ public BuildParameters (ProjectCollection projectCollection)
+ {
+ if (projectCollection == null)
+ throw new ArgumentNullException ("projectCollection");
+ projects = projectCollection;
+
+ EnableNodeReuse = true;
+ Culture = CultureInfo.CurrentCulture;
+ UICulture = CultureInfo.CurrentUICulture;
+ MaxNodeCount = projectCollection.MaxNodeCount;
+
+ // these properties are copied, while some members (such as Loggers) are not.
+ this.DefaultToolsVersion = projectCollection.DefaultToolsVersion;
+ this.ToolsetDefinitionLocations = projectCollection.ToolsetLocations;
+ this.GlobalProperties = projectCollection.GlobalProperties;
+ environment_properties = new Dictionary<string,string> ();
+ foreach (DictionaryEntry p in Environment.GetEnvironmentVariables ())
+ environment_properties [(string) p.Key] = (string) p.Value;
+ }
+
+ readonly ProjectCollection projects;
+ Dictionary<string,string> environment_properties;
+
+ internal ProjectCollection ProjectCollection {
+ get { return projects; }
+ }
+
+ public BuildParameters Clone ()
+ {
+ var ret = (BuildParameters) MemberwiseClone ();
+ ret.ForwardingLoggers = ForwardingLoggers == null ? null : ForwardingLoggers.ToArray ();
+ ret.GlobalProperties = GlobalProperties == null ? null : GlobalProperties.ToDictionary (p => p.Key, p => p.Value);
+ ret.Loggers = Loggers == null ? null : new List<ILogger> (Loggers);
+ ret.environment_properties = new Dictionary<string, string> (environment_properties);
+ return ret;
+ }
+
+ public Toolset GetToolset (string toolsVersion)
+ {
+ // can return null.
+ return projects.Toolsets.FirstOrDefault (t => t.ToolsVersion == toolsVersion);
+ }
+
+ [MonoTODO]
+ public ThreadPriority BuildThreadPriority { get; set; }
+
+ [MonoTODO]
+ public CultureInfo Culture { get; set; }
+
+ public string DefaultToolsVersion { get; set; }
+
+ [MonoTODO]
+ public bool DetailedSummary { get; set; }
+
+ public bool EnableNodeReuse { get; set; }
+
+ [MonoTODO]
+ public IDictionary<string, string> EnvironmentProperties {
+ get { return environment_properties; }
+ }
+
+ [MonoTODO]
+ public IEnumerable<ForwardingLoggerRecord> ForwardingLoggers { get; set; }
+
+ [MonoTODO]
+ public IDictionary<string, string> GlobalProperties { get; set; }
+
+ public HostServices HostServices { get; set; }
+
+ [MonoTODO]
+ public bool LegacyThreadingSemantics { get; set; }
+
+ [MonoTODO]
+ public IEnumerable<ILogger> Loggers { get; set; }
+
+ [MonoTODO]
+ public int MaxNodeCount { get; set; }
+
+ [MonoTODO]
+ public int MemoryUseLimit { get; set; }
+
+ [MonoTODO]
+ public string NodeExeLocation { get; set; }
+
+ [MonoTODO]
+ public bool OnlyLogCriticalEvents { get; set; }
+
+ [MonoTODO]
+ public bool ResetCaches { get; set; }
+
+ [MonoTODO]
+ public bool SaveOperatingEnvironment { get; set; }
+
+ [MonoTODO]
+ public ToolsetDefinitionLocations ToolsetDefinitionLocations { get; set; }
+
+ [MonoTODO]
+ public ICollection<Toolset> Toolsets {
+ get { return projects.Toolsets; }
+ }
+
+ [MonoTODO]
+ public CultureInfo UICulture { get; set; }
+
+ [MonoTODO]
+ public bool UseSynchronousLogging { get; set; }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildRequestData.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildRequestData.cs
index 2380077e51..bd5edffd7c 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildRequestData.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildRequestData.cs
@@ -3,8 +3,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -27,50 +28,69 @@
//
using System;
+using System.Linq;
using System.Collections.Generic;
namespace Microsoft.Build.Execution
{
- public class BuildRequestData
- {
- public BuildRequestData (ProjectInstance projectInstance, string[] targetsToBuild)
- : this (projectInstance, targetsToBuild, null, BuildRequestDataFlags.None)
- {
- }
+ public class BuildRequestData
+ {
+ public BuildRequestData (ProjectInstance projectInstance, string[] targetsToBuild)
+ : this (projectInstance, targetsToBuild, null, BuildRequestDataFlags.None)
+ {
+ }
- public BuildRequestData (ProjectInstance projectInstance, string[] targetsToBuild, HostServices hostServices)
- : this (projectInstance, targetsToBuild, hostServices, BuildRequestDataFlags.None)
- {
- }
+ public BuildRequestData (ProjectInstance projectInstance, string[] targetsToBuild, HostServices hostServices)
+ : this (projectInstance, targetsToBuild, hostServices, BuildRequestDataFlags.None)
+ {
+ }
- public BuildRequestData (ProjectInstance projectInstance, string[] targetsToBuild, HostServices hostServices,
- BuildRequestDataFlags flags)
- {
- throw new NotImplementedException ();
- }
+ public BuildRequestData (ProjectInstance projectInstance, string[] targetsToBuild, HostServices hostServices,
+ BuildRequestDataFlags flags)
+ {
+ ProjectInstance = projectInstance;
+ TargetNames = targetsToBuild;
+ HostServices = hostServices;
+ Flags = flags;
+ }
- public BuildRequestData (string projectFullPath, IDictionary<string, string> globalProperties,
- string toolsVersion, string[] targetsToBuild, HostServices hostServices)
- : this (projectFullPath, globalProperties, toolsVersion, targetsToBuild, hostServices, BuildRequestDataFlags.None)
- {
- }
+ public BuildRequestData (string projectFullPath, IDictionary<string, string> globalProperties,
+ string toolsVersion, string[] targetsToBuild, HostServices hostServices)
+ : this (projectFullPath, globalProperties, toolsVersion, targetsToBuild, hostServices, BuildRequestDataFlags.None)
+ {
+ }
- public BuildRequestData (string projectFullPath, IDictionary<string, string> globalProperties,
- string toolsVersion, string[] targetsToBuild, HostServices hostServices, BuildRequestDataFlags flags)
- {
- throw new NotImplementedException ();
- }
+ public BuildRequestData (string projectFullPath, IDictionary<string, string> globalProperties,
+ string toolsVersion, string[] targetsToBuild, HostServices hostServices, BuildRequestDataFlags flags)
+ : this (new ProjectInstance (projectFullPath, globalProperties, toolsVersion), targetsToBuild, hostServices, flags)
+ {
+ ExplicitlySpecifiedToolsVersion = toolsVersion;
+ }
- public string ExplicitlySpecifiedToolsVersion { get; private set; }
- public BuildRequestDataFlags Flags { get; private set; }
- public HostServices HostServices { get; private set; }
- public string ProjectFullPath { get; private set; }
- public ProjectInstance ProjectInstance { get; private set; }
- public ICollection<string> TargetNames { get; private set; }
+ public string ExplicitlySpecifiedToolsVersion { get; private set; }
- ICollection<ProjectPropertyInstance> GlobalProperties {
- get { throw new NotImplementedException (); }
- }
- }
+ [MonoTODO ("unused")]
+ public BuildRequestDataFlags Flags { get; private set; }
+
+ [MonoTODO ("unused")]
+ public HostServices HostServices { get; private set; }
+
+ public string ProjectFullPath {
+ get { return ProjectInstance.FullPath; }
+ }
+
+ [MonoTODO ("unused")]
+ public ProjectInstance ProjectInstance { get; private set; }
+
+ [MonoTODO]
+ public IEnumerable<string> PropertiesToTransfer { get; private set; }
+
+ [MonoTODO]
+ public ICollection<string> TargetNames { get; private set; }
+
+ ICollection<ProjectPropertyInstance> GlobalProperties {
+ get { return ProjectInstance.Properties.Where (p => ProjectInstance.GlobalProperties.Any (i => i.Key == p.Name)).ToArray (); } // we can use == as it should be identical match there.
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildResult.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildResult.cs
index 7286a247d2..479b137a4f 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildResult.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildResult.cs
@@ -27,67 +27,75 @@
using System;
using System.Collections.Generic;
+using System.Linq;
namespace Microsoft.Build.Execution
{
- public class BuildResult
- {
- public void AddResultsForTarget (string target, TargetResult result)
- {
- throw new NotImplementedException ();
- }
-
- public bool HasResultsForTarget (string target)
- {
- throw new NotImplementedException ();
- }
-
- public void MergeResults (BuildResult results)
- {
- throw new NotImplementedException ();
- }
-
- public bool CircularDependency {
- get { throw new NotImplementedException (); }
- }
-
- public int ConfigurationId {
- get { throw new NotImplementedException (); }
- }
-
- public Exception Exception {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public int GlobalRequestId {
- get { throw new NotImplementedException (); }
- }
-
- public ITargetResult this [string target] {
- get { throw new NotImplementedException (); }
- }
-
- public int NodeRequestId {
- get { throw new NotImplementedException (); }
- }
-
- public BuildResultCode OverallResult {
- get { throw new NotImplementedException (); }
- }
-
- public int ParentGlobalRequestId {
- get { throw new NotImplementedException (); }
- }
-
- public IDictionary<string, TargetResult> ResultsByTarget {
- get { throw new NotImplementedException (); }
- }
-
- public int SubmissionId {
- get { throw new NotImplementedException (); }
- }
-
- }
+ public class BuildResult
+ {
+ public BuildResult ()
+ {
+ ResultsByTarget = new Dictionary<string, TargetResult> ();
+ }
+
+ public void AddResultsForTarget (string target, TargetResult result)
+ {
+ ResultsByTarget.Add (target, result);
+ }
+
+ public bool HasResultsForTarget (string target)
+ {
+ return ResultsByTarget.ContainsKey (target);
+ }
+
+ public void MergeResults (BuildResult results)
+ {
+ if (ConfigurationId != results.ConfigurationId)
+ throw new InvalidOperationException ("Argument BuildResults have inconsistent ConfigurationId.");
+ if (GlobalRequestId != results.GlobalRequestId)
+ throw new InvalidOperationException ("Argument BuildResults have inconsistent GlobalRequestId.");
+ if (NodeRequestId != results.NodeRequestId)
+ throw new InvalidOperationException ("Argument BuildResults have inconsistent NodeRequestId.");
+ if (ParentGlobalRequestId != results.ParentGlobalRequestId)
+ throw new InvalidOperationException ("Argument BuildResults have inconsistent ParentGlobalRequestId.");
+ if (SubmissionId != results.SubmissionId)
+ throw new InvalidOperationException ("Argument BuildResults have inconsistent SubmissionId.");
+
+ CircularDependency |= results.CircularDependency;
+ Exception = Exception ?? results.Exception;
+ foreach (var p in results.ResultsByTarget)
+ ResultsByTarget.Add (p.Key, p.Value);
+ }
+
+ public bool CircularDependency { get; internal set; }
+
+ public int ConfigurationId { get; internal set; }
+
+ public Exception Exception { get; set; }
+
+ public int GlobalRequestId { get; internal set; }
+
+ public ITargetResult this [string target] {
+ get { return ResultsByTarget [target]; }
+ }
+
+ public int NodeRequestId { get; internal set; }
+
+ BuildResultCode? overall_result;
+ public BuildResultCode OverallResult {
+ get {
+ if (overall_result == null)
+ throw new InvalidOperationException ("Build has not finished");
+ return overall_result.Value;
+ }
+ internal set { overall_result = value; }
+ }
+
+ public int ParentGlobalRequestId { get; internal set; }
+
+ public IDictionary<string, TargetResult> ResultsByTarget { get; private set; }
+
+ public int SubmissionId { get; internal set; }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmission.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmission.cs
index 3a8a612b93..91a9823ce8 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmission.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmission.cs
@@ -2,8 +2,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -26,15 +27,94 @@
//
using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Internal;
+using System.Collections.Generic;
namespace Microsoft.Build.Execution
{
- public class BuildSubmission
- {
- private BuildSubmission ()
- {
- throw new NotImplementedException ();
- }
- }
+ public class BuildSubmission
+ {
+ static Random rnd = new Random ();
+
+ internal BuildSubmission (BuildManager build, BuildRequestData requestData)
+ {
+ BuildManager = build;
+ this.request = requestData;
+ SubmissionId = rnd.Next ();
+ }
+
+ BuildRequestData request;
+ BuildSubmissionCompleteCallback callback;
+ bool is_started, is_completed, is_canceled;
+ ManualResetEvent wait_handle = new ManualResetEvent (true);
+
+ public object AsyncContext { get; private set; }
+ public BuildManager BuildManager { get; private set; }
+ public BuildResult BuildResult { get; set; }
+ public bool IsCompleted {
+ get { return is_completed; }
+ }
+ public int SubmissionId { get; private set; }
+ public WaitHandle WaitHandle {
+ get { return wait_handle; }
+ }
+
+ internal BuildRequestData BuildRequest {
+ get { return this.request; }
+ }
+
+ internal void Cancel ()
+ {
+ if (is_canceled)
+ throw new InvalidOperationException ("Build has already canceled");
+ is_canceled = true;
+ }
+
+ public BuildResult Execute ()
+ {
+ ExecuteAsync (null, null);
+ WaitHandle.WaitOne ();
+ return BuildResult;
+ }
+
+ internal BuildResult InternalExecute ()
+ {
+ BuildResult = new BuildResult () { SubmissionId = SubmissionId };
+ try {
+ var engine = new BuildEngine4 (this);
+ string toolsVersion = request.ExplicitlySpecifiedToolsVersion ?? request.ProjectInstance.ToolsVersion ?? BuildManager.OngoingBuildParameters.DefaultToolsVersion;
+ var outputs = new Dictionary<string,string> ();
+ engine.BuildProject (() => is_canceled, BuildResult, request.ProjectInstance, request.TargetNames, BuildManager.OngoingBuildParameters.GlobalProperties, outputs, toolsVersion);
+ } catch (Exception ex) {
+ BuildResult.Exception = ex;
+ BuildResult.OverallResult = BuildResultCode.Failure;
+ }
+ is_completed = true;
+ if (callback != null)
+ callback (this);
+ wait_handle.Set ();
+ return BuildResult;
+ }
+
+ public void ExecuteAsync (BuildSubmissionCompleteCallback callback, object context)
+ {
+ if (is_completed)
+ throw new InvalidOperationException ("Build has already completed");
+ if (is_canceled)
+ throw new InvalidOperationException ("Build has already canceled");
+ if (is_started)
+ throw new InvalidOperationException ("Build has already started");
+ is_started = true;
+ this.AsyncContext = context;
+ this.callback = callback;
+ wait_handle.Reset ();
+
+ BuildManager.BuildNodeManager.Enqueue (this);
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmissionCompleteCallback.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmissionCompleteCallback.cs
new file mode 100644
index 0000000000..fba101a2fd
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/BuildSubmissionCompleteCallback.cs
@@ -0,0 +1,5 @@
+namespace Microsoft.Build.Execution
+{
+ public delegate void BuildSubmissionCompleteCallback (BuildSubmission submission);
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/HostServices.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/HostServices.cs
index 2389bdf330..e68755affe 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/HostServices.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/HostServices.cs
@@ -26,42 +26,95 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using Microsoft.Build.Framework;
using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Framework;
namespace Microsoft.Build.Execution
{
- public class HostServices
- {
- public ITaskHost GetHostObject (string projectFile, string targetName, string taskName)
- {
- throw new NotImplementedException ();
- }
+ public class HostServices
+ {
+ class HostObjectRegistration
+ {
+ public string ProjectFile { get; set; }
+ public string TargetName { get; set; }
+ public string TaskName { get; set; }
+ public ITaskHost HostObject { get; set; }
+ }
+
+ readonly List<HostObjectRegistration> hosts = new List<HostObjectRegistration> ();
+ readonly Dictionary<string,NodeAffinity> node_affinities = new Dictionary<string, NodeAffinity> ();
+
+ HostObjectRegistration GetHostRegistration (string projectFile, string targetName, string taskName)
+ {
+ if (projectFile == null)
+ throw new ArgumentNullException ("projectFile");
+ if (targetName == null)
+ throw new ArgumentNullException ("targetName");
+ if (taskName == null)
+ throw new ArgumentNullException ("taskName");
+ return hosts.FirstOrDefault (h =>
+ string.Equals (projectFile, h.ProjectFile, StringComparison.OrdinalIgnoreCase) &&
+ string.Equals (targetName, h.TargetName, StringComparison.OrdinalIgnoreCase) &&
+ string.Equals (taskName, h.TaskName, StringComparison.OrdinalIgnoreCase));
+ }
+
+ public ITaskHost GetHostObject (string projectFile, string targetName, string taskName)
+ {
+ var reg = GetHostRegistration (projectFile, targetName, taskName);
+ return reg != null ? reg.HostObject : null;
+ }
- public NodeAffinity GetNodeAffinity (string projectFile)
- {
- throw new NotImplementedException ();
- }
+ public NodeAffinity GetNodeAffinity (string projectFile)
+ {
+ if (projectFile == null)
+ throw new ArgumentNullException ("projectFile");
+ NodeAffinity na;
+ return node_affinities.TryGetValue (projectFile, out na) ? na : NodeAffinity.Any;
+ }
+
+ IEnumerable<HostObjectRegistration> GetRegistrationsByProject (string project)
+ {
+ return hosts.Where (h => string.Equals (project, h.ProjectFile, StringComparison.OrdinalIgnoreCase));
+ }
- public void OnRenameProject (string oldFullPath, string newFullPath)
- {
- throw new NotImplementedException ();
- }
+ public void OnRenameProject (string oldFullPath, string newFullPath)
+ {
+ if (oldFullPath == null)
+ throw new ArgumentNullException ("oldFullPath");
+ if (newFullPath == null)
+ throw new ArgumentNullException ("newFullPath");
+ foreach (var reg in GetRegistrationsByProject (oldFullPath))
+ reg.ProjectFile = newFullPath;
+ }
- public void RegisterHostObject (string projectFile, string targetName, string taskName, ITaskHost hostObject)
- {
- throw new NotImplementedException ();
- }
+ public void RegisterHostObject (string projectFile, string targetName, string taskName, ITaskHost hostObject)
+ {
+ if (hostObject == null)
+ throw new ArgumentNullException ("hostObject");
+ var reg = GetHostRegistration (projectFile, targetName, taskName);
+ if (reg != null)
+ reg.HostObject = hostObject;
+ else
+ hosts.Add (new HostObjectRegistration () { ProjectFile = projectFile, TargetName = targetName, TaskName = taskName, HostObject = hostObject });
+ }
- public void SetNodeAffinity (string projectFile, NodeAffinity nodeAffinity)
- {
- throw new NotImplementedException ();
- }
+ public void SetNodeAffinity (string projectFile, NodeAffinity nodeAffinity)
+ {
+ if (projectFile == null)
+ throw new ArgumentNullException ("projectFile");
+ node_affinities [projectFile] = nodeAffinity;
+ }
- public void UnregisterProject (string projectFullPath)
- {
- throw new NotImplementedException ();
- }
- }
+ public void UnregisterProject (string projectFullPath)
+ {
+ if (projectFullPath == null)
+ throw new ArgumentNullException ("projectFullPath");
+ var removed = GetRegistrationsByProject (projectFullPath).ToArray ();
+ foreach (var r in removed)
+ hosts.Remove (r);
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/WriteHandler.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/NodeEngineShutdownReason.cs
index 29ce75c414..b88423f2a8 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/WriteHandler.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/NodeEngineShutdownReason.cs
@@ -1,9 +1,10 @@
-// WriteHandler.cs
+//
+// NodeEngineShutdownReason.cs
//
// Author:
-// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -15,7 +16,7 @@
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
-//
+//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -24,9 +25,23 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Build.BuildEngine;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Evaluation;
+using System.Linq;
+using System.IO;
-namespace Microsoft.Build.Logging
+namespace Microsoft.Build.Internal
{
- public delegate void WriteHandler (string message);
+ public enum NodeEngineShutdownReason
+ {
+ BuildComplete,
+ BuildCompleteReuse,
+ ConnectionFailed,
+ Error,
+ }
}
-
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/OutOfProcNode.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/OutOfProcNode.cs
new file mode 100644
index 0000000000..7237052d19
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/OutOfProcNode.cs
@@ -0,0 +1,48 @@
+//
+// OutOfProcNode.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Build.BuildEngine;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Evaluation;
+using System.Linq;
+using System.IO;
+
+namespace Microsoft.Build.Internal
+{
+ // from MSDN: this class has deprecated and there is no alternative.
+ public class OutOfProcNode
+ {
+ public NodeEngineShutdownReason Run (out Exception shutdownException)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectInstance.cs
index 4661496222..91ce68a040 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectInstance.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectInstance.cs
@@ -3,8 +3,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -27,13 +28,34 @@
//
using System;
+using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
+using Microsoft.Build.Internal.Expressions;
using Microsoft.Build.Logging;
+//
+// It is not always consistent to reuse Project and its evaluation stuff mostly because
+// both BuildParameters.ctor() and Project.ctor() takes arbitrary ProjectCollection, which are not very likely eqivalent
+// (as BuildParameters.ctor(), unlike Project.ctor(...), is known to create a new ProjectCollection instance).
+//
+// However, that inconsistency could happen even if you only use ProjectInstance and BuildParameters.
+// They both have constructors that take ProjectCollection and there is no guarantee that the arguments are the same.
+// BuildManager.Build() does not fail because of inconsistent ProjectCollection instance on .NET.
+//
+// Anyhow, I'm not going to instantiate Project within ProjectInstance code for another reason:
+// ProjectCollection.GetLoadedProject() does not return any Project instnace for corresponding ProjectInstance
+// (or I should say, ProjectRootElement for both).
+using Microsoft.Build.Internal;
+using System.Xml;
+using Microsoft.Build.Exceptions;
+using System.IO;
+
+
namespace Microsoft.Build.Execution
{
public class ProjectInstance
@@ -59,35 +81,210 @@ namespace Microsoft.Build.Execution
public ProjectInstance (ProjectRootElement xml, IDictionary<string, string> globalProperties,
string toolsVersion, ProjectCollection projectCollection)
{
- InitializeProperties ();
-
- throw new NotImplementedException ();
+ projects = projectCollection;
+ global_properties = globalProperties ?? new Dictionary<string, string> ();
+ tools_version = !string.IsNullOrEmpty (toolsVersion) ? toolsVersion :
+ !string.IsNullOrEmpty (xml.ToolsVersion) ? xml.ToolsVersion :
+ projects.DefaultToolsVersion;
+ InitializeProperties (xml, null);
}
public ProjectInstance (string projectFile, IDictionary<string, string> globalProperties,
string toolsVersion, ProjectCollection projectCollection)
+ : this (ProjectRootElement.Create (projectFile), globalProperties, toolsVersion, projectCollection)
+ {
+ }
+
+ ProjectCollection projects;
+ IDictionary<string, string> global_properties;
+
+ string full_path, directory;
+ #if NET_4_5
+ ElementLocation location;
+ #endif
+
+ Dictionary<string, ProjectItemDefinitionInstance> item_definitions;
+ List<ResolvedImport> raw_imports; // maybe we don't need this...
+ List<ProjectItemInstance> all_evaluated_items;
+ List<ProjectItemInstance> raw_items;
+ List<ProjectPropertyInstance> properties;
+ Dictionary<string, ProjectTargetInstance> targets;
+ string tools_version;
+
+ List<string> GetDefaultTargets (ProjectRootElement xml)
{
- InitializeProperties ();
+ var ret = xml.DefaultTargets.Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries).Select (s => s.Trim ()).ToList ();
+ if (ret.Count == 0 && xml.Targets.Any ())
+ ret.Add (xml.Targets.First ().Name);
+ return ret;
+ }
+
+ void InitializeProperties (ProjectRootElement xml, ProjectInstance parent)
+ {
+ #if NET_4_5
+ location = xml.Location;
+ #endif
+ full_path = xml.FullPath;
+ directory = string.IsNullOrWhiteSpace (xml.DirectoryPath) ? System.IO.Directory.GetCurrentDirectory () : xml.DirectoryPath;
+ DefaultTargets = GetDefaultTargets (xml);
+ InitialTargets = xml.InitialTargets.Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries).Select (s => s.Trim ()).ToList ();
+
+ raw_imports = new List<ResolvedImport> ();
+ item_definitions = new Dictionary<string, ProjectItemDefinitionInstance> ();
+ targets = new Dictionary<string, ProjectTargetInstance> ();
+ raw_items = new List<ProjectItemInstance> ();
- throw new NotImplementedException ();
+ // FIXME: this is likely hack. Test ImportedProject.Properties to see what exactly should happen.
+ if (parent != null) {
+ properties = parent.properties;
+ } else {
+ properties = new List<ProjectPropertyInstance> ();
+
+ foreach (DictionaryEntry p in Environment.GetEnvironmentVariables ())
+ // FIXME: this is kind of workaround for unavoidable issue that PLATFORM=* is actually given
+ // on some platforms and that prevents setting default "PLATFORM=AnyCPU" property.
+ if (!string.Equals ("PLATFORM", (string) p.Key, StringComparison.OrdinalIgnoreCase))
+ this.properties.Add (new ProjectPropertyInstance ((string) p.Key, false, (string) p.Value));
+ foreach (var p in global_properties)
+ this.properties.Add (new ProjectPropertyInstance (p.Key, false, p.Value));
+ var tools = projects.GetToolset (tools_version) ?? projects.GetToolset (projects.DefaultToolsVersion);
+ foreach (var p in projects.GetReservedProperties (tools, this, xml))
+ this.properties.Add (p);
+ foreach (var p in ProjectCollection.GetWellKnownProperties (this))
+ this.properties.Add (p);
+ }
+
+ ProcessXml (parent, xml);
}
- void InitializeProperties ()
+ static readonly char [] item_target_sep = {';'};
+
+ void ProcessXml (ProjectInstance parent, ProjectRootElement xml)
{
- DefaultTargets = new List<string> ();
- InitialTargets = new List<string> ();
+ TaskDatabase = new BuildTaskDatabase (this, xml);
+
+ // this needs to be initialized here (regardless of that items won't be evaluated at property evaluation;
+ // Conditions could incorrectly reference items and lack of this list causes NRE.
+ all_evaluated_items = new List<ProjectItemInstance> ();
+
+ // property evaluation happens couple of times.
+ // At first step, all non-imported properties are evaluated TOO, WHILE those properties are being evaluated.
+ // This means, Include and IncludeGroup elements with Condition attribute MAY contain references to
+ // properties and they will be expanded.
+ var elements = EvaluatePropertiesAndImports (xml.Children).ToArray (); // ToArray(): to not lazily evaluate elements.
+
+ // next, evaluate items
+ EvaluateItems (xml, elements);
+
+ // finally, evaluate targets and tasks
+ EvaluateTasks (elements);
}
- Dictionary<string, string> global_properties = new Dictionary<string, string> ();
+ IEnumerable<ProjectElement> EvaluatePropertiesAndImports (IEnumerable<ProjectElement> elements)
+ {
+ // First step: evaluate Properties
+ foreach (var child in elements) {
+ yield return child;
+ var pge = child as ProjectPropertyGroupElement;
+ if (pge != null && EvaluateCondition (pge.Condition))
+ foreach (var p in pge.Properties)
+ // do not allow overwriting reserved or well-known properties by user
+ if (!this.properties.Any (_ => (_.IsImmutable) && _.Name.Equals (p.Name, StringComparison.InvariantCultureIgnoreCase)))
+ if (EvaluateCondition (p.Condition))
+ this.properties.Add (new ProjectPropertyInstance (p.Name, false, ExpandString (p.Value)));
+
+ var ige = child as ProjectImportGroupElement;
+ if (ige != null && EvaluateCondition (ige.Condition)) {
+ foreach (var incc in ige.Imports) {
+ foreach (var e in Import (incc))
+ yield return e;
+ }
+ }
+ var inc = child as ProjectImportElement;
+ if (inc != null && EvaluateCondition (inc.Condition))
+ foreach (var e in Import (inc))
+ yield return e;
+ }
+ }
+ internal IEnumerable<T> GetAllItems<T> (string include, string exclude, Func<string,T> creator, Func<string,ITaskItem> taskItemCreator, Func<string,bool> itemTypeCheck, Action<T,string> assignRecurse)
+ {
+ return ProjectCollection.GetAllItems<T> (ExpandString, include, exclude, creator, taskItemCreator, Directory, assignRecurse,
+ t => all_evaluated_items.Any (i => i.EvaluatedInclude == t.ItemSpec && itemTypeCheck (i.ItemType)));
+ }
+
+ void EvaluateItems (ProjectRootElement xml, IEnumerable<ProjectElement> elements)
+ {
+ foreach (var child in elements) {
+ var ige = child as ProjectItemGroupElement;
+ if (ige != null) {
+ foreach (var p in ige.Items) {
+ if (!EvaluateCondition (ige.Condition) || !EvaluateCondition (p.Condition))
+ continue;
+ Func<string,ProjectItemInstance> creator = s => new ProjectItemInstance (this, p.ItemType, p.Metadata.Select (m => new KeyValuePair<string,string> (m.Name, m.Value)).ToList (), s);
+ foreach (var item in GetAllItems (p.Include, p.Exclude, creator, s => new ProjectTaskItem (p, s), it => string.Equals (it, p.ItemType, StringComparison.OrdinalIgnoreCase), (t, s) => t.RecursiveDir = s)) {
+ raw_items.Add (item);
+ all_evaluated_items.Add (item);
+ }
+ }
+ }
+ var def = child as ProjectItemDefinitionGroupElement;
+ if (def != null) {
+ foreach (var p in def.ItemDefinitions) {
+ if (EvaluateCondition (p.Condition)) {
+ ProjectItemDefinitionInstance existing;
+ if (!item_definitions.TryGetValue (p.ItemType, out existing))
+ item_definitions.Add (p.ItemType, (existing = new ProjectItemDefinitionInstance (p)));
+ existing.AddItems (p);
+ }
+ }
+ }
+ }
+ all_evaluated_items.Sort ((p1, p2) => string.Compare (p1.ItemType, p2.ItemType, StringComparison.OrdinalIgnoreCase));
+ }
+
+ void EvaluateTasks (IEnumerable<ProjectElement> elements)
+ {
+ foreach (var child in elements) {
+ var te = child as ProjectTargetElement;
+ if (te != null)
+ this.targets.Add (te.Name, new ProjectTargetInstance (te));
+ }
+ }
+
+ IEnumerable<ProjectElement> Import (ProjectImportElement import)
+ {
+ string dir = projects.GetEvaluationTimeThisFileDirectory (() => FullPath);
+ string path = WindowsCompatibilityExtensions.NormalizeFilePath (ExpandString (import.Project));
+ path = Path.IsPathRooted (path) ? path : dir != null ? Path.Combine (dir, path) : Path.GetFullPath (path);
+ if (projects.OngoingImports.Contains (path))
+ throw new InvalidProjectFileException (import.Location, null, string.Format ("Circular imports was detected: {0} is already on \"importing\" stack", path));
+ projects.OngoingImports.Push (path);
+ try {
+ using (var reader = XmlReader.Create (path)) {
+ var root = ProjectRootElement.Create (reader, projects);
+ if (DefaultTargets.Count == 0)
+ DefaultTargets.AddRange (GetDefaultTargets (root));
+ raw_imports.Add (new ResolvedImport (import, root, true));
+ return this.EvaluatePropertiesAndImports (root.Children).ToArray ();
+ }
+ } finally {
+ projects.OngoingImports.Pop ();
+ }
+ }
+
+ internal IEnumerable<ProjectItemInstance> AllEvaluatedItems {
+ get { return all_evaluated_items; }
+ }
+
public List<string> DefaultTargets { get; private set; }
public string Directory {
- get { throw new NotImplementedException (); }
+ get { return directory; }
}
public string FullPath {
- get { throw new NotImplementedException (); }
+ get { return full_path; }
}
public IDictionary<string, string> GlobalProperties {
@@ -103,33 +300,33 @@ namespace Microsoft.Build.Execution
#endif
public IDictionary<string, ProjectItemDefinitionInstance> ItemDefinitions {
- get { throw new NotImplementedException (); }
+ get { return item_definitions; }
}
public ICollection<ProjectItemInstance> Items {
- get { throw new NotImplementedException (); }
+ get { return all_evaluated_items; }
}
public ICollection<string> ItemTypes {
- get { throw new NotImplementedException (); }
+ get { return all_evaluated_items.Select (i => i.ItemType).Distinct ().ToArray (); }
}
#if NET_4_5
public ElementLocation ProjectFileLocation {
- get { throw new NotImplementedException (); }
+ get { return location; }
}
#endif
public ICollection<ProjectPropertyInstance> Properties {
- get { throw new NotImplementedException (); }
+ get { return properties; }
}
public IDictionary<string, ProjectTargetInstance> Targets {
- get { throw new NotImplementedException (); }
+ get { return targets; }
}
public string ToolsVersion {
- get { throw new NotImplementedException (); }
+ get { return tools_version; }
}
public ProjectItemInstance AddItem (string itemType, string evaluatedInclude)
@@ -139,7 +336,10 @@ namespace Microsoft.Build.Execution
public ProjectItemInstance AddItem (string itemType, string evaluatedInclude, IEnumerable<KeyValuePair<string, string>> metadata)
{
- throw new NotImplementedException ();
+ var item = new ProjectItemInstance (this, itemType, metadata, evaluatedInclude);
+ raw_items.Add (item);
+ all_evaluated_items.Add (item);
+ return item;
}
public bool Build ()
@@ -154,7 +354,7 @@ namespace Microsoft.Build.Execution
public bool Build (IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers)
{
- return Build ((string []) null, loggers, remoteLoggers);
+ return Build (DefaultTargets.ToArray (), loggers, remoteLoggers);
}
public bool Build (string target, IEnumerable<ILogger> loggers)
@@ -180,12 +380,20 @@ namespace Microsoft.Build.Execution
public bool Build (string[] targets, IEnumerable<ILogger> loggers, out IDictionary<string, TargetResult> targetOutputs)
{
- return Build (targets, loggers, new ForwardingLoggerRecord [0], out targetOutputs);
+ return Build (targets, loggers, new ForwardingLoggerRecord [0], out targetOutputs);
}
public bool Build (string[] targets, IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers, out IDictionary<string, TargetResult> targetOutputs)
{
- throw new NotImplementedException ();
+ var manager = new BuildManager ();
+ var parameters = new BuildParameters (projects) {
+ ForwardingLoggers = remoteLoggers,
+ Loggers = loggers,
+ };
+ var requestData = new BuildRequestData (this, targets);
+ var result = manager.Build (parameters, requestData);
+ targetOutputs = result.ResultsByTarget;
+ return result.OverallResult == BuildResultCode.Success;
}
public ProjectInstance DeepCopy ()
@@ -200,17 +408,22 @@ namespace Microsoft.Build.Execution
public bool EvaluateCondition (string condition)
{
- throw new NotImplementedException ();
+ return string.IsNullOrWhiteSpace (condition) || new ExpressionEvaluator (this, null).EvaluateAsBoolean (condition);
}
public string ExpandString (string unexpandedValue)
{
- throw new NotImplementedException ();
+ return ExpandString (unexpandedValue, null);
+ }
+
+ string ExpandString (string unexpandedValue, string replacementForMissingStuff)
+ {
+ return new ExpressionEvaluator (this, replacementForMissingStuff).Evaluate (unexpandedValue);
}
public ICollection<ProjectItemInstance> GetItems (string itemType)
{
- throw new NotImplementedException ();
+ return new CollectionFromEnumerable<ProjectItemInstance> (Items.Where (p => p.ItemType.Equals (itemType, StringComparison.OrdinalIgnoreCase)));
}
public IEnumerable<ProjectItemInstance> GetItemsByItemTypeAndEvaluatedInclude (string itemType, string evaluatedInclude)
@@ -220,27 +433,36 @@ namespace Microsoft.Build.Execution
public ProjectPropertyInstance GetProperty (string name)
{
- throw new NotImplementedException ();
+ return properties.FirstOrDefault (p => p.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
}
public string GetPropertyValue (string name)
{
- throw new NotImplementedException ();
+ var prop = GetProperty (name);
+ return prop != null ? prop.EvaluatedValue : string.Empty;
}
public bool RemoveItem (ProjectItemInstance item)
{
- throw new NotImplementedException ();
+ // yeah, this raw_items should vanish...
+ raw_items.Remove (item);
+ return all_evaluated_items.Remove (item);
}
public bool RemoveProperty (string name)
{
- throw new NotImplementedException ();
+ var removed = properties.FirstOrDefault (p => p.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ if (removed == null)
+ return false;
+ properties.Remove (removed);
+ return true;
}
public ProjectPropertyInstance SetProperty (string name, string evaluatedValue)
{
- throw new NotImplementedException ();
+ var p = new ProjectPropertyInstance (name, false, evaluatedValue);
+ properties.Add (p);
+ return p;
}
public ProjectRootElement ToProjectRootElement ()
@@ -259,32 +481,46 @@ namespace Microsoft.Build.Execution
public static string GetEvaluatedItemIncludeEscaped (ProjectItemDefinitionInstance item)
{
+ // ?? ItemDefinition does not have Include attribute. What's the point here?
throw new NotImplementedException ();
}
public static string GetEvaluatedItemIncludeEscaped (ProjectItemInstance item)
{
- throw new NotImplementedException ();
+ return ProjectCollection.Escape (item.EvaluatedInclude);
}
public static string GetMetadataValueEscaped (ProjectMetadataInstance metadatum)
{
- throw new NotImplementedException ();
+ return ProjectCollection.Escape (metadatum.EvaluatedValue);
}
public static string GetMetadataValueEscaped (ProjectItemDefinitionInstance item, string name)
{
- throw new NotImplementedException ();
+ var md = item.Metadata.FirstOrDefault (m => m.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
}
public static string GetMetadataValueEscaped (ProjectItemInstance item, string name)
{
- throw new NotImplementedException ();
+ var md = item.Metadata.FirstOrDefault (m => m.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ return md != null ? ProjectCollection.Escape (md.EvaluatedValue) : null;
}
public static string GetPropertyValueEscaped (ProjectPropertyInstance property)
{
- throw new NotImplementedException ();
+ // WTF happens here.
+ //return ProjectCollection.Escape (property.EvaluatedValue);
+ return property.EvaluatedValue;
+ }
+
+ internal BuildTaskDatabase TaskDatabase { get; private set; }
+
+ internal string GetFullPath (string pathRelativeToProject)
+ {
+ if (Path.IsPathRooted (pathRelativeToProject))
+ return pathRelativeToProject;
+ return Path.GetFullPath (Path.Combine (Directory, pathRelativeToProject));
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemDefinitionInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemDefinitionInstance.cs
index 4641a951ce..fb10ceec17 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemDefinitionInstance.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemDefinitionInstance.cs
@@ -4,7 +4,7 @@
// Author:
// Atsushi Enomoto (atsushi@veritas-vos-liberabit.com)
//
-// Copyright (C) 2012 Xamarin Inc.
+// Copyright (C) 2012,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -29,29 +29,43 @@
using Microsoft.Build.Framework;
using System;
using System.Collections.Generic;
+using Microsoft.Build.Construction;
+using System.Linq;
namespace Microsoft.Build.Execution
{
public class ProjectItemDefinitionInstance
{
- internal ProjectItemDefinitionInstance ()
+ internal ProjectItemDefinitionInstance (ProjectItemDefinitionElement xml)
{
+ ItemType = xml.ItemType;
+ AddItems (xml);
}
- public string ItemType {
- get { throw new NotImplementedException (); }
- }
+ List<ProjectMetadataInstance> metadata = new List<ProjectMetadataInstance> ();
+
+ public string ItemType { get; private set; }
public ICollection<ProjectMetadataInstance> Metadata {
- get { throw new NotImplementedException (); }
+ get { return metadata; }
}
public int MetadataCount {
- get { throw new NotImplementedException (); }
+ get { return metadata.Count; }
}
public IEnumerable<string> MetadataNames {
- get { throw new NotImplementedException (); }
+ get { return metadata.Select (m => m.Name).ToArray (); }
+ }
+
+ internal void AddItems (ProjectItemDefinitionElement xml)
+ {
+ foreach (var item in xml.Metadata) {
+ var existing = metadata.FirstOrDefault (i => i.Name == item.Name);
+ if (existing != null)
+ metadata.Remove (existing);
+ metadata.Add (new ProjectMetadataInstance (item.Name, item.Value));
+ }
}
}
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskInstance.cs
new file mode 100644
index 0000000000..bf9d03f05f
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskInstance.cs
@@ -0,0 +1,82 @@
+//
+// ProjectItemGroupTaskInstance.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public sealed class ProjectItemGroupTaskInstance : ProjectTargetInstanceChild
+ {
+ internal ProjectItemGroupTaskInstance (ProjectItemGroupElement xml)
+ {
+ condition = xml.Condition;
+ condition_location = xml.ConditionLocation;
+ //this.FullPath = fullPath;
+ location = xml.Location;
+
+ Items = xml.Items.Select (item => new ProjectItemGroupTaskItemInstance (item)).ToArray ();
+ }
+
+ readonly string condition;
+ readonly ElementLocation condition_location, location;
+
+ public override string Condition {
+ get { return condition; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation ConditionLocation {
+ get { return condition_location; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation Location {
+ get { return location; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ ElementLocation ExecuteTargetsLocation { get; private set; }
+
+ public ICollection<ProjectItemGroupTaskItemInstance> Items { get; private set; }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskItemInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskItemInstance.cs
new file mode 100644
index 0000000000..8d947b72ba
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskItemInstance.cs
@@ -0,0 +1,95 @@
+//
+// ProjectItemGroupTaskItemInstance.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using Microsoft.Build.Construction;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Build.Execution
+{
+ public class ProjectItemGroupTaskItemInstance
+ {
+ internal ProjectItemGroupTaskItemInstance (ProjectItemElement xml)
+ {
+ Condition = xml.Condition;
+ Exclude = xml.Exclude;
+ Include = xml.Include;
+ ItemType = xml.ItemType;
+ Metadata = xml.Metadata.Select (m => new ProjectItemGroupTaskMetadataInstance (m)).ToArray ();
+ Remove = xml.Remove;
+ #if NET_4_5
+ KeepDuplicates = xml.KeepDuplicates;
+ KeepMetadata = xml.KeepMetadata;
+ RemoveMetadata = xml.RemoveMetadata;
+
+ ConditionLocation = xml.ConditionLocation;
+ ExcludeLocation = xml.ExcludeLocation;
+ IncludeLocation = xml.IncludeLocation;
+ Location = xml.Location;
+ KeepDuplicatesLocation = xml.KeepDuplicatesLocation;
+ RemoveLocation = xml.RemoveLocation;
+ RemoveMetadataLocation = xml.RemoveMetadataLocation;
+ #endif
+ }
+
+ public string Condition { get; private set; }
+
+ public string Exclude { get; private set; }
+
+ public string Include { get; private set; }
+
+ public string ItemType { get; private set; }
+
+ public string KeepDuplicates { get; private set; }
+
+ public string KeepMetadata { get; private set; }
+
+ public ICollection<ProjectItemGroupTaskMetadataInstance> Metadata { get; private set; }
+
+ public string Remove { get; private set; }
+
+ public string RemoveMetadata { get; private set; }
+ #if NET_4_5
+ public ElementLocation ConditionLocation { get; private set; }
+
+ public ElementLocation ExcludeLocation { get; private set; }
+
+ public ElementLocation IncludeLocation { get; private set; }
+
+ public ElementLocation KeepDuplicatesLocation { get; private set; }
+
+ public ElementLocation KeepMetadataLocation { get; private set; }
+
+ public ElementLocation Location { get; private set; }
+
+ public ElementLocation RemoveLocation { get; private set; }
+
+ public ElementLocation RemoveMetadataLocation { get; private set; }
+ #endif
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskMetadataInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskMetadataInstance.cs
new file mode 100644
index 0000000000..ad871731c5
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemGroupTaskMetadataInstance.cs
@@ -0,0 +1,58 @@
+//
+// ProjectItemGroupTaskMetadataInstance.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public sealed class ProjectItemGroupTaskMetadataInstance
+ {
+ internal ProjectItemGroupTaskMetadataInstance (ProjectMetadataElement xml)
+ {
+ Condition = xml.Condition;
+ Name = xml.Name;
+ Value = xml.Value;
+ #if NET_4_5
+ ConditionLocation = xml.ConditionLocation;
+ Location = xml.Location;
+ #endif
+ }
+ public string Condition { get; private set; }
+
+ public string Name { get; private set; }
+
+ public string Value { get; private set; }
+ #if NET_4_5
+ public ElementLocation ConditionLocation { get; private set; }
+
+ public ElementLocation Location { get; private set; }
+ #endif
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemInstance.cs
index 0e27811ef8..720c4b32b1 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemInstance.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectItemInstance.cs
@@ -3,8 +3,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -26,152 +27,191 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using Microsoft.Build.Framework;
using System;
using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Evaluation;
+using System.Collections;
+using Microsoft.Build.Construction;
+using System.Globalization;
+using System.IO;
namespace Microsoft.Build.Execution
{
- public class ProjectItemInstance
+ public class ProjectItemInstance
: ITaskItem2
- {
- private ProjectItemInstance ()
- {
- throw new NotImplementedException ();
- }
-
- public ProjectMetadataInstance GetMetadata (string name)
- {
- throw new NotImplementedException ();
- }
-
- public string GetMetadataValue (string name)
- {
- throw new NotImplementedException ();
- }
-
- public bool HasMetadata (string name)
- {
- throw new NotImplementedException ();
- }
-
- public void RemoveMetadata (string metadataName)
- {
- throw new NotImplementedException ();
- }
-
- public void SetMetadata (IEnumerable<KeyValuePair<string, string>> metadataDictionary)
- {
- throw new NotImplementedException ();
- }
-
- public ProjectMetadataInstance SetMetadata (string name, string evaluatedValue)
- {
- throw new NotImplementedException ();
- }
-
- public int DirectMetadataCount {
- get { throw new NotImplementedException (); }
- }
-
- public string EvaluatedInclude {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
-
- public string ItemType {
- get { throw new NotImplementedException (); }
- }
-
- public IEnumerable<ProjectMetadataInstance> Metadata {
- get { throw new NotImplementedException (); }
- }
-
- public int MetadataCount {
- get { throw new NotImplementedException (); }
- }
-
- public ICollection<string> MetadataNames {
- get { throw new NotImplementedException (); }
- }
-
- public ProjectInstance Project {
- get { throw new NotImplementedException (); }
- }
-
- #region ITaskItem2 implementation
- string ITaskItem2.GetMetadataValueEscaped (string metadataName)
- {
- throw new NotImplementedException ();
- }
-
- void ITaskItem2.SetMetadataValueLiteral (string metadataName, string metadataValue)
- {
- throw new NotImplementedException ();
- }
-
- System.Collections.IDictionary ITaskItem2.CloneCustomMetadataEscaped ()
- {
- throw new NotImplementedException ();
- }
-
- string ITaskItem2.EvaluatedIncludeEscaped {
- get {
- throw new NotImplementedException ();
- }
- set {
- throw new NotImplementedException ();
- }
- }
- #endregion
-
- #region ITaskItem implementation
- System.Collections.IDictionary ITaskItem.CloneCustomMetadata ()
- {
- throw new NotImplementedException ();
- }
-
- void ITaskItem.CopyMetadataTo (ITaskItem destinationItem)
- {
- throw new NotImplementedException ();
- }
-
- string ITaskItem.GetMetadata (string metadataName)
- {
- throw new NotImplementedException ();
- }
-
- void ITaskItem.RemoveMetadata (string metadataName)
- {
- throw new NotImplementedException ();
- }
-
- void ITaskItem.SetMetadata (string metadataName, string metadataValue)
- {
- throw new NotImplementedException ();
- }
-
- string ITaskItem.ItemSpec {
- get {
- throw new NotImplementedException ();
- }
- set {
- throw new NotImplementedException ();
- }
- }
-
- int ITaskItem.MetadataCount {
- get {
- throw new NotImplementedException ();
- }
- }
-
- System.Collections.ICollection ITaskItem.MetadataNames {
- get {
- throw new NotImplementedException ();
- }
- }
- #endregion
- }
+ {
+ internal ProjectItemInstance (ProjectInstance project, string itemType, IEnumerable<KeyValuePair<string,string>> metadata, string evaluatedInclude)
+ {
+ this.project = project;
+ this.evaluated_include = evaluatedInclude;
+ this.item_type = itemType;
+ this.metadata = new List<ProjectMetadataInstance> ();
+ SetMetadata (metadata);
+ }
+
+ readonly ProjectInstance project;
+ readonly string item_type;
+ string evaluated_include;
+ readonly List<ProjectMetadataInstance> metadata;
+
+ public ProjectMetadataInstance GetMetadata (string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+ // This does not return any Well Known metadata
+ return Metadata.FirstOrDefault (m => m.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ }
+
+ public string GetMetadataValue (string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException ("name");
+ var wk = ProjectCollection.GetWellKnownMetadata (name, EvaluatedInclude, project.GetFullPath, RecursiveDir);
+ if (wk != null)
+ return wk;
+ var m = GetMetadata (name);
+ return m != null ? m.EvaluatedValue : null;
+ }
+
+ public bool HasMetadata (string name)
+ {
+ return GetMetadata (name) != null;
+ }
+
+ public void RemoveMetadata (string metadataName)
+ {
+ var m = GetMetadata (metadataName);
+ if (m != null)
+ metadata.Remove (m);
+ }
+
+ public void SetMetadata (IEnumerable<KeyValuePair<string, string>> metadataDictionary)
+ {
+ foreach (var p in metadataDictionary)
+ SetMetadata (p.Key, p.Value);
+ }
+
+ public ProjectMetadataInstance SetMetadata (string name, string evaluatedValue)
+ {
+ var m = metadata.FirstOrDefault (_ => _.Name.Equals (name, StringComparison.OrdinalIgnoreCase));
+ if (m != null)
+ metadata.Remove (m);
+ m = new ProjectMetadataInstance (name, evaluatedValue);
+ metadata.Add (m);
+ return m;
+ }
+
+ public int DirectMetadataCount {
+ get { throw new NotImplementedException (); }
+ }
+
+ public string EvaluatedInclude {
+ get { return evaluated_include; }
+ set {
+ if (value == null)
+ throw new ArgumentNullException ("value");
+ evaluated_include = value;
+ }
+ }
+
+ public string ItemType {
+ get { return item_type; }
+ }
+
+ public IEnumerable<ProjectMetadataInstance> Metadata {
+ get { return metadata; }
+ }
+
+ public int MetadataCount {
+ get { return metadata.Count; }
+ }
+
+ public ICollection<string> MetadataNames {
+ get { return metadata.Select (m => m.Name).ToArray (); }
+ }
+
+ public ProjectInstance Project {
+ get { return project; }
+ }
+
+ internal string RecursiveDir { get; set; }
+
+ #region ITaskItem2 implementation
+
+ string ITaskItem2.GetMetadataValueEscaped (string metadataName)
+ {
+ return ProjectCollection.Escape (GetMetadataValue (metadataName));
+ }
+
+ void ITaskItem2.SetMetadataValueLiteral (string metadataName, string metadataValue)
+ {
+ SetMetadata (metadataName, metadataValue);
+ }
+
+ System.Collections.IDictionary ITaskItem2.CloneCustomMetadataEscaped ()
+ {
+ var dic = ((ITaskItem) this).CloneCustomMetadata ();
+ foreach (DictionaryEntry p in dic)
+ dic [p.Key] = ProjectCollection.Escape ((string) p.Value);
+ return dic;
+ }
+
+ string ITaskItem2.EvaluatedIncludeEscaped {
+ get { return ProjectCollection.Escape (EvaluatedInclude); }
+ set { EvaluatedInclude = ProjectCollection.Unescape (value); }
+ }
+
+ #endregion
+
+ #region ITaskItem implementation
+
+ IDictionary ITaskItem.CloneCustomMetadata ()
+ {
+ var dic = new Hashtable ();
+ foreach (var md in Metadata)
+ dic [md.Name] = md.EvaluatedValue;
+ return dic;
+ }
+
+ void ITaskItem.CopyMetadataTo (ITaskItem destinationItem)
+ {
+ if (destinationItem == null)
+ throw new ArgumentNullException ("destinationItem");
+ foreach (var md in Metadata)
+ destinationItem.SetMetadata (md.Name, md.EvaluatedValue);
+ }
+
+ string ITaskItem.GetMetadata (string metadataName)
+ {
+ return GetMetadataValue (metadataName);
+ }
+
+ void ITaskItem.RemoveMetadata (string metadataName)
+ {
+ RemoveMetadata (metadataName);
+ }
+
+ void ITaskItem.SetMetadata (string metadataName, string metadataValue)
+ {
+ SetMetadata (metadataName, ProjectCollection.Unescape (metadataValue));
+ }
+
+ string ITaskItem.ItemSpec {
+ get { return EvaluatedInclude; }
+ set { EvaluatedInclude = value; }
+ }
+
+ int ITaskItem.MetadataCount {
+ get { return MetadataCount; }
+ }
+
+ ICollection ITaskItem.MetadataNames {
+ get { return MetadataNames.ToArray (); }
+ }
+
+ #endregion
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectMetadataInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectMetadataInstance.cs
index b0386fe28a..c94fe8740d 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectMetadataInstance.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectMetadataInstance.cs
@@ -27,15 +27,29 @@
//
using System;
+using Microsoft.Build.Construction;
namespace Microsoft.Build.Execution
{
- public class ProjectMetadataInstance
- {
- private ProjectMetadataInstance ()
- {
- throw new NotImplementedException ();
- }
- }
+ public class ProjectMetadataInstance
+ {
+ internal ProjectMetadataInstance (string name, string value)
+ {
+ Name = name;
+ EvaluatedValue = value;
+ }
+
+ public string EvaluatedValue { get; private set; }
+ public string Name { get; private set; }
+
+ public ProjectMetadataInstance DeepClone ()
+ {
+ return new ProjectMetadataInstance (Name, EvaluatedValue);
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("{0}={1}", Name, EvaluatedValue);
+ }
+ }
}
-
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectOnErrorInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectOnErrorInstance.cs
new file mode 100644
index 0000000000..b5572ef8a5
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectOnErrorInstance.cs
@@ -0,0 +1,82 @@
+//
+// ProjectOnErrorInstance.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public class ProjectOnErrorInstance : ProjectTargetInstanceChild
+ {
+ internal ProjectOnErrorInstance (ProjectOnErrorElement xml)
+ {
+ condition = xml.Condition;
+ ExecuteTargets = xml.ExecuteTargetsAttribute;
+ //this.FullPath = fullPath;
+ #if NET_4_5
+ condition_location = xml.ConditionLocation;
+ ExecuteTargetsLocation = xml.ExecuteTargetsAttributeLocation;
+ location = xml.Location;
+ #endif
+ }
+
+ readonly string condition;
+
+ public override string Condition {
+ get { return condition; }
+ }
+
+ public string ExecuteTargets { get; private set; }
+
+ readonly ElementLocation condition_location, location;
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation ConditionLocation {
+ get { return condition_location; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ ElementLocation ExecuteTargetsLocation { get; private set; }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation Location {
+ get { return location; }
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskInstance.cs
new file mode 100644
index 0000000000..1e5d7fa6e3
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskInstance.cs
@@ -0,0 +1,82 @@
+//
+// ProjectPropertyGroupTaskInstance.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public sealed class ProjectPropertyGroupTaskInstance : ProjectTargetInstanceChild
+ {
+ internal ProjectPropertyGroupTaskInstance (ProjectPropertyGroupElement xml)
+ {
+ condition = xml.Condition;
+ condition_location = xml.ConditionLocation;
+ //this.FullPath = fullPath;
+ location = xml.Location;
+
+ Properties = xml.Properties.Select (prop => new ProjectPropertyGroupTaskPropertyInstance (prop)).ToArray ();
+ }
+
+ readonly string condition;
+ readonly ElementLocation condition_location, location;
+
+ public override string Condition {
+ get { return condition; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation ConditionLocation {
+ get { return condition_location; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation Location {
+ get { return location; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ ElementLocation ExecuteTargetsLocation { get; private set; }
+
+ public ICollection<ProjectPropertyGroupTaskPropertyInstance> Properties { get; private set; }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskPropertyInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskPropertyInstance.cs
new file mode 100644
index 0000000000..df1d2c6af1
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyGroupTaskPropertyInstance.cs
@@ -0,0 +1,61 @@
+//
+// ProjectPropertyGroupTaskPropertyInstance.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public class ProjectPropertyGroupTaskPropertyInstance
+ {
+ internal ProjectPropertyGroupTaskPropertyInstance (ProjectPropertyElement xml)
+ {
+ Condition = xml.Condition;
+ Name = xml.Name;
+ Value = xml.Value;
+ #if NET_4_5
+ ConditionLocation = xml.ConditionLocation;
+ Location = xml.Location;
+ #endif
+ }
+
+ public string Condition { get; private set; }
+
+ public string Name { get; private set; }
+
+ public string Value { get; private set; }
+
+ #if NET_4_5
+ public ElementLocation ConditionLocation { get; private set; }
+
+ public ElementLocation Location { get; private set; }
+ #endif
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyInstance.cs
index 3b6b32c2d8..5bacdbe61a 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyInstance.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectPropertyInstance.cs
@@ -3,8 +3,9 @@
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -30,12 +31,32 @@ using System;
namespace Microsoft.Build.Execution
{
- public class ProjectPropertyInstance
- {
- private ProjectPropertyInstance ()
- {
- throw new NotImplementedException ();
- }
- }
-}
+ public class ProjectPropertyInstance
+ {
+ internal ProjectPropertyInstance (string name, bool isImmutable, string evaluatedValue, Func<string> evaluatedValueGetter = null)
+ {
+ Name = name;
+ IsImmutable = isImmutable;
+ evaluated_value_getter = evaluatedValueGetter ?? (() => evaluatedValue);
+ }
+
+ Func<string> evaluated_value_getter;
+ public string EvaluatedValue {
+ get { return evaluated_value_getter (); }
+ set {
+ if (IsImmutable)
+ throw new InvalidOperationException ();
+ evaluated_value_getter = () => value;
+ }
+ }
+ public virtual bool IsImmutable { get; private set; }
+
+ public string Name { get; private set; }
+
+ public override string ToString ()
+ {
+ return string.Format ("{0}={1}", Name, EvaluatedValue);
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstance.cs
index a95ee16cb0..3046811068 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstance.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstance.cs
@@ -1,9 +1,11 @@
+//
// ProjectTargetInstance.cs
//
// Author:
// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsshi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -25,16 +27,83 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+
using System;
+using Microsoft.Build.Construction;
+using System.Collections.Generic;
+using System.Linq;
namespace Microsoft.Build.Execution
{
- public sealed class ProjectTargetInstance
- {
- private ProjectTargetInstance ()
- {
- throw new NotImplementedException ();
- }
- }
+ public sealed class ProjectTargetInstance
+ {
+ internal ProjectTargetInstance (ProjectTargetElement xml)
+ {
+ FullPath = xml.ContainingProject.FullPath;
+ Children = xml.Children.Select<ProjectElement,ProjectTargetInstanceChild> (c => {
+ if (c is ProjectOnErrorElement)
+ return new ProjectOnErrorInstance ((ProjectOnErrorElement) c);
+ if (c is ProjectItemGroupElement)
+ return new ProjectItemGroupTaskInstance ((ProjectItemGroupElement) c);
+ if (c is ProjectPropertyGroupElement)
+ return new ProjectPropertyGroupTaskInstance ((ProjectPropertyGroupElement) c);
+ if (c is ProjectTaskElement)
+ return new ProjectTaskInstance ((ProjectTaskElement) c);
+ throw new NotSupportedException ();
+ }).ToArray ();
+ Condition = xml.Condition;
+ DependsOnTargets = xml.DependsOnTargets;
+ //FullPath = fullPath;
+ Inputs = xml.Inputs;
+ KeepDuplicateOutputs = xml.KeepDuplicateOutputs;
+ Name = xml.Name;
+ OnErrorChildren = xml.OnErrors.Select (c => new ProjectOnErrorInstance (c)).ToArray ();
+ Outputs = xml.Outputs;
+ Returns = xml.Returns;
+ Tasks = xml.Tasks.Select (t => new ProjectTaskInstance (t)).ToArray ();
+ AfterTargetsLocation = xml.AfterTargetsLocation;
+ BeforeTargetsLocation = xml.BeforeTargetsLocation;
+ ConditionLocation = xml.ConditionLocation;
+ DependsOnTargetsLocation = xml.DependsOnTargetsLocation;
+ InputsLocation = xml.InputsLocation;
+ KeepDuplicateOutputsLocation = xml.KeepDuplicateOutputsLocation;
+ Location = xml.Location;
+ OutputsLocation = xml.OutputsLocation;
+ ReturnsLocation = xml.ReturnsLocation;
+ }
+
+ public IList<ProjectTargetInstanceChild> Children { get; private set; }
+ public string Condition { get; private set; }
+ public string DependsOnTargets { get; private set; }
+ public string FullPath { get; private set; }
+ public string Inputs { get; private set; }
+ public string KeepDuplicateOutputs { get; private set; }
+ public string Name { get; private set; }
+ public IList<ProjectOnErrorInstance> OnErrorChildren { get; private set; }
+ public string Outputs { get; private set; }
+ public string Returns { get; private set; }
+ public ICollection<ProjectTaskInstance> Tasks { get; private set; }
+#if NET_4_5
+ public ElementLocation AfterTargetsLocation { get; private set; }
+ public ElementLocation BeforeTargetsLocation { get; private set; }
+ public ElementLocation ConditionLocation { get; private set; }
+ public ElementLocation DependsOnTargetsLocation { get; private set; }
+ public ElementLocation InputsLocation { get; private set; }
+ public ElementLocation KeepDuplicateOutputsLocation { get; private set; }
+ public ElementLocation Location { get; private set; }
+ public ElementLocation OutputsLocation { get; private set; }
+ public ElementLocation ReturnsLocation { get; private set; }
+#else
+ internal ElementLocation AfterTargetsLocation { get; private set; }
+ internal ElementLocation BeforeTargetsLocation { get; private set; }
+ internal ElementLocation ConditionLocation { get; private set; }
+ internal ElementLocation DependsOnTargetsLocation { get; private set; }
+ internal ElementLocation InputsLocation { get; private set; }
+ internal ElementLocation KeepDuplicateOutputsLocation { get; private set; }
+ internal ElementLocation Location { get; private set; }
+ internal ElementLocation OutputsLocation { get; private set; }
+ internal ElementLocation ReturnsLocation { get; private set; }
+#endif
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstanceChild.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstanceChild.cs
new file mode 100644
index 0000000000..aa38e6b006
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTargetInstanceChild.cs
@@ -0,0 +1,52 @@
+//
+// ProjectTargetInstanceChild.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public abstract class ProjectTargetInstanceChild
+ {
+ public abstract string Condition { get; }
+ public string FullPath { get; internal set; }
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ abstract ElementLocation ConditionLocation { get; }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ abstract ElementLocation Location { get; }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstance.cs
new file mode 100644
index 0000000000..bc53da1656
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstance.cs
@@ -0,0 +1,108 @@
+//
+// ProjectOnErrorInstance.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using Microsoft.Build.Construction;
+using System.Linq;
+
+namespace Microsoft.Build.Execution
+{
+ public sealed class ProjectTaskInstance : ProjectTargetInstanceChild
+ {
+ internal ProjectTaskInstance (ProjectTaskElement xml)
+ {
+ condition = xml.Condition;
+ ContinueOnError = xml.ContinueOnError;
+ Name = xml.Name;
+ Outputs = xml.Outputs.Select (o => {
+ if (o.IsOutputItem)
+ return (ProjectTaskInstanceChild) new ProjectTaskOutputItemInstance ((ProjectOutputElement) o);
+ if (o.IsOutputProperty)
+ return new ProjectTaskOutputPropertyInstance ((ProjectOutputElement) o);
+ throw new NotSupportedException ();
+ }).ToArray ();
+ Parameters = new Dictionary<string,string> (xml.Parameters);
+ #if NET_4_5
+ MSBuildArchitecture = xml.MSBuildArchitecture;
+ MSBuildRuntime = xml.MSBuildRuntime;
+
+ condition_location = xml.ConditionLocation;
+ ContinueOnErrorLocation = xml.ContinueOnErrorLocation;
+ location = xml.Location;
+ MSBuildArchitectureLocation = xml.MSBuildArchitectureLocation;
+ MSBuildRuntimeLocation = xml.MSBuildRuntimeLocation;
+ #endif
+ }
+
+ string condition;
+ public override string Condition {
+ get { return condition; }
+ }
+
+ ElementLocation condition_location, location;
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation ConditionLocation {
+ get { return condition_location; }
+ }
+
+ #if NET_4_5
+ public
+ #else
+ internal
+ #endif
+ override ElementLocation Location {
+ get { return location; }
+ }
+
+ public string ContinueOnError { get; private set; }
+
+ #if NET_4_5
+ public ElementLocation ContinueOnErrorLocation { get; private set; }
+
+ public string MSBuildArchitecture { get; private set; }
+
+ public ElementLocation MSBuildArchitectureLocation { get; private set; }
+
+ public string MSBuildRuntime { get; private set; }
+
+ public ElementLocation MSBuildRuntimeLocation { get; private set; }
+ #endif
+
+ public string Name { get; private set; }
+
+ public IList<ProjectTaskInstanceChild> Outputs { get; private set; }
+
+ public IDictionary<string, string> Parameters { get; private set; }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstanceChild.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstanceChild.cs
new file mode 100644
index 0000000000..77ea7a2525
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskInstanceChild.cs
@@ -0,0 +1,16 @@
+using System;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public abstract class ProjectTaskInstanceChild
+ {
+ public abstract string Condition { get; }
+ #if NET_4_5
+ public abstract ElementLocation ConditionLocation { get; }
+ public abstract ElementLocation Location { get; }
+ public abstract ElementLocation TaskParameterLocation { get; }
+ #endif
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputItemInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputItemInstance.cs
new file mode 100644
index 0000000000..356f9a62f2
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputItemInstance.cs
@@ -0,0 +1,42 @@
+using System;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public class ProjectTaskOutputItemInstance : ProjectTaskInstanceChild
+ {
+ internal ProjectTaskOutputItemInstance (ProjectOutputElement xml)
+ {
+ condition = xml.Condition;
+ ItemType = xml.ItemType;
+ TaskParameter = xml.TaskParameter;
+ #if NET_4_5
+ condition_location = xml.ConditionLocation;
+ location = xml.Location;
+ task_parameter_location = xml.TaskParameterLocation;
+ #endif
+ }
+
+ public string ItemType { get; private set; }
+ public string TaskParameter { get; private set; }
+
+ readonly string condition;
+ public override string Condition {
+ get { return condition; }
+ }
+ #if NET_4_5
+ readonly ElementLocation condition_location, location, task_parameter_location;
+ public ElementLocation ItemTypeLocation { get; private set; }
+ public override ElementLocation ConditionLocation {
+ get { return condition_location; }
+ }
+ public override ElementLocation Location {
+ get { return location; }
+ }
+ public override ElementLocation TaskParameterLocation {
+ get { return task_parameter_location; }
+ }
+ #endif
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputPropertyInstance.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputPropertyInstance.cs
new file mode 100644
index 0000000000..855268e130
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/ProjectTaskOutputPropertyInstance.cs
@@ -0,0 +1,43 @@
+using System;
+using Microsoft.Build.Construction;
+
+namespace Microsoft.Build.Execution
+{
+ public class ProjectTaskOutputPropertyInstance : ProjectTaskInstanceChild
+ {
+ internal ProjectTaskOutputPropertyInstance (ProjectOutputElement xml)
+ {
+ condition = xml.Condition;
+ PropertyName = xml.PropertyName;
+ TaskParameter = xml.TaskParameter;
+ #if NET_4_5
+ condition_location = xml.ConditionLocation;
+ location = xml.Location;
+ task_parameter_location = xml.TaskParameterLocation;
+ #endif
+ }
+
+ public string PropertyName { get; private set; }
+ public string TaskParameter { get; private set; }
+
+ readonly string condition;
+ public override string Condition {
+ get { return condition; }
+ }
+
+ #if NET_4_5
+ readonly ElementLocation condition_location, location, task_parameter_location;
+ public ElementLocation PropertyNameLocation { get; private set; }
+ public override ElementLocation ConditionLocation {
+ get { return condition_location; }
+ }
+ public override ElementLocation Location {
+ get { return location; }
+ }
+ public override ElementLocation TaskParameterLocation {
+ get { return task_parameter_location; }
+ }
+ #endif
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/TargetResult.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/TargetResult.cs
index 863966ba78..d6dac3a8ff 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Execution/TargetResult.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Execution/TargetResult.cs
@@ -26,30 +26,40 @@
//
using Microsoft.Build.Framework;
-
using System;
+using System.Linq;
+using System.Collections.Generic;
namespace Microsoft.Build.Execution
{
- public class TargetResult : ITargetResult
- {
- internal TargetResult ()
- {
- throw new NotImplementedException ();
- }
+ public class TargetResult : ITargetResult
+ {
+ internal TargetResult ()
+ {
+ }
- public Exception Exception {
- get { throw new NotImplementedException (); }
- }
+ public Exception Exception { get; private set; }
- public ITaskItem[] Items {
- get { throw new NotImplementedException (); }
- }
+ public ITaskItem[] Items { get; private set; }
+ public TargetResultCode ResultCode { get; private set; }
- public TargetResultCode ResultCode {
- get { throw new NotImplementedException (); }
- }
- }
+ internal void Failure (Exception exception)
+ {
+ this.Exception = exception;
+ ResultCode = TargetResultCode.Failure;
+ }
+
+ internal void Skip ()
+ {
+ ResultCode = TargetResultCode.Skipped;
+ }
+
+ internal void Success (IEnumerable<ITaskItem> items)
+ {
+ Items = items.ToArray ();
+ ResultCode = TargetResultCode.Success;
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs
new file mode 100644
index 0000000000..22a8c615d4
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildEngine4.cs
@@ -0,0 +1,634 @@
+//
+// BuildEngine4.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Evaluation;
+using System.Linq;
+using System.IO;
+using Microsoft.Build.Exceptions;
+using System.Globalization;
+
+namespace Microsoft.Build.Internal
+{
+ class BuildEngine4
+#if NET_4_5
+ : IBuildEngine4
+#else
+ : IBuildEngine3
+#endif
+ {
+ public BuildEngine4 (BuildSubmission submission)
+ {
+ this.submission = submission;
+ event_source = new Microsoft.Build.BuildEngine.EventSource ();
+ if (submission.BuildManager.OngoingBuildParameters.Loggers != null)
+ foreach (var l in submission.BuildManager.OngoingBuildParameters.Loggers)
+ l.Initialize (event_source);
+ }
+
+ BuildSubmission submission;
+ ProjectInstance project;
+ ProjectTaskInstance current_task;
+ Microsoft.Build.BuildEngine.EventSource event_source;
+
+ public ProjectCollection Projects {
+ get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; }
+ }
+
+ // FIXME:
+ // While we are not faced to implement those features, there are some modern task execution requirements.
+ //
+ // This will have to be available for "out of process" nodes (see NodeAffinity).
+ // NodeAffinity is set per project file at BuildManager.HostServices.
+ // When NodeAffinity is set to OutOfProc, it should probably launch different build host
+ // that runs separate build tasks. (.NET has MSBuildTaskHost.exe which I guess is about that.)
+ //
+ // Also note that the complete implementation has to support LoadInSeparateAppDomainAttribute
+ // (which is most likely derived from AppDomainIsolatedBuildTask) that marks a task to run
+ // in separate AppDomain.
+ //
+ public void BuildProject (Func<bool> checkCancel, BuildResult result, ProjectInstance project, IEnumerable<string> targetNames, IDictionary<string,string> globalProperties, IDictionary<string,string> targetOutputs, string toolsVersion)
+ {
+ if (toolsVersion == null)
+ throw new ArgumentNullException ("toolsVersion");
+
+ var parameters = submission.BuildManager.OngoingBuildParameters;
+ var toolset = parameters.GetToolset (toolsVersion);
+ if (toolset == null)
+ throw new InvalidOperationException (string.Format ("Toolset version '{0}' was not resolved to valid toolset", toolsVersion));
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using Toolset version {0}.", toolsVersion), null, null, MessageImportance.Low));
+ var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (toolset), submission.BuildRequest.ProjectInstance.TaskDatabase);
+ BuildProject (new InternalBuildArguments () { CheckCancel = checkCancel, Result = result, Project = project, TargetNames = targetNames, GlobalProperties = globalProperties, TargetOutputs = targetOutputs, ToolsVersion = toolsVersion, BuildTaskFactory = buildTaskFactory });
+ }
+
+ class InternalBuildArguments
+ {
+ public Func<bool> CheckCancel;
+ public BuildResult Result;
+ public ProjectInstance Project;
+ public IEnumerable<string> TargetNames;
+ public IDictionary<string,string> GlobalProperties;
+ 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)
+ {
+ var request = submission.BuildRequest;
+ var parameters = submission.BuildManager.OngoingBuildParameters;
+ this.project = args.Project;
+
+ event_source.FireBuildStarted (this, new BuildStartedEventArgs ("Build Started", null, DateTime.Now));
+
+ try {
+
+ 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 ());
+ LogMessageEvent (new BuildMessageEventArgs (initialPropertiesFormatted, null, null, MessageImportance.Low));
+ var initialItemsFormatted = "Initial Items:\n" + string.Join (Environment.NewLine, project.Items.OrderBy (i => i.ItemType).Select (i => string.Format ("{0} : {1}", i.ItemType, i.EvaluatedInclude)).ToArray ());
+ LogMessageEvent (new BuildMessageEventArgs (initialItemsFormatted, null, null, MessageImportance.Low));
+
+ // null targets -> success. empty targets -> success(!)
+ if (request.TargetNames == null)
+ args.Result.OverallResult = BuildResultCode.Success;
+ else {
+ foreach (var targetName in (args.TargetNames ?? request.TargetNames).Where (t => t != null))
+ BuildTargetByName (targetName, args);
+
+ // FIXME: check .NET behavior, whether cancellation always results in failure.
+ args.Result.OverallResult = args.CheckCancel () ? BuildResultCode.Failure : args.Result.ResultsByTarget.Any (p => p.Value.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success;
+ }
+ } catch (Exception ex) {
+ args.Result.OverallResult = BuildResultCode.Failure;
+ LogErrorEvent (new BuildErrorEventArgs (null, null, project.FullPath, 0, 0, 0, 0, "Unhandled exception occured during a build", null, null));
+ LogMessageEvent (new BuildMessageEventArgs ("Exception details: " + ex, null, null, MessageImportance.Low));
+ throw; // BuildSubmission re-catches this.
+ } finally {
+ event_source.FireBuildFinished (this, new BuildFinishedEventArgs ("Build Finished.", null, args.Result.OverallResult == BuildResultCode.Success, DateTime.Now));
+ }
+ }
+
+ bool BuildTargetByName (string targetName, InternalBuildArguments args)
+ {
+ var request = submission.BuildRequest;
+ var parameters = submission.BuildManager.OngoingBuildParameters;
+ ProjectTargetInstance target;
+ TargetResult dummyResult;
+
+ if (args.Result.ResultsByTarget.TryGetValue (targetName, out dummyResult) && dummyResult.ResultCode == TargetResultCode.Success) {
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because it was already built successfully.", targetName), null, null, MessageImportance.Low));
+ return true; // do not add result.
+ }
+
+ var targetResult = new TargetResult ();
+
+ // null key is allowed and regarded as blind success(!) (as long as it could retrieve target)
+ if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target))
+ throw new InvalidOperationException (string.Format ("target '{0}' was not found in project '{1}'", targetName, project.FullPath));
+ else if (!args.Project.EvaluateCondition (target.Condition)) {
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because condition '{1}' was not met.", target.Name, target.Condition), null, null, MessageImportance.Low));
+ targetResult.Skip ();
+ } else {
+ // process DependsOnTargets first.
+ foreach (var dep in project.ExpandString (target.DependsOnTargets).Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s))) {
+ if (!BuildTargetByName (dep, args)) {
+ return false;
+ }
+ }
+
+ Func<string,ITaskItem> creator = s => new TargetOutputTaskItem () { ItemSpec = s };
+
+ event_source.FireTargetStarted (this, new TargetStartedEventArgs ("Target Started", null, target.Name, project.FullPath, target.FullPath));
+ try {
+ if (!string.IsNullOrEmpty (target.Inputs) != !string.IsNullOrEmpty (target.Outputs)) {
+ targetResult.Failure (new InvalidProjectFileException (target.Location, null, string.Format ("Target {0} has mismatching Inputs and Outputs specification. When one is specified, another one has to be specified too.", targetName), null, null, null));
+ } else {
+ bool skip = false;
+ if (!string.IsNullOrEmpty (target.Inputs)) {
+ var inputs = args.Project.GetAllItems (target.Inputs, string.Empty, creator, creator, s => true, (t, s) => {
+ });
+ if (!inputs.Any ()) {
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because there is no input.", target.Name), null, null, MessageImportance.Low));
+ skip = true;
+ } else {
+ var outputs = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
+ });
+ var needsUpdates = GetOlderOutputsThanInputs (inputs, outputs).FirstOrDefault ();
+ if (needsUpdates != null)
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' needs to be built because new output {1} is needed.", target.Name, needsUpdates.ItemSpec), null, null, MessageImportance.Low));
+ else {
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Target '{0}' was skipped because all the outputs are newer than all the inputs.", target.Name), null, null, MessageImportance.Low));
+ skip = true;
+ }
+ }
+ }
+ if (skip) {
+ targetResult.Skip ();
+ } else {
+ if (DoBuildTarget (target, targetResult, args)) {
+ var items = args.Project.GetAllItems (target.Outputs, string.Empty, creator, creator, s => true, (t, s) => {
+ });
+ targetResult.Success (items);
+ }
+ }
+ }
+ } finally {
+ event_source.FireTargetFinished (this, new TargetFinishedEventArgs ("Target Finished", null, targetName, project.FullPath, target.FullPath, targetResult.ResultCode != TargetResultCode.Failure));
+ }
+ }
+ args.AddTargetResult (targetName, targetResult);
+
+ return targetResult.ResultCode != TargetResultCode.Failure;
+ }
+
+ IEnumerable<ITaskItem> GetOlderOutputsThanInputs (IEnumerable<ITaskItem> inputs, IEnumerable<ITaskItem> outputs)
+ {
+ return outputs.Where (o => !File.Exists (o.GetMetadata ("FullPath")) || inputs.Any (i => string.CompareOrdinal (i.GetMetadata ("LastModifiedTime"), o.GetMetadata ("LastModifiedTime")) > 0));
+ }
+
+ bool DoBuildTarget (ProjectTargetInstance target, TargetResult targetResult, InternalBuildArguments args)
+ {
+ var request = submission.BuildRequest;
+
+ // 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 {
+ // Evaluate additional target properties
+ foreach (var c in target.Children.OfType<ProjectPropertyGroupTaskInstance> ()) {
+ if (!args.Project.EvaluateCondition (c.Condition))
+ continue;
+ 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);
+ }
+ }
+
+ // Evaluate additional target items
+ foreach (var c in target.Children.OfType<ProjectItemGroupTaskInstance> ()) {
+ if (!args.Project.EvaluateCondition (c.Condition))
+ continue;
+ foreach (var item in c.Items) {
+ if (!args.Project.EvaluateCondition (item.Condition))
+ continue;
+ 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))
+ continue;
+ throw new NotImplementedException ();
+ }
+
+ // run tasks
+ foreach (var ti in target.Children.OfType<ProjectTaskInstance> ()) {
+ current_task = ti;
+ if (!args.Project.EvaluateCondition (ti.Condition)) {
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Task '{0}' was skipped because condition '{1}' wasn't met.", ti.Name, ti.Condition), null, null, MessageImportance.Low));
+ continue;
+ }
+ if (!RunBuildTask (target, ti, targetResult, args))
+ return false;
+ }
+ } 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 true;
+ }
+
+ bool RunBuildTask (ProjectTargetInstance target, ProjectTaskInstance taskInstance, TargetResult targetResult, InternalBuildArguments args)
+ {
+ var request = submission.BuildRequest;
+
+ var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, target.Name, taskInstance.Name);
+
+ // Create Task instance.
+ var factoryIdentityParameters = new Dictionary<string,string> ();
+ #if NET_4_5
+ factoryIdentityParameters ["MSBuildRuntime"] = taskInstance.MSBuildRuntime;
+ factoryIdentityParameters ["MSBuildArchitecture"] = taskInstance.MSBuildArchitecture;
+ #endif
+ var task = args.BuildTaskFactory.CreateTask (taskInstance.Name, factoryIdentityParameters, this);
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Using task {0} from {1}", taskInstance.Name, task.GetType ().AssemblyQualifiedName), null, null, MessageImportance.Low));
+ task.HostObject = host;
+ task.BuildEngine = this;
+
+ // Prepare task parameters.
+ var evaluatedTaskParams = taskInstance.Parameters.Select (p => new KeyValuePair<string,string> (p.Key, project.ExpandString (p.Value)));
+
+ var requiredProps = task.GetType ().GetProperties ()
+ .Where (p => p.CanWrite && p.GetCustomAttributes (typeof (RequiredAttribute), true).Any ());
+ var missings = requiredProps.Where (p => !evaluatedTaskParams.Any (tp => tp.Key.Equals (p.Name, StringComparison.OrdinalIgnoreCase)));
+ if (missings.Any ())
+ throw new InvalidOperationException (string.Format ("Task {0} of type {1} is used without specifying mandatory property: {2}",
+ taskInstance.Name, task.GetType (), string.Join (", ", missings.Select (p => p.Name).ToArray ())));
+
+ foreach (var p in evaluatedTaskParams) {
+ var prop = task.GetType ().GetProperty (p.Key);
+ if (prop == null)
+ throw new InvalidOperationException (string.Format ("Task {0} does not have property {1}", taskInstance.Name, p.Key));
+ if (!prop.CanWrite)
+ throw new InvalidOperationException (string.Format ("Task {0} has property {1} but it is read-only.", taskInstance.Name, p.Key));
+ if (string.IsNullOrEmpty (p.Value) && !requiredProps.Contains (prop))
+ continue;
+ try {
+ var valueInstance = ConvertTo (p.Value, prop.PropertyType);
+ prop.SetValue (task, valueInstance, null);
+ } catch (Exception ex) {
+ throw new InvalidOperationException (string.Format ("Failed to convert '{0}' for property '{1}' of type {2}", p.Value, prop.Name, prop.PropertyType), ex);
+ }
+ }
+
+ // Do execute task.
+ bool taskSuccess = false;
+ event_source.FireTaskStarted (this, new TaskStartedEventArgs ("Task Started", null, project.FullPath, taskInstance.FullPath, taskInstance.Name));
+ try {
+ taskSuccess = task.Execute ();
+
+ if (!taskSuccess) {
+ targetResult.Failure (null);
+ if (!ContinueOnError) {
+ return false;
+ }
+ } else {
+ // Evaluate task output properties and items.
+ foreach (var to in taskInstance.Outputs) {
+ if (!project.EvaluateCondition (to.Condition))
+ continue;
+ var toItem = to as ProjectTaskOutputItemInstance;
+ var toProp = to as ProjectTaskOutputPropertyInstance;
+ string taskParameter = toItem != null ? toItem.TaskParameter : toProp.TaskParameter;
+ var pi = task.GetType ().GetProperty (taskParameter);
+ if (pi == null)
+ throw new InvalidOperationException (string.Format ("Task {0} does not have property {1} specified as TaskParameter", taskInstance.Name, toItem.TaskParameter));
+ if (!pi.CanRead)
+ throw new InvalidOperationException (string.Format ("Task {0} has property {1} specified as TaskParameter, but it is write-only", taskInstance.Name, toItem.TaskParameter));
+ var value = ConvertFrom (pi.GetValue (task, null));
+ if (toItem != null) {
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Item {0} from TaskParameter {1}: {2}", toItem.ItemType, toItem.TaskParameter, value), null, null, MessageImportance.Low));
+ foreach (var item in value.Split (';'))
+ args.Project.AddItem (toItem.ItemType, item);
+ } else {
+ LogMessageEvent (new BuildMessageEventArgs (string.Format ("Output Property {0} from TaskParameter {1}: {2}", toProp.PropertyName, toProp.TaskParameter, value), null, null, MessageImportance.Low));
+ args.Project.SetProperty (toProp.PropertyName, value);
+ }
+ }
+ }
+ } finally {
+ event_source.FireTaskFinished (this, new TaskFinishedEventArgs ("Task Finished", null, project.FullPath, taskInstance.FullPath, taskInstance.Name, taskSuccess));
+ }
+ return true;
+ }
+
+ object ConvertTo (string source, Type targetType)
+ {
+ if (targetType == typeof(ITaskItem) || targetType.IsSubclassOf (typeof(ITaskItem)))
+ return new TargetOutputTaskItem () { ItemSpec = WindowsCompatibilityExtensions.NormalizeFilePath (source.Trim ()) };
+ if (targetType.IsArray)
+ return new ArrayList (source.Split (';').Select (s => s.Trim ()).Where (s => !string.IsNullOrEmpty (s)).Select (s => ConvertTo (s, targetType.GetElementType ())).ToArray ())
+ .ToArray (targetType.GetElementType ());
+ if (targetType == typeof(bool)) {
+ switch (source != null ? source.ToLower (CultureInfo.InvariantCulture) : string.Empty) {
+ case "true":
+ case "yes":
+ case "on":
+ return true;
+ case "false":
+ case "no":
+ case "off":
+ case "":
+ return false;
+ }
+ }
+ return Convert.ChangeType (source == "" ? null : source, targetType);
+ }
+
+ string ConvertFrom (object source)
+ {
+ if (source == null)
+ return string.Empty;
+ if (source is ITaskItem)
+ return ((ITaskItem) source).ItemSpec;
+ 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 ProjectCollection.Escape ((string) metadata [metadataName]);
+ }
+ public void SetMetadataValueLiteral (string metadataName, string metadataValue)
+ {
+ metadata [metadataName] = ProjectCollection.Unescape (metadataValue);
+ }
+ public IDictionary CloneCustomMetadataEscaped ()
+ {
+ 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); }
+ set { ItemSpec = ProjectCollection.Unescape (value); }
+ }
+ #endregion
+ #region ITaskItem implementation
+ public IDictionary CloneCustomMetadata ()
+ {
+ return new Hashtable (metadata);
+ }
+ public void CopyMetadataTo (ITaskItem destinationItem)
+ {
+ foreach (DictionaryEntry e in metadata)
+ destinationItem.SetMetadata ((string) e.Key, (string) e.Value);
+ }
+ public string GetMetadata (string metadataName)
+ {
+ var wk = ProjectCollection.GetWellKnownMetadata (metadataName, ItemSpec, Path.GetFullPath, null);
+ if (wk != null)
+ return wk;
+ return (string) metadata [metadataName];
+ }
+ public void RemoveMetadata (string metadataName)
+ {
+ metadata.Remove (metadataName);
+ }
+ public void SetMetadata (string metadataName, string metadataValue)
+ {
+ metadata [metadataName] = metadataValue;
+ }
+ public string ItemSpec { get; set; }
+ public int MetadataCount {
+ get { return metadata.Count; }
+ }
+ public ICollection MetadataNames {
+ get { return metadata.Keys; }
+ }
+ #endregion
+ }
+
+#if NET_4_5
+ #region IBuildEngine4 implementation
+
+ // task objects are not in use anyways though...
+
+ class TaskObjectRegistration
+ {
+ public TaskObjectRegistration (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
+ {
+ Key = key;
+ Object = obj;
+ Lifetime = lifetime;
+ AllowEarlyCollection = allowEarlyCollection;
+ }
+ public object Key { get; private set; }
+ public object Object { get; private set; }
+ public RegisteredTaskObjectLifetime Lifetime { get; private set; }
+ public bool AllowEarlyCollection { get; private set; }
+ }
+
+ List<TaskObjectRegistration> task_objects = new List<TaskObjectRegistration> ();
+
+ public object GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
+ {
+ var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
+ return reg != null ? reg.Object : null;
+ }
+
+ public void RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
+ {
+ task_objects.Add (new TaskObjectRegistration (key, obj, lifetime, allowEarlyCollection));
+ }
+
+ public object UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
+ {
+ var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
+ if (reg != null)
+ task_objects.Remove (reg);
+ return reg.Object;
+ }
+ #endregion
+#endif
+
+ #region IBuildEngine3 implementation
+
+ public BuildEngineResult BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList<string>[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public void Reacquire ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public void Yield ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ #endregion
+
+ #region IBuildEngine2 implementation
+
+ public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion)
+ {
+ var proj = GetProjectInstance (projectFileName, toolsVersion);
+ var globalPropertiesThatMakeSense = new Dictionary<string,string> ();
+ foreach (DictionaryEntry p in globalProperties)
+ globalPropertiesThatMakeSense [(string) p.Key] = (string) p.Value;
+ var result = new BuildResult ();
+ var outputs = new Dictionary<string, string> ();
+ BuildProject (() => false, result, proj, targetNames, globalPropertiesThatMakeSense, outputs, toolsVersion);
+ foreach (var p in outputs)
+ targetOutputs [p.Key] = p.Value;
+ return result.OverallResult == BuildResultCode.Success;
+ }
+
+ public bool BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public bool IsRunningMultipleNodes {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+
+ ProjectInstance GetProjectInstance (string projectFileName, string toolsVersion)
+ {
+ string fullPath = Path.GetFullPath (projectFileName);
+ if (submission.BuildRequest.ProjectFullPath == fullPath)
+ return submission.BuildRequest.ProjectInstance;
+ // FIXME: could also be filtered by global properties
+ // http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.getloadedprojects.aspx
+ var project = Projects.GetLoadedProjects (projectFileName).FirstOrDefault (p => p.ToolsVersion == toolsVersion);
+ if (project == null)
+ throw new InvalidOperationException (string.Format ("Project '{0}' is not loaded", projectFileName));
+ return submission.BuildManager.GetProjectInstanceForBuild (project);
+ }
+
+ #endregion
+
+ #region IBuildEngine implementation
+
+ public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
+ {
+ return BuildProjectFile (projectFileName, targetNames, globalProperties, targetOutputs, Projects.DefaultToolsVersion);
+ }
+
+ public void LogCustomEvent (CustomBuildEventArgs e)
+ {
+ event_source.FireCustomEventRaised (this, e);
+ }
+
+ public void LogErrorEvent (BuildErrorEventArgs e)
+ {
+ event_source.FireErrorRaised (this, e);
+ }
+
+ public void LogMessageEvent (BuildMessageEventArgs e)
+ {
+ event_source.FireMessageRaised (this, e);
+ }
+
+ public void LogWarningEvent (BuildWarningEventArgs e)
+ {
+ event_source.FireWarningRaised (this, e);
+ }
+
+ public int ColumnNumberOfTaskNode {
+ get { return current_task.Location != null ? current_task.Location.Column : 0; }
+ }
+
+ public bool 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 {
+ get { return current_task.Location != null ? current_task.Location.Line : 0; }
+ }
+
+ public string ProjectFileOfTaskNode {
+ get { return current_task.FullPath; }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs
new file mode 100644
index 0000000000..ebfad5ba75
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildNodeManager.cs
@@ -0,0 +1,209 @@
+//
+// BuildNodeManager.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Collections.Concurrent;
+
+namespace Microsoft.Build.Internal
+{
+ class BuildNodeManager
+ {
+ public BuildNodeManager (BuildManager buildManager)
+ {
+ BuildManager = buildManager;
+ new Thread (RunLoop).Start ();
+ }
+
+ ~BuildNodeManager ()
+ {
+ run_loop = false;
+ queue_wait_handle.Set ();
+ }
+
+ public BuildManager BuildManager { get; private set; }
+
+ List<BuildNode> in_proc_nodes = new List<BuildNode> ();
+ List<BuildNode> out_proc_nodes = new List<BuildNode> ();
+ AutoResetEvent queue_wait_handle = new AutoResetEvent (false);
+ ConcurrentQueue<BuildSubmission> queued_builds = new ConcurrentQueue<BuildSubmission> ();
+ // FIXME: currently it is not in use but it should be stored somewhere for cancellation.
+ Dictionary<BuildSubmission,Task> ongoing_builds = new Dictionary<BuildSubmission, Task> ();
+ bool run_loop = true;
+
+ readonly TaskFactory<BuildResult> task_factory = new TaskFactory<BuildResult> ();
+ internal TaskFactory<BuildResult> ThreadTaskFactory {
+ get { return task_factory; }
+ }
+
+ void RunLoop ()
+ {
+ while (run_loop) {
+ try {
+ if (queued_builds.Count == 0)
+ queue_wait_handle.WaitOne ();
+ if (!run_loop)
+ break;
+ BuildSubmission build;
+ if (!queued_builds.TryDequeue (out build))
+ continue;
+ StartOneBuild (build);
+ } catch (Exception ex) {
+ // FIXME: I guess INodeLogger should be used instead.
+ Console.Error.WriteLine ("Uncaught build node exception occured");
+ Console.Error.WriteLine (ex);
+ }
+ }
+ }
+
+ public void Stop ()
+ {
+ run_loop = false;
+ queue_wait_handle.Set ();
+ }
+
+ public void ResetCaches ()
+ {
+ in_proc_nodes.Clear ();
+ out_proc_nodes.Clear ();
+ }
+
+ public void Enqueue (BuildSubmission build)
+ {
+ queued_builds.Enqueue (build);
+ queue_wait_handle.Set ();
+ }
+
+ void StartOneBuild (BuildSubmission build)
+ {
+ var node = TakeNode (build);
+ // FIXME: Task (non-generic) here causes NotImplementedException in somewhere in Interlocked. It does not make sense.
+ ongoing_builds [build] = task_factory.StartNew (node.ExecuteBuild);
+ //new Thread (() => { node.ExecuteBuild (); }).Start ();
+ }
+
+ void EndOneBuild (BuildNode node)
+ {
+ var task = ongoing_builds [node.Build];
+ ongoing_builds [node.Build] = null;
+ node.Release ();
+ }
+
+ // FIXME: take max nodes into account here, and get throttling working.
+ BuildNode TakeNode (BuildSubmission build)
+ {
+ var host = BuildManager.OngoingBuildParameters.HostServices;
+ NodeAffinity affinity;
+ if (host == null)
+ affinity = NodeAffinity.Any;
+ else
+ affinity = host.GetNodeAffinity (build.BuildRequest.ProjectFullPath);
+ BuildNode n = GetReusableNode (affinity);
+ if (n != null)
+ n.Assign (build);
+ else {
+ n = new BuildNode (this, affinity == NodeAffinity.Any ? NodeAffinity.InProc : affinity);
+ n.Assign (build);
+ if (n.Affinity == NodeAffinity.InProc)
+ in_proc_nodes.Add (n);
+ else
+ out_proc_nodes.Add (n);
+ }
+ return n;
+ }
+
+ BuildNode GetReusableNode (NodeAffinity affinity)
+ {
+ if (!BuildManager.OngoingBuildParameters.EnableNodeReuse)
+ return null;
+
+ if (affinity != NodeAffinity.OutOfProc)
+ foreach (var n in in_proc_nodes)
+ if (n.IsAvailable && (n.Affinity & affinity) != 0)
+ return n;
+ if (affinity != NodeAffinity.InProc)
+ foreach (var n in out_proc_nodes)
+ if (n.IsAvailable && (n.Affinity & affinity) != 0)
+ return n;
+ return null;
+ }
+
+ internal class BuildNode
+ {
+ static Random rnd = new Random ();
+
+ public BuildNode (BuildNodeManager manager, NodeAffinity affinity)
+ {
+ Manager = manager;
+ Affinity = affinity;
+ Id = rnd.Next ();
+ }
+
+ public bool IsAvailable { get; private set; }
+ public int Id { get; private set; }
+ public BuildNodeManager Manager { get; set; }
+ public NodeAffinity Affinity { get; private set; }
+ public BuildSubmission Build { get; private set; }
+
+ public void Assign (BuildSubmission build)
+ {
+ IsAvailable = false;
+ Build = build;
+ }
+
+ public void Release ()
+ {
+ Build = null;
+ IsAvailable = true;
+ }
+
+ public BuildResult ExecuteBuild ()
+ {
+ BuildResult result;
+ try {
+ // FIXME: depending on NodeAffinity, build it through another MSBuild process.
+ if (Affinity == NodeAffinity.OutOfProc)
+ throw new NotImplementedException ();
+ result = Build.InternalExecute ();
+ } catch (Exception ex) {
+ // FIXME: I guess INodeLogger should be used instead.
+ Console.Error.WriteLine ("Uncaught build node exception occured");
+ Console.Error.WriteLine (ex);
+ result = null;
+ } finally {
+ Manager.EndOneBuild (this);
+ }
+ return result;
+ }
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskDatabase.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskDatabase.cs
new file mode 100644
index 0000000000..0b7660b2d4
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskDatabase.cs
@@ -0,0 +1,138 @@
+//
+// BuildTaskFactory.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Framework;
+using System.Reflection;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Construction;
+using System.IO;
+using System.Xml;
+
+namespace Microsoft.Build.Internal
+{
+ class BuildTaskDatabase
+ {
+ const string default_tasks_file = "Microsoft.Common.tasks";
+ static readonly Dictionary<string,BuildTaskDatabase> default_factory = new Dictionary<string, BuildTaskDatabase> ();
+
+ public static BuildTaskDatabase GetDefaultTaskDatabase (Toolset toolset)
+ {
+ if (toolset == null)
+ throw new ArgumentNullException ("toolset");
+ BuildTaskDatabase defaults;
+ if (!default_factory.TryGetValue (toolset.ToolsVersion, out defaults)) {
+ defaults = new BuildTaskDatabase (toolset);
+ }
+ return defaults;
+ }
+
+ // for 'default' tasks.
+ BuildTaskDatabase (Toolset toolset)
+ {
+ ProjectRootElement root;
+ using (var xml = XmlReader.Create (Path.Combine (toolset.ToolsPath, default_tasks_file)))
+ root = ProjectRootElement.Create (xml);
+ LoadUsingTasks (null, root);
+ }
+
+ public BuildTaskDatabase (ProjectInstance projectInstance, ProjectRootElement projectRootElement)
+ {
+ LoadUsingTasks (projectInstance, projectRootElement);
+ }
+
+ internal class TaskDescription
+ {
+ public TaskAssembly TaskAssembly { get; set; }
+ public string Name { get; set; }
+ public Type TaskFactoryType { get; set; }
+ public Type TaskType { get; set; }
+ public IDictionary<string, TaskPropertyInfo> TaskFactoryParameters { get; set; }
+ public string TaskBody { get; set; }
+
+ public bool IsMatch (string name)
+ {
+ int ridx = Name.LastIndexOf ('.');
+ int tidx = name.IndexOf ('.');
+ return string.Equals (Name, name, StringComparison.OrdinalIgnoreCase) ||
+ tidx < 0 && ridx > 0 && string.Equals (Name.Substring (ridx + 1), name, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+
+ internal class TaskAssembly
+ {
+ public string AssemblyName { get; set; }
+ public string AssemblyFile { get; set; }
+ public Assembly LoadedAssembly { get; set; }
+ }
+
+ readonly List<TaskAssembly> assemblies = new List<TaskAssembly> ();
+ readonly List<TaskDescription> task_descs = new List<TaskDescription> ();
+
+ public List<TaskDescription> Tasks {
+ get { return task_descs; }
+ }
+
+ void LoadUsingTasks (ProjectInstance projectInstance, ProjectRootElement project)
+ {
+ Func<string,bool> cond = s => projectInstance != null ? projectInstance.EvaluateCondition (s) : Convert.ToBoolean (s);
+ foreach (var ut in project.UsingTasks) {
+ var ta = assemblies.FirstOrDefault (a => a.AssemblyFile.Equals (ut.AssemblyFile, StringComparison.OrdinalIgnoreCase) || a.AssemblyName.Equals (ut.AssemblyName, StringComparison.OrdinalIgnoreCase));
+ if (ta == null) {
+ ta = new TaskAssembly () { AssemblyName = ut.AssemblyName, AssemblyFile = ut.AssemblyFile };
+ ta.LoadedAssembly = ta.AssemblyName != null ? Assembly.Load (ta.AssemblyName) : Assembly.LoadFile (ta.AssemblyFile);
+ assemblies.Add (ta);
+ }
+ var pg = ut.ParameterGroup == null ? null : ut.ParameterGroup.Parameters.Select (p => new TaskPropertyInfo (p.Name, Type.GetType (p.ParameterType), cond (p.Output), cond (p.Required)))
+ .ToDictionary (p => p.Name);
+ var task = new TaskDescription () {
+ TaskAssembly = ta,
+ Name = ut.TaskName,
+ TaskFactoryType = string.IsNullOrEmpty (ut.TaskFactory) ? null : LoadTypeFrom (ta.LoadedAssembly, ut.TaskName, ut.TaskFactory),
+ TaskType = string.IsNullOrEmpty (ut.TaskFactory) ? LoadTypeFrom (ta.LoadedAssembly, ut.TaskName, ut.TaskName) : null,
+ TaskFactoryParameters = pg,
+ TaskBody = ut.TaskBody != null && cond (ut.TaskBody.Condition) ? ut.TaskBody.Evaluate : null,
+ };
+ task_descs.Add (task);
+ }
+ }
+
+ Type LoadTypeFrom (Assembly a, string taskName, string possiblyShortTypeName)
+ {
+ Type type = a.GetType (possiblyShortTypeName, false, true);
+ if (possiblyShortTypeName.IndexOf ('.') < 0)
+ type = a.GetTypes ().FirstOrDefault (t => t.Name == possiblyShortTypeName);
+ if (type == null)
+ throw new InvalidOperationException (string.Format ("For task '{0}' Specified type '{1}' was not found in assembly '{2}'", taskName, possiblyShortTypeName, a.FullName));
+ return type;
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskFactory.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskFactory.cs
new file mode 100644
index 0000000000..c587b35136
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/BuildTaskFactory.cs
@@ -0,0 +1,81 @@
+// BuildTaskFactory.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Internal;
+using System.Collections.Generic;
+using Microsoft.Build.Execution;
+
+namespace Microsoft.Build.Internal
+{
+ class BuildTaskFactory
+ {
+ public BuildTaskFactory (BuildTaskDatabase builtInDatabase, BuildTaskDatabase perProjectDatabase)
+ {
+ this.built_in_database = builtInDatabase;
+ this.per_project_database = perProjectDatabase;
+ }
+
+ readonly BuildTaskDatabase built_in_database, per_project_database;
+ readonly List<ITaskFactory> task_factories = new List<ITaskFactory> ();
+
+ public void ResetCaches ()
+ {
+ task_factories.Clear ();
+ }
+
+ public ITask CreateTask (string name, IDictionary<string,string> factoryIdentityParameters, IBuildEngine engine)
+ {
+ Func<BuildTaskDatabase.TaskDescription,bool> fn = t => t.IsMatch (name);
+ var td = per_project_database.Tasks.FirstOrDefault (fn) ?? built_in_database.Tasks.FirstOrDefault (fn);
+ if (td == null)
+ throw new InvalidOperationException (string.Format ("Task '{0}' could not be found", name));
+ if (td.TaskFactoryType != null) {
+ var tf = task_factories.FirstOrDefault (f => f.GetType () == td.TaskFactoryType);
+ if (tf == null) {
+ tf = (ITaskFactory) Activator.CreateInstance (td.TaskFactoryType);
+#if NET_4_5
+ var tf2 = tf as ITaskFactory2;
+ if (tf2 != null)
+ tf2.Initialize (name, factoryIdentityParameters, td.TaskFactoryParameters, td.TaskBody, engine);
+ else
+#endif
+ tf.Initialize (name, td.TaskFactoryParameters, td.TaskBody, engine);
+ task_factories.Add (tf);
+ }
+ return tf.CreateTask (engine);
+ }
+ else
+ return (ITask) Activator.CreateInstance (td.TaskType);
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionConstructs.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionConstructs.cs
new file mode 100644
index 0000000000..358f58e925
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionConstructs.cs
@@ -0,0 +1,197 @@
+//
+// ExpressionConstructs.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Microsoft.Build.Internal.Expressions
+{
+
+ class Locatable
+ {
+ public ILocation Location { get; set; }
+ }
+
+ partial class ExpressionList : ILocation, IEnumerable<Expression>
+ {
+ public ExpressionList ()
+ {
+ }
+
+ public ExpressionList (Expression entry)
+ {
+ Add (entry);
+ }
+
+ public int Count {
+ get { return list.Count; }
+ }
+
+ //public int Line {
+ // get { return list.Count == 0 ? 0 : list [0].Line; }
+ //}
+ public int Column {
+ get { return list.Count == 0 ? 0 : list [0].Column; }
+ }
+ public string File {
+ get { return list.Count == 0 ? null : list [0].File; }
+ }
+ public string ToLocationString ()
+ {
+ return list.Count == 0 ? null : list [0].Location.ToLocationString ();
+ }
+
+ public IEnumerator<Expression> GetEnumerator ()
+ {
+ return list.GetEnumerator ();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return list.GetEnumerator ();
+ }
+
+ List<Expression> list = new List<Expression> ();
+
+ public ExpressionList Add (Expression expr)
+ {
+ list.Add (expr);
+ return this;
+ }
+
+ public ExpressionList Insert (int pos, Expression expr)
+ {
+ list.Insert (pos, expr);
+ return this;
+ }
+ }
+
+ abstract partial class Expression : Locatable, ILocation
+ {
+ //public int Line {
+ // get { return Location.Line; }
+ //}
+ public int Column {
+ get { return Location.Column; }
+ }
+ public string File {
+ get { return Location.File; }
+ }
+ public string ToLocationString ()
+ {
+ return Location.ToLocationString ();
+ }
+ }
+
+ enum Operator
+ {
+ EQ,
+ NE,
+ LT,
+ LE,
+ GT,
+ GE,
+ And,
+ Or
+ }
+
+ partial class BinaryExpression : Expression
+ {
+ public Operator Operator { get; set; }
+ public Expression Left { get; set; }
+ public Expression Right { get; set; }
+ }
+
+ partial class BooleanLiteral : Expression
+ {
+ public bool Value { get; set; }
+ }
+
+ partial class NotExpression : Expression
+ {
+ public Expression Negated { get; set; }
+ }
+
+ partial class PropertyAccessExpression : Expression
+ {
+ public PropertyAccess Access { get; set; }
+ }
+
+ enum PropertyTargetType
+ {
+ Object,
+ Type,
+ }
+
+ class PropertyAccess : Locatable
+ {
+ public NameToken Name { get; set; }
+ public Expression Target { get; set; }
+ public PropertyTargetType TargetType { get; set; }
+ public ExpressionList Arguments { get; set; }
+ }
+
+ partial class ItemAccessExpression : Expression
+ {
+ public ItemApplication Application { get; set; }
+ }
+
+ class ItemApplication : Locatable
+ {
+ public NameToken Name { get; set; }
+ public ExpressionList Expressions { get; set; }
+ }
+
+ partial class MetadataAccessExpression : Expression
+ {
+ public MetadataAccess Access { get; set; }
+ }
+
+ class MetadataAccess : Locatable
+ {
+ public NameToken Metadata { get; set; }
+ public NameToken ItemType { get; set; }
+ }
+
+ partial class StringLiteral : Expression
+ {
+ public NameToken Value { get; set; }
+ }
+
+ partial class RawStringLiteral : Expression
+ {
+ public NameToken Value { get; set; }
+ }
+
+ partial class FunctionCallExpression : Expression
+ {
+ public NameToken Name { get; set; }
+ public ExpressionList Arguments { get; set; }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionEvaluator.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionEvaluator.cs
new file mode 100644
index 0000000000..4aa9b259f2
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionEvaluator.cs
@@ -0,0 +1,521 @@
+//
+// ExpressionEvaluator.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Linq;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Exceptions;
+using System.Collections.Generic;
+using System.Reflection;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using System.IO;
+
+namespace Microsoft.Build.Internal.Expressions
+{
+ class ExpressionEvaluator
+ {
+ public ExpressionEvaluator (Project project, string replacementForMissingPropertyAndItem)
+ {
+ ReplacementForMissingPropertyAndItem = replacementForMissingPropertyAndItem;
+ Project = project;
+ /*
+ GetItems = (name) => project.GetItems (name).Select (i => new KeyValuePair<string,string> (i.ItemType, i.EvaluatedInclude));
+ GetProperty = (name) => {
+ var prop = project.GetProperty (name);
+ return new KeyValuePair<string,string> (prop != null ? prop.Name : null, prop != null ? prop.EvaluatedValue : null);
+ };
+ */
+ }
+
+ public ExpressionEvaluator (ProjectInstance project, string replacementForMissingPropertyAndItem)
+ {
+ ReplacementForMissingPropertyAndItem = replacementForMissingPropertyAndItem;
+ ProjectInstance = project;
+ /*
+ GetItems = (name) => project.GetItems (name).Select (i => new KeyValuePair<string,string> (i.ItemType, i.EvaluatedInclude));
+ GetProperty = (name) => {
+ var prop = project.GetProperty (name);
+ return new KeyValuePair<string,string> (prop != null ? prop.Name : null, prop != null ? prop.EvaluatedValue : null);
+ };
+ */
+ }
+
+ EvaluationContext CreateContext (string source)
+ {
+ return new EvaluationContext (source, this);
+ }
+
+ public Project Project { get; private set; }
+ public ProjectInstance ProjectInstance { get; set; }
+ //public Func<string,IEnumerable<KeyValuePair<string,string>>> GetItems { get; private set; }
+ //public Func<string,KeyValuePair<string,string>> GetProperty { get; private set; }
+
+ public string ReplacementForMissingPropertyAndItem { get; set; }
+
+ // it is to prevent sequential property value expansion in boolean expression
+ public string Wrapper {
+ get { return ReplacementForMissingPropertyAndItem != null ? "'" : null; }
+ }
+
+ public string Evaluate (string source)
+ {
+ return Evaluate (source, new ExpressionParserManual (source ?? string.Empty, ExpressionValidationType.LaxString).Parse ());
+ }
+
+ string Evaluate (string source, ExpressionList exprList)
+ {
+ if (exprList == null)
+ throw new ArgumentNullException ("exprList");
+ return string.Concat (exprList.Select (e => e.EvaluateAsString (CreateContext (source))));
+ }
+
+ public bool EvaluateAsBoolean (string source)
+ {
+ try {
+ var el = new ExpressionParser ().Parse (source, ExpressionValidationType.StrictBoolean);
+ if (el.Count () != 1)
+ throw new InvalidProjectFileException ("Unexpected number of tokens: " + el.Count ());
+ return el.First ().EvaluateAsBoolean (CreateContext (source));
+ } catch (yyParser.yyException ex) {
+ throw new InvalidProjectFileException (string.Format ("failed to evaluate expression as boolean: '{0}': {1}", source, ex.Message), ex);
+ }
+ }
+ }
+
+ class EvaluationContext
+ {
+ public EvaluationContext (string source, ExpressionEvaluator evaluator)
+ {
+ Source = source;
+ Evaluator = evaluator;
+ }
+
+ public string Source { get; private set; }
+
+ public ExpressionEvaluator Evaluator { get; private set; }
+ public object ContextItem { get; set; }
+
+ Stack<object> evaluating_items = new Stack<object> ();
+ Stack<object> evaluating_props = new Stack<object> ();
+
+ public IEnumerable<object> GetItems (string name)
+ {
+ if (Evaluator.Project != null)
+ return Evaluator.Project.GetItems (name);
+ else
+ return Evaluator.ProjectInstance.GetItems (name);
+ }
+
+ public IEnumerable<object> GetAllItems ()
+ {
+ if (Evaluator.Project != null)
+ return Evaluator.Project.AllEvaluatedItems;
+ else
+ return Evaluator.ProjectInstance.AllEvaluatedItems;
+ }
+
+ public string EvaluateItem (string itemType, object item)
+ {
+ if (evaluating_items.Contains (item))
+ throw new InvalidProjectFileException (string.Format ("Recursive reference to item '{0}' was found", itemType));
+ try {
+ evaluating_items.Push (item);
+ var eval = item as ProjectItem;
+ if (eval != null)
+ return Evaluator.Evaluate (eval.EvaluatedInclude);
+ else
+ return Evaluator.Evaluate (((ProjectItemInstance) item).EvaluatedInclude);
+ } finally {
+ evaluating_items.Pop ();
+ }
+ }
+
+ public string EvaluateProperty (string name)
+ {
+ if (Evaluator.Project != null) {
+ var prop = Evaluator.Project.GetProperty (name);
+ if (prop == null)
+ return null;
+ return EvaluateProperty (prop, prop.Name, prop.EvaluatedValue);
+ } else {
+ var prop = Evaluator.ProjectInstance.GetProperty (name);
+ if (prop == null)
+ return null;
+ return EvaluateProperty (prop, prop.Name, prop.EvaluatedValue);
+ }
+ }
+
+ public string EvaluateProperty (object prop, string name, string value)
+ {
+ if (evaluating_props.Contains (prop))
+ throw new InvalidProjectFileException (string.Format ("Recursive reference to property '{0}' was found", name));
+ try {
+ evaluating_props.Push (prop);
+ // FIXME: needs verification on whether string evaluation is appropriate or not.
+ return Evaluator.Evaluate (value);
+ } finally {
+ evaluating_props.Pop ();
+ }
+ }
+ }
+
+ abstract partial class Expression
+ {
+ public abstract string EvaluateAsString (EvaluationContext context);
+ public abstract bool EvaluateAsBoolean (EvaluationContext context);
+ public abstract object EvaluateAsObject (EvaluationContext context);
+
+ public bool EvaluateStringAsBoolean (EvaluationContext context, string ret)
+ {
+ if (ret != null) {
+ if (ret.Equals ("TRUE", StringComparison.InvariantCultureIgnoreCase))
+ return true;
+ else if (ret.Equals ("FALSE", StringComparison.InvariantCultureIgnoreCase))
+ return false;
+ }
+ throw new InvalidProjectFileException (this.Location, string.Format ("Condition '{0}' is evaluated as '{1}' and cannot be converted to boolean", context.Source, ret));
+ }
+ }
+
+ partial class BinaryExpression : Expression
+ {
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ switch (Operator) {
+ case Operator.EQ:
+ return string.Equals (Left.EvaluateAsString (context), Right.EvaluateAsString (context), StringComparison.OrdinalIgnoreCase);
+ case Operator.NE:
+ return !string.Equals (Left.EvaluateAsString (context), Right.EvaluateAsString (context), StringComparison.OrdinalIgnoreCase);
+ case Operator.And:
+ case Operator.Or:
+ // evaluate first, to detect possible syntax error on right expr.
+ var lb = Left.EvaluateAsBoolean (context);
+ var rb = Right.EvaluateAsBoolean (context);
+ return Operator == Operator.And ? (lb && rb) : (lb || rb);
+ }
+ // comparison expressions - evaluate comparable first, then compare values.
+ var left = Left.EvaluateAsObject (context);
+ var right = Right.EvaluateAsObject (context);
+ if (!(left is IComparable && right is IComparable))
+ throw new InvalidProjectFileException ("expression cannot be evaluated as boolean");
+ var result = ((IComparable) left).CompareTo (right);
+ switch (Operator) {
+ case Operator.GE:
+ return result >= 0;
+ case Operator.GT:
+ return result > 0;
+ case Operator.LE:
+ return result <= 0;
+ case Operator.LT:
+ return result < 0;
+ }
+ throw new InvalidOperationException ();
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ throw new NotImplementedException ();
+ }
+
+ static readonly Dictionary<Operator,string> strings = new Dictionary<Operator, string> () {
+ {Operator.EQ, " == "},
+ {Operator.NE, " != "},
+ {Operator.LT, " < "},
+ {Operator.LE, " <= "},
+ {Operator.GT, " > "},
+ {Operator.GE, " >= "},
+ {Operator.And, " And "},
+ {Operator.Or, " Or "},
+ };
+
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ return Left.EvaluateAsString (context) + strings [Operator] + Right.EvaluateAsString (context);
+ }
+ }
+
+ partial class BooleanLiteral : Expression
+ {
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ return Value ? "True" : "False";
+ }
+
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ return Value;
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ return Value;
+ }
+ }
+
+ partial class NotExpression : Expression
+ {
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ // no negation for string
+ return "!" + Negated.EvaluateAsString (context);
+ }
+
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ return !Negated.EvaluateAsBoolean (context);
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ return EvaluateAsString (context);
+ }
+ }
+
+ partial class PropertyAccessExpression : Expression
+ {
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ var ret = EvaluateAsString (context);
+ return EvaluateStringAsBoolean (context, ret);
+ }
+
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ var ret = EvaluateAsObject (context);
+ // FIXME: this "wrapper" is kind of hack, to prevent sequential property references such as $(X)$(Y).
+ return ret == null ? context.Evaluator.ReplacementForMissingPropertyAndItem : context.Evaluator.Wrapper + ret.ToString () + context.Evaluator.Wrapper;
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ try {
+ return DoEvaluateAsObject (context);
+ } catch (TargetInvocationException ex) {
+ throw new InvalidProjectFileException ("Access to property caused an error", ex);
+ }
+ }
+
+ object DoEvaluateAsObject (EvaluationContext context)
+ {
+ if (Access.Target == null) {
+ return context.EvaluateProperty (Access.Name.Name);
+ } else {
+ if (this.Access.TargetType == PropertyTargetType.Object) {
+ var obj = Access.Target.EvaluateAsObject (context);
+ if (obj == null)
+ return null;
+ if (Access.Arguments != null) {
+ var args = Access.Arguments.Select (e => e.EvaluateAsObject (context)).ToArray ();
+ var method = FindMethod (obj.GetType (), Access.Name.Name, args);
+ if (method == null)
+ throw new InvalidProjectFileException (Location, string.Format ("access to undefined method '{0}' of '{1}' at {2}", Access.Name.Name, Access.Target.EvaluateAsString (context), Location));
+ return method.Invoke (obj, AdjustArgsForCall (method, args));
+ } else {
+ var prop = obj.GetType ().GetProperty (Access.Name.Name);
+ if (prop == null)
+ throw new InvalidProjectFileException (Location, string.Format ("access to undefined property '{0}' of '{1}' at {2}", Access.Name.Name, Access.Target.EvaluateAsString (context), Location));
+ return prop.GetValue (obj, null);
+ }
+ } else {
+ var type = Type.GetType (Access.Target.EvaluateAsString (context));
+ if (type == null)
+ throw new InvalidProjectFileException (Location, string.Format ("specified type '{0}' was not found", Access.Target.EvaluateAsString (context)));
+ if (Access.Arguments != null) {
+ var args = Access.Arguments.Select (e => e.EvaluateAsObject (context)).ToArray ();
+ var method = FindMethod (type, Access.Name.Name, args);
+ if (method == null)
+ throw new InvalidProjectFileException (Location, string.Format ("access to undefined static method '{0}' of '{1}' at {2}", Access.Name.Name, Access.Target.EvaluateAsString (context), Location));
+ return method.Invoke (null, AdjustArgsForCall (method, args));
+ } else {
+ var prop = type.GetProperty (Access.Name.Name);
+ if (prop == null)
+ throw new InvalidProjectFileException (Location, string.Format ("access to undefined static property '{0}' of '{1}' at {2}", Access.Name.Name, Access.Target.EvaluateAsString (context), Location));
+ return prop.GetValue (null, null);
+ }
+ }
+ }
+ }
+
+ MethodInfo FindMethod (Type type, string name, object [] args)
+ {
+ var methods = type.GetMethods ().Where (m => {
+ if (m.Name != name)
+ return false;
+ var pl = m.GetParameters ();
+ if (pl.Length == args.Length)
+ return true;
+ // calling String.Format() with either set of arguments is valid:
+ // - three strings (two for varargs)
+ // - two strings (happen to be exact match)
+ // - one string (no varargs)
+ if (pl.Length > 0 && pl.Length - 1 <= args.Length &&
+ pl.Last ().GetCustomAttributesData ().Any (a => a.Constructor.DeclaringType == typeof (ParamArrayAttribute)))
+ return true;
+ return false;
+ });
+ if (methods.Count () == 1)
+ return methods.First ();
+ return args.Any (a => a == null) ?
+ type.GetMethod (name) :
+ type.GetMethod (name, args.Select (o => o.GetType ()).ToArray ());
+ }
+
+ object [] AdjustArgsForCall (MethodInfo m, object[] args)
+ {
+ if (m.GetParameters ().Length == args.Length + 1)
+ return args.Concat (new object[] {Array.CreateInstance (m.GetParameters ().Last ().ParameterType.GetElementType (), 0)}).ToArray ();
+ else
+ return args;
+ }
+ }
+
+ partial class ItemAccessExpression : Expression
+ {
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ return EvaluateStringAsBoolean (context, EvaluateAsString (context));
+ }
+
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ string itemType = Application.Name.Name;
+ var items = context.GetItems (itemType);
+ if (!items.Any ())
+ return context.Evaluator.ReplacementForMissingPropertyAndItem;
+ if (Application.Expressions == null)
+ return string.Join (";", items.Select (item => context.EvaluateItem (itemType, item)));
+ else
+ return string.Join (";", items.Select (item => {
+ context.ContextItem = item;
+ var ret = string.Concat (Application.Expressions.Select (e => e.EvaluateAsString (context)));
+ context.ContextItem = null;
+ return ret;
+ }));
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ return EvaluateAsString (context);
+ }
+ }
+
+ partial class MetadataAccessExpression : Expression
+ {
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ return EvaluateStringAsBoolean (context, EvaluateAsString (context));
+ }
+
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ string itemType = this.Access.ItemType != null ? this.Access.ItemType.Name : null;
+ string metadataName = Access.Metadata.Name;
+ IEnumerable<object> items;
+ if (this.Access.ItemType != null)
+ items = context.GetItems (itemType);
+ else if (context.ContextItem != null)
+ items = new Object [] { context.ContextItem };
+ else
+ items = context.GetAllItems ();
+
+ var values = items.Select (i => (i is ProjectItem) ? ((ProjectItem) i).GetMetadataValue (metadataName) : ((ProjectItemInstance) i).GetMetadataValue (metadataName)).Where (s => !string.IsNullOrEmpty (s));
+ return string.Join (";", values);
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ return EvaluateAsString (context);
+ }
+ }
+ partial class StringLiteral : Expression
+ {
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ var ret = EvaluateAsString (context);
+ return EvaluateStringAsBoolean (context, ret);
+ }
+
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ return context.Evaluator.Evaluate (this.Value.Name);
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ return EvaluateAsString (context);
+ }
+ }
+ partial class RawStringLiteral : Expression
+ {
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ return Value.Name;
+ }
+
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ throw new InvalidProjectFileException ("raw string literal cannot be evaluated as boolean");
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ return EvaluateAsString (context);
+ }
+ }
+
+ partial class FunctionCallExpression : Expression
+ {
+ public override string EvaluateAsString (EvaluationContext context)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override bool EvaluateAsBoolean (EvaluationContext context)
+ {
+ if (string.Equals (Name.Name, "Exists", StringComparison.OrdinalIgnoreCase)) {
+ if (Arguments.Count != 1)
+ throw new InvalidProjectFileException (Location, "Function 'Exists' expects 1 argument");
+ string val = Arguments.First ().EvaluateAsString (context);
+ return Directory.Exists (val) || System.IO.File.Exists (val);
+ }
+ if (string.Equals (Name.Name, "HasTrailingSlash", StringComparison.OrdinalIgnoreCase)) {
+ if (Arguments.Count != 1)
+ throw new InvalidProjectFileException (Location, "Function 'HasTrailingSlash' expects 1 argument");
+ string val = Arguments.First ().EvaluateAsString (context);
+ return val.LastOrDefault () == '\\' || val.LastOrDefault () == '/';
+ }
+ throw new InvalidProjectFileException (Location, string.Format ("Unsupported function '{0}'", Name));
+ }
+
+ public override object EvaluateAsObject (EvaluationContext context)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParser.jay b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParser.jay
new file mode 100644
index 0000000000..200c96ba83
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParser.jay
@@ -0,0 +1,264 @@
+//
+// ExpressionParser.jay
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+%{
+
+using System;
+using System.Text;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Exceptions;
+using Microsoft.Build.Framework;
+
+/*
+
+Pseudo formal syntax for .NET 4.0 expression:
+
+Condition = Expression
+Include = Expression*
+
+ Expression
+ BooleanLiteral
+ TrueLiteral
+ FalseLiteral
+ BinaryExpression
+ Expression "==" Expression
+ Expression "!=" Expression
+ Expression ">" Expression
+ Expression ">=" Expression
+ Expression "<" Expression
+ Expression "<=" Expression
+ Expression "And" Expression
+ Expression "Or" Expression
+ UnaryExpression
+ "!" Expression
+ PropertyExpression
+ "$(" PropertyApplication ")"
+ ItemExpression
+ "@(" ItemApplication ")"
+ MetadataBatchingExpression
+ "%(" MetadataBatchingApplication ")"
+ StringLiteralOrFunction
+ StringLiteralOrFunctionName ( "(" FunctionArguments ")" )?
+
+.NET error messages are so detailed which is something like "you forgot '(' after '$' ?" - so
+it is likely that the MS tokenizer is hand-written.
+
+*/
+
+namespace Microsoft.Build.Internal.Expressions
+{
+ class ExpressionParser
+ {
+ static readonly int yacc_verbose_flag = Environment.GetEnvironmentVariable ("MONO_MSBUILD_PARSER_DEBUG") == "1" ? 1 : 0;
+
+ object debug_obj = yacc_verbose_flag == 0 ? null : new yydebug.yyDebugSimple ();
+
+ public ExpressionList Parse (string source, ExpressionValidationType validationType)
+ {
+ var tokenizer = new ExpressionTokenizer (source, validationType);
+ return (ExpressionList) yyparse (tokenizer, debug_obj);
+ }
+
+ BinaryExpression Binary (Operator op, object left, object right)
+ {
+ return new BinaryExpression () { Operator = op, Left = (Expression) left, Right = (Expression) right, Location = (ILocation) left };
+ }
+%}
+
+%token TRUE_LITERAL
+%token FALSE_LITERAL
+%token STRING_LITERAL
+%token EQ // ==
+%token NE // !=
+%token GT // >
+%token GE // >=
+%token LT // <
+%token LE // <=
+%token AND // AND
+%token OR // OR
+%token NOT //!
+%token DOT //.
+%token COMMA //,
+%token PROP_OPEN // $(
+%token ITEM_OPEN // @(
+%token METADATA_OPEN // %(
+%token PAREN_OPEN // (
+%token PAREN_CLOSE // )
+%token BRACE_OPEN // [
+%token BRACE_CLOSE // ]
+%token COLON2 // ::
+%token ARROW // ->
+%token NAME
+%token ERROR
+
+%start ExpressionList
+
+%%
+
+ExpressionList
+ : /* empty */
+ { $$ = new ExpressionList (); }
+ | ExpressionList Expression
+ { $$ = ((ExpressionList) $1).Add ((Expression) $2); }
+ ;
+
+Expression
+ : LogicalExpression
+ ;
+
+LogicalExpression
+ : ComparisonExpression
+ | LogicalExpression AND LogicalExpression
+ { $$ = Binary (Operator.And, $1, $3); }
+ | LogicalExpression OR LogicalExpression
+ { $$ = Binary (Operator.Or, $1, $3); }
+ ;
+
+ComparisonExpression
+ : UnaryExpression
+ | UnaryExpression EQ UnaryExpression
+ { $$ = Binary (Operator.EQ, $1, $3); }
+ | UnaryExpression NE UnaryExpression
+ { $$ = Binary (Operator.NE, $1, $3); }
+ | UnaryExpression GT UnaryExpression
+ { $$ = Binary (Operator.GT, $1, $3); }
+ | UnaryExpression GE UnaryExpression
+ { $$ = Binary (Operator.GE, $1, $3); }
+ | UnaryExpression LT UnaryExpression
+ { $$ = Binary (Operator.LT, $1, $3); }
+ | UnaryExpression LE UnaryExpression
+ { $$ = Binary (Operator.LE, $1, $3); }
+ ;
+
+UnaryExpression
+ : PrimaryExpression
+ | NOT UnaryExpression
+ { $$ = new NotExpression () { Negated = (Expression) $2, Location = (ILocation) $1 }; }
+ ;
+
+PrimaryExpression
+ : BooleanLiteral
+ | StringLiteral
+ | UnaryExpression
+ | PropertyAccessExpression
+ | ItemAccessExpression
+ | MetadataAccessExpression
+ | RawStringLiteralOrFunction
+ | ParenthesizedExpression
+ ;
+
+BooleanLiteral
+ : TRUE_LITERAL
+ { $$ = new BooleanLiteral () { Value = true, Location = (ILocation) $1 }; }
+ | FALSE_LITERAL
+ { $$ = new BooleanLiteral () { Value = false, Location = (ILocation) $1 }; }
+ ;
+
+PropertyAccessExpression
+ : PROP_OPEN PropertyAccess PAREN_CLOSE
+ { $$ = new PropertyAccessExpression () { Access = (PropertyAccess) $2, Location = (ILocation) $1 }; }
+ ;
+
+PropertyAccess
+ : NAME
+ { $$ = new PropertyAccess () { Name = (NameToken) $1, TargetType = PropertyTargetType.Object, Location = (NameToken) $1 }; }
+ | Expression DOT NAME
+ { $$ = new PropertyAccess () { Name = (NameToken) $3, Target = (Expression) $1, TargetType = PropertyTargetType.Object, Location = (ILocation) $1 }; }
+ | BRACE_OPEN QualifiedNameExpression BRACE_CLOSE COLON2 NAME
+ { $$ = new PropertyAccess () { Name = (NameToken) $5, Target = (Expression) $2, TargetType = PropertyTargetType.Type, Location = (ILocation) $1 }; }
+ | BRACE_OPEN QualifiedNameExpression BRACE_CLOSE COLON2 NAME PAREN_OPEN FunctionCallArguments PAREN_CLOSE
+ { $$ = new PropertyAccess () { Name = (NameToken) $5, Target = (Expression) $2, TargetType = PropertyTargetType.Type, Arguments = (ExpressionList) $7, Location = (ILocation) $1 }; }
+ ;
+
+QualifiedNameExpression
+ : QualifiedName
+ { $$ = new StringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
+ ;
+
+QualifiedName
+ : NAME
+ | QualifiedName DOT NAME
+ { $$ = new NameToken () { Name = ((NameToken) $1).Name + "." + ((NameToken) $3).Name, Column = ((ILocation) $1).Column }; }
+ ;
+
+ItemAccessExpression
+ : ITEM_OPEN ItemApplication PAREN_CLOSE
+ { $$ = new ItemAccessExpression () { Application = (ItemApplication) $2, Location = (ILocation) $1 }; }
+ ;
+
+// looking a bit messy, but gives different location
+ItemApplication
+ : NAME
+ { $$ = new ItemApplication () { Name = (NameToken) $1, Location = (ILocation) $1 }; }
+ | NAME ARROW ExpressionList
+ { $$ = new ItemApplication () { Name = (NameToken) $1, Expressions = (ExpressionList) $3, Location = (ILocation) $1 }; }
+ ;
+
+MetadataAccessExpression
+ : METADATA_OPEN MetadataAccess PAREN_CLOSE
+ { $$ = new MetadataAccessExpression () { Access = (MetadataAccess) $2, Location = (ILocation) $1 }; }
+ ;
+
+// looking a bit messy, but gives different location
+MetadataAccess
+ : NAME
+ { $$ = new MetadataAccess () { Metadata = (NameToken) $1, Location = (ILocation) $1 }; }
+ | NAME DOT NAME
+ { $$ = new MetadataAccess () { ItemType = (NameToken) $1, Metadata = (NameToken) $3, Location = (ILocation) $1 }; }
+ ;
+
+StringLiteral
+ : STRING_LITERAL
+ { $$ = new StringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
+ ;
+
+RawStringLiteralOrFunction
+ : NAME
+ { $$ = new RawStringLiteral () { Value = (NameToken) $1, Location = (ILocation) $1 }; }
+ | NAME PAREN_OPEN PAREN_CLOSE
+ { $$ = new FunctionCallExpression () { Name = (NameToken) $1, Arguments = new ExpressionList (), Location = (ILocation) $1 }; }
+ | NAME PAREN_OPEN FunctionCallArguments PAREN_CLOSE
+ { $$ = new FunctionCallExpression () { Name = (NameToken) $1, Arguments = (ExpressionList) $3, Location = (ILocation) $1 }; }
+ ;
+
+FunctionCallArguments
+ : /* empty */
+ { $$ = new ExpressionList (); }
+ | Expression
+ { $$ = new ExpressionList ().Add ((Expression) $1); }
+ | FunctionCallArguments COMMA Expression
+ { $$ = ((ExpressionList) $1).Add ((Expression) $3); }
+ ;
+
+ParenthesizedExpression
+ : PAREN_OPEN Expression PAREN_CLOSE
+ { $$ = (Expression) $2; }
+ ;
+
+%%
+
+ }
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParserManual.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParserManual.cs
new file mode 100644
index 0000000000..f177505587
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionParserManual.cs
@@ -0,0 +1,271 @@
+//
+// ExpressionParserManual.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Exceptions;
+
+namespace Microsoft.Build.Internal.Expressions
+{
+ class ExpressionParserManual
+ {
+ // FIXME: we are going to not need ExpressionValidationType for this; always LaxString.
+ public ExpressionParserManual (string source, ExpressionValidationType validationType)
+ {
+ if (source == null)
+ throw new ArgumentNullException ("source");
+ this.source = source;
+ validation_type = validationType;
+ }
+
+ string source;
+ ExpressionValidationType validation_type;
+
+ public ExpressionList Parse ()
+ {
+ return Parse (0, source.Length);
+ }
+
+ ExpressionList Parse (int start, int end)
+ {
+ if (string.IsNullOrWhiteSpace (source))
+ return new ExpressionList ();
+
+ var ret = new ExpressionList ();
+ while (start < end) {
+ int bak = start;
+ ret.Add (ParseSingle (ref start, end));
+ SkipSpaces (ref start);
+ if (bak == start)
+ throw new Exception ("Parser failed to progress token position: " + source);
+ }
+ return ret;
+ }
+
+ static readonly char [] token_starters = "$@%(),".ToCharArray ();
+
+ Expression ParseSingle (ref int start, int end)
+ {
+ char token = source [start];
+ switch (token) {
+ case '$':
+ case '@':
+ case '%':
+ if (start == end || start + 1 == source.Length || source [start + 1] != '(') {
+ if (validation_type == ExpressionValidationType.StrictBoolean)
+ throw new InvalidProjectFileException (string.Format ("missing '(' after '{0}' at {1} in \"{2}\"", source [start], start, source));
+ else
+ goto default; // treat as raw literal to the section end
+ }
+ start += 2;
+ int last = FindMatchingCloseParen (start, end);
+ if (last < 0) {
+ if (validation_type == ExpressionValidationType.StrictBoolean)
+ throw new InvalidProjectFileException (string.Format ("expression did not have matching ')' since index {0} in \"{1}\"", start, source));
+ else {
+ start -= 2;
+ goto default; // treat as raw literal to the section end
+ }
+ }
+ Expression ret;
+ if (token == '$')
+ ret = EvaluatePropertyExpression (start, last);
+ else if (token == '%')
+ ret = EvaluateMetadataExpression (start, last);
+ else
+ ret = EvaluateItemExpression (start, last);
+ start = last + 1;
+ return ret;
+
+ // Below (until default) are important only for Condition evaluation
+ case '(':
+ if (validation_type == ExpressionValidationType.LaxString)
+ goto default;
+ start++;
+ last = FindMatchingCloseParen (start, end);
+ if (last < 0) {
+ if (validation_type == ExpressionValidationType.StrictBoolean)
+ throw new InvalidProjectFileException (string.Format ("expression did not have matching ')' since index {0} in \"{1}\"", start, source));
+ else {
+ start--;
+ goto default; // treat as raw literal to the section end
+ }
+ }
+ var contents = Parse (start, last).ToArray ();
+ if (contents.Length > 1)
+ throw new InvalidProjectFileException (string.Format ("unexpected continuous expression within (){0} in \"{1}\"", contents [1].Column > 0 ? " at " + contents [1].Column : null, source));
+ return contents.First ();
+
+ default:
+ int idx = source.IndexOfAny (token_starters, start + 1);
+ string name = idx < 0 ? source.Substring (start, end - start) : source.Substring (start, idx - start);
+ var val = new NameToken () { Name = name };
+ ret = new RawStringLiteral () { Value = val };
+ if (idx >= 0)
+ start = idx;
+ else
+ start = end;
+
+ return ret;
+ }
+ }
+
+ int FindMatchingCloseParen (int start, int end)
+ {
+ int n = 0;
+ for (int i = start; i < end; i++) {
+ if (source [i] == '(')
+ n++;
+ else if (source [i] == ')') {
+ if (n-- == 0)
+ return i;
+ }
+ }
+ return -1; // invalid
+ }
+
+ static readonly string spaces = " \t\r\n";
+
+ void SkipSpaces (ref int start)
+ {
+ while (start < source.Length && spaces.Contains (source [start]))
+ start++;
+ }
+
+ PropertyAccessExpression EvaluatePropertyExpression (int start, int end)
+ {
+ // member access
+ int dotAt = source.LastIndexOf ('.', end, end - start);
+ int colonsAt = source.LastIndexOf ("::", end, end - start, StringComparison.Ordinal);
+ if (dotAt < 0 && colonsAt < 0) {
+ // property access without member specification
+ int parenAt = source.IndexOf ('(', start, end - start);
+ string name = parenAt < 0 ? source.Substring (start, end - start) : source.Substring (start, parenAt - start);
+ var access = new PropertyAccess () {
+ Name = new NameToken () { Name = name },
+ TargetType = PropertyTargetType.Object
+ };
+ if (parenAt > 0) { // method arguments
+ start = parenAt + 1;
+ access.Arguments = ParseFunctionArguments (ref start, end);
+ }
+ return new PropertyAccessExpression () { Access = access };
+ }
+ if (colonsAt < 0 || colonsAt < dotAt) {
+ // property access with member specification
+ int mstart = dotAt + 1;
+ int parenAt = source.IndexOf ('(', mstart, end - mstart);
+ string name = parenAt < 0 ? source.Substring (mstart, end - mstart) : source.Substring (mstart, parenAt - mstart);
+ var access = new PropertyAccess () {
+ Name = new NameToken () { Name = name },
+ TargetType = PropertyTargetType.Object,
+ Target = dotAt < 0 ? null : Parse (start, dotAt).FirstOrDefault ()
+ };
+ if (parenAt > 0) { // method arguments
+ start = parenAt + 1;
+ access.Arguments = ParseFunctionArguments (ref start, end);
+ }
+ return new PropertyAccessExpression () { Access = access };
+ } else {
+ // static type access
+ string type = source.Substring (start, colonsAt - start);
+ if (type.Length < 2 || type [0] != '[' || type [type.Length - 1] != ']')
+ throw new InvalidProjectFileException (string.Format ("Static function call misses appropriate type name surrounded by '[' and ']' at {0} in \"{1}\"", start, source));
+ type = type.Substring (1, type.Length - 2);
+ start = colonsAt + 2;
+ int parenAt = source.IndexOf ('(', start, end - start);
+ string member = parenAt < 0 ? source.Substring (start, end - start) : source.Substring (start, parenAt - start);
+ if (member.Length == 0)
+ throw new InvalidProjectFileException ("Static member name is missing");
+ var access = new PropertyAccess () {
+ Name = new NameToken () { Name = member },
+ TargetType = PropertyTargetType.Type,
+ Target = new StringLiteral () { Value = new NameToken () { Name = type } }
+ };
+ if (parenAt > 0) { // method arguments
+ start = parenAt + 1;
+ access.Arguments = ParseFunctionArguments (ref start, end);
+ }
+ return new PropertyAccessExpression () { Access = access };
+ }
+ }
+
+ ExpressionList ParseFunctionArguments (ref int start, int end)
+ {
+ var args = new ExpressionList ();
+ do {
+ SkipSpaces (ref start);
+ if (start == source.Length)
+ throw new InvalidProjectFileException ("unterminated function call arguments.");
+ if (source [start] == ')')
+ break;
+ else if (args.Any ()) {
+ if (source [start] != ',')
+ throw new InvalidProjectFileException (string.Format ("invalid function call arguments specification. ',' is expected, got '{0}'", source [start]));
+ start++;
+ }
+ args.Add (ParseSingle (ref start, end));
+ } while (true);
+ start++;
+ return args;
+ }
+
+ ItemAccessExpression EvaluateItemExpression (int start, int end)
+ {
+ // using property as context and evaluate
+ int idx = source.IndexOf ("->", start, StringComparison.Ordinal);
+ if (idx > 0) {
+ string name = source.Substring (start, idx - start);
+ return new ItemAccessExpression () {
+ Application = new ItemApplication () {
+ Name = new NameToken () { Name = name },
+ Expressions = Parse (idx, end - idx)
+ }
+ };
+ } else {
+ string name = source.Substring (start, end - start);
+ return new ItemAccessExpression () {
+ Application = new ItemApplication () { Name = new NameToken () { Name = name } }
+ };
+ }
+ }
+
+ MetadataAccessExpression EvaluateMetadataExpression (int start, int end)
+ {
+ int idx = source.IndexOf ('.', start, end - start);
+ string item = idx < 0 ? null : source.Substring (start, idx);
+ string meta = idx < 0 ? source.Substring (start, end - start) : source.Substring (idx + 1, end - idx - 1);
+ var access = new MetadataAccess () {
+ ItemType = item == null ? null : new NameToken () { Column = start, Name = item },
+ Metadata = new NameToken () { Column = idx < 0 ? start : idx + 1, Name = meta }
+ };
+ return new MetadataAccessExpression () { Access = access };
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionTokenizer.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionTokenizer.cs
new file mode 100644
index 0000000000..9760ad2acb
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ExpressionTokenizer.cs
@@ -0,0 +1,309 @@
+//
+// ExpressionTokenizer.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Collections.Generic;
+using Microsoft.Build.Evaluation;
+
+namespace Microsoft.Build.Internal.Expressions
+{
+ enum ExpressionValidationType
+ {
+ LaxString,
+ StrictBoolean,
+ }
+
+ enum TokenizerMode
+ {
+ Default,
+ InsideItemOrProperty,
+ }
+
+ class ExpressionTokenizer : yyParser.yyInput
+ {
+ public ExpressionTokenizer (string source, ExpressionValidationType validationType)
+ {
+ this.source = source;
+ current_token_position = -1;
+ validation_type = validationType;
+ modes.Push (TokenizerMode.Default);
+ }
+
+ string source;
+ ExpressionValidationType validation_type;
+
+ int current_token;
+ string error;
+ int pos, current_token_position;
+ object token_value;
+ Stack<TokenizerMode> modes = new Stack<TokenizerMode> ();
+
+ TokenizerMode CurrentTokenizerMode {
+ get { return modes.Peek (); }
+ }
+
+ public bool advance ()
+ {
+ if (pos == source.Length)
+ return false;
+
+ error = null;
+ token_value = null;
+
+ while (pos < source.Length) {
+ if (spaces.IndexOf (source [pos]) >= 0)
+ pos++;
+ else
+ break;
+ }
+ if (pos == source.Length)
+ return false;
+ current_token_position = pos;
+
+ switch (source [pos++]) {
+ case '.':
+ TokenForItemPropertyValue (".", Token.DOT);
+ break;
+ case ',':
+ TokenForItemPropertyValue (",", Token.COMMA);
+ break;
+ case '[':
+ TokenForItemPropertyValue ("[", Token.BRACE_OPEN);
+ break;
+ case ']':
+ TokenForItemPropertyValue ("]", Token.BRACE_CLOSE);
+ break;
+ case '(':
+ modes.Push (TokenizerMode.Default);
+ TokenForItemPropertyValue ("(", Token.PAREN_OPEN);
+ break;
+ case ')':
+ if (modes.Count > 0) {
+ modes.Pop ();
+ current_token = Token.PAREN_CLOSE;
+ } else {
+ token_value = ")";
+ current_token = Token.NAME;
+ }
+ break;
+ case '-':
+ if (pos < source.Length && source [pos] == '>') {
+ current_token = Token.ARROW;
+ pos++;
+ } else
+ ErrorOnStrictBoolean ("-", "'-' is not followed by '>'.");
+ break;
+ case '=':
+ if (pos < source.Length && source [pos] == '=') {
+ current_token = Token.EQ;
+ pos++;
+ } else
+ ErrorOnStrictBoolean ("=", "'=' is not followed by '='.");
+ break;
+ case ':':
+ if (pos < source.Length && source [pos] == ':') {
+ current_token = Token.COLON2;
+ } else
+ ErrorOnStrictBoolean (":", "':' is not followed by ':'.");
+ pos++;
+ break;
+ case '!':
+ if (pos < source.Length && source [pos] == '=') {
+ pos++;
+ current_token = Token.NE;
+ } else
+ TokenForItemPropertyValue ("!", Token.NOT);
+ break;
+ case '>':
+ if (pos < source.Length && source [pos] == '=') {
+ pos++;
+ current_token = Token.GE;
+ } else
+ current_token = Token.GT;
+ break;
+ case '<':
+ if (pos < source.Length && source [pos] == '=') {
+ pos++;
+ current_token = Token.LE;
+ } else
+ current_token = Token.LT;
+ break;
+ case '$':
+ if (pos < source.Length && source [pos] == '(') {
+ modes.Push (TokenizerMode.InsideItemOrProperty);
+ current_token = Token.PROP_OPEN;
+ pos++;
+ } else
+ ErrorOnStrictBoolean ("$", "property reference '$' is not followed by '('.");
+ break;
+ case '@':
+ if (pos < source.Length && source [pos] == '(') {
+ modes.Push (TokenizerMode.InsideItemOrProperty);
+ current_token = Token.ITEM_OPEN;
+ pos++;
+ } else
+ ErrorOnStrictBoolean ("@", "item reference '@' is not followed by '('.");
+ break;
+ case '%':
+ if (pos < source.Length && source [pos] == '(') {
+ modes.Push (TokenizerMode.InsideItemOrProperty);
+ current_token = Token.METADATA_OPEN;
+ pos++;
+ } else
+ ErrorOnStrictBoolean ("%", "metadata reference '%' is not followed by '('.");
+ break;
+ case '"':
+ case '\'':
+ pos = source.IndexOf (source [pos - 1], pos);
+ if (pos < 0) {
+ ErrorOnStrictBoolean ("'", "unterminated string literal");
+ pos = source.Length;
+ }
+ token_value = source.Substring (current_token_position + 1, pos - current_token_position - 1);
+ current_token = Token.STRING_LITERAL;
+ pos++;
+ break;
+ default:
+ pos = source.IndexOfAny (token_starter_chars, pos);
+ if (pos < 0)
+ pos = source.Length;
+ var val = source.Substring (current_token_position, pos - current_token_position);
+ if (val.Equals ("AND", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.AND;
+ else if (val.Equals ("OR", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.OR;
+ else if (val.Equals ("TRUE", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.TRUE_LITERAL;
+ else if (val.Equals ("FALSE", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.FALSE_LITERAL;
+ else if (val.Equals ("YES", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.TRUE_LITERAL;
+ else if (val.Equals ("NO", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.FALSE_LITERAL;
+ else if (val.Equals ("ON", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.TRUE_LITERAL;
+ else if (val.Equals ("OFF", StringComparison.OrdinalIgnoreCase))
+ current_token = Token.FALSE_LITERAL;
+ else {
+ current_token = Token.NAME;
+ token_value = ProjectCollection.Unescape (val);
+ break;
+ }
+ break;
+ }
+ return true;
+ }
+ string spaces = " \t\r\n";
+
+ static readonly char [] token_starter_chars = ".,[]()-=:!><$@%\"' ".ToCharArray ();
+
+ void ReadStringLiteral (string source, char c)
+ {
+ while (pos < source.Length && source [pos] != c)
+ pos++;
+ if (source [pos - 1] != c)
+ ErrorOnStrictBoolean (c.ToString (), string.Format ("missing string literal terminator [{0}]", c));
+ else {
+ current_token = Token.NAME;
+ token_value = source.Substring (current_token_position + 1, pos - current_token_position - 2);
+ token_value = ProjectCollection.Unescape ((string) token_value);
+ }
+ }
+
+ void TokenForItemPropertyValue (string value, int token)
+ {
+ if (true)//CurrentTokenizerMode == TokenizerMode.InsideItemOrProperty)
+ current_token = token;
+ else {
+ current_token = Token.NAME;
+ token_value = value;
+ }
+ }
+
+ void ErrorOnStrictBoolean (string value, string message)
+ {
+ if (validation_type == ExpressionValidationType.StrictBoolean) {
+ current_token = Token.ERROR;
+ error = message;
+ } else {
+ current_token = Token.NAME;
+ token_value = value;
+ }
+ }
+
+ public int token ()
+ {
+ return current_token;
+ }
+
+ public object value ()
+ {
+ if (current_token == Token.NAME || current_token == Token.STRING_LITERAL)
+ return new NameToken () { Name = (string) token_value, Column = current_token_position };
+ else if (error != null)
+ return new ErrorToken () { Message = error, Column = current_token_position };
+ else
+ return new Location () { Column = current_token_position };
+ }
+ }
+
+ class NameToken : Location
+ {
+ public string Name { get; set; }
+
+ public override string ToString ()
+ {
+ return string.Format ("[NameToken: Value={0}]", Name);
+ }
+ }
+
+ class ErrorToken : Location
+ {
+ public string Message { get; set; }
+ }
+
+ interface ILocation
+ {
+ //int Line { get; }
+ int Column { get; }
+ string File { get; }
+
+ string ToLocationString ();
+ }
+
+ class Location : ILocation
+ {
+ //public int Line { get; set; }
+ public int Column { get; set; }
+ public string File { get; set; }
+
+ public string ToLocationString ()
+ {
+ return "at " + Column;
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ProjectTaskItem.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ProjectTaskItem.cs
new file mode 100644
index 0000000000..98d4bb2a58
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/ProjectTaskItem.cs
@@ -0,0 +1,92 @@
+//
+// ProjectTaskItem.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using System.IO;
+
+namespace Microsoft.Build.Internal
+{
+ class ProjectTaskItem : ITaskItem
+ {
+ ProjectItemElement item;
+ string evaluated_include_part;
+
+ public ProjectTaskItem (ProjectItemElement item, string evaluatedIncludePart)
+ {
+ this.item = item;
+ this.evaluated_include_part = WindowsCompatibilityExtensions.NormalizeFilePath (evaluatedIncludePart);
+ }
+ #region ITaskItem implementation
+ System.Collections.IDictionary ITaskItem.CloneCustomMetadata ()
+ {
+ var ret = new System.Collections.Hashtable ();
+ foreach (var p in item.Metadata)
+ ret [p.Name] = p;
+ return ret;
+ }
+ void ITaskItem.CopyMetadataTo (ITaskItem destinationItem)
+ {
+ throw new NotImplementedException ();
+ }
+ string ITaskItem.GetMetadata (string metadataName)
+ {
+ var wk = ProjectCollection.GetWellKnownMetadata (metadataName, evaluated_include_part, Path.GetFullPath, null);
+ if (wk != null)
+ return wk;
+ var mde = item.Metadata.FirstOrDefault (m => m.Name == metadataName);
+ return mde != null ? mde.Value : null;
+ }
+ void ITaskItem.RemoveMetadata (string metadataName)
+ {
+ throw new NotImplementedException ();
+ }
+ void ITaskItem.SetMetadata (string metadataName, string metadataValue)
+ {
+ throw new NotImplementedException ();
+ }
+ string ITaskItem.ItemSpec {
+ get { return evaluated_include_part; }
+ set { throw new NotImplementedException (); }
+ }
+ int ITaskItem.MetadataCount {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+ System.Collections.ICollection ITaskItem.MetadataNames {
+ get {
+ throw new NotImplementedException ();
+ }
+ }
+ #endregion
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/FileLogger.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/WindowsCompatibilityExtensions.cs
index 331d29ab9b..38f9d6b29e 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/FileLogger.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Internal/WindowsCompatibilityExtensions.cs
@@ -1,9 +1,10 @@
-// FileLogger.cs
+//
+// WindowsCompatibilityExtensions.cs
//
// Author:
-// Rolf Bjarne Kvinge (rolf@xamarin.com)
+// Atsushi Enomoto (atsushi@xamarin.com)
//
-// Copyright (C) 2011 Xamarin Inc.
+// (C) 2013 Xamarin Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -24,13 +25,17 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-
using System;
+using System.IO;
-namespace Microsoft.Build.Logging
+namespace Microsoft.Build.Internal
{
- public class FileLogger
- {
- }
+ static class WindowsCompatibilityExtensions
+ {
+ public static string NormalizeFilePath (string path)
+ {
+ return path.Replace ('\\', Path.DirectorySeparatorChar);
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConfigurableForwardingLogger.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConfigurableForwardingLogger.cs
new file mode 100644
index 0000000000..077a1bb4ef
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConfigurableForwardingLogger.cs
@@ -0,0 +1,43 @@
+using System;
+using Microsoft.Build.Framework;
+
+namespace Microsoft.Build.Logging
+{
+ public class ConfigurableForwardingLogger : IForwardingLogger
+ {
+ #region INodeLogger implementation
+
+ public void Initialize (IEventSource eventSource, int nodeCount)
+ {
+ Initialize (eventSource);
+ }
+
+ #endregion
+
+ #region ILogger implementation
+
+ public void Initialize (IEventSource eventSource)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public void Shutdown ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public string Parameters { get; set; }
+
+ public LoggerVerbosity Verbosity { get; set; }
+
+ #endregion
+
+ #region IForwardingLogger implementation
+
+ public IEventRedirector BuildEventRedirector { get; set; }
+
+ public int NodeId { get; set; }
+
+ #endregion
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConsoleLogger.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConsoleLogger.cs
deleted file mode 100644
index 161476c2c0..0000000000
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/ConsoleLogger.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-// ConsoleLogger.cs
-//
-// Author:
-// Rolf Bjarne Kvinge (rolf@xamarin.com)
-//
-// Copyright (C) 2011 Xamarin Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using Microsoft.Build.Framework;
-
-using System;
-
-namespace Microsoft.Build.Logging
-{
- public class ConsoleLogger : INodeLogger
- {
- public ConsoleLogger ()
- : this (LoggerVerbosity.Normal)
- {
- }
-
- public ConsoleLogger (LoggerVerbosity verbosity)
- {
- throw new NotImplementedException ();
- }
-
- public ConsoleLogger (LoggerVerbosity verbosity, WriteHandler write, ColorSetter colorSet, ColorResetter colorReset)
- {
- throw new NotImplementedException ();
- }
-
- #region INodeLogger implementation
- public void Initialize (IEventSource eventSource, int nodeCount)
- {
- throw new NotImplementedException ();
- }
- #endregion
-
- #region ILogger implementation
- public void Initialize (IEventSource eventSource)
- {
- throw new NotImplementedException ();
- }
-
- public void Shutdown ()
- {
- throw new NotImplementedException ();
- }
-
- public string Parameters {
- get {
- throw new NotImplementedException ();
- }
- set {
- throw new NotImplementedException ();
- }
- }
-
- public LoggerVerbosity Verbosity {
- get {
- throw new NotImplementedException ();
- }
- set {
- throw new NotImplementedException ();
- }
- }
- #endregion
- }
-}
-
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/LoggerDescription.cs b/mcs/class/Microsoft.Build/Microsoft.Build.Logging/LoggerDescription.cs
index 5115402c67..4e7b72f1a7 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.Logging/LoggerDescription.cs
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.Logging/LoggerDescription.cs
@@ -26,18 +26,39 @@
//
using System;
+using System.Reflection;
using Microsoft.Build.Framework;
namespace Microsoft.Build.Logging
{
- public class LoggerDescription
- {
- public LoggerDescription (string loggerClassName, string loggerAssemblyName,
- string loggerAssemblyFile, string loggerSwitchParameters,
- LoggerVerbosity verbosity)
- {
- throw new NotImplementedException ();
- }
- }
+ public class LoggerDescription
+ {
+ public LoggerDescription (string loggerClassName, string loggerAssemblyName,
+ string loggerAssemblyFile, string loggerSwitchParameters,
+ LoggerVerbosity verbosity)
+ {
+ if (loggerAssemblyName != null && loggerAssemblyFile != null)
+ throw new InvalidOperationException ("Cannot specify both loggerAssemblyName and loggerAssemblyFile at the same time.");
+ if (loggerAssemblyName == null && loggerAssemblyFile == null)
+ throw new InvalidOperationException ("Either loggerAssemblyName or loggerAssemblyFile must be specified");
+ class_name = loggerClassName;
+ assembly_name = loggerAssemblyName;
+ assembly_file = loggerAssemblyFile;
+ LoggerSwitchParameters = loggerSwitchParameters;
+ Verbosity = verbosity;
+ }
+
+ string class_name, assembly_name, assembly_file;
+
+ public string LoggerSwitchParameters { get; private set; }
+ public LoggerVerbosity Verbosity { get; private set; }
+
+ public ILogger CreateLogger ()
+ {
+ var assembly = assembly_name != null ? AppDomain.CurrentDomain.Load (assembly_name) : Assembly.LoadFile (assembly_file);
+ var type = assembly.GetType (class_name);
+ return (ILogger) Activator.CreateInstance (type, Verbosity);
+ }
+ }
}
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build.dll.sources b/mcs/class/Microsoft.Build/Microsoft.Build.dll.sources
index 37c1f480c1..6cbed8f392 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build.dll.sources
+++ b/mcs/class/Microsoft.Build/Microsoft.Build.dll.sources
@@ -1,6 +1,19 @@
Assembly/AssemblyInfo.cs
../../build/common/Consts.cs
../../build/common/MonoTODOAttribute.cs
+../../tools/xbuild/XBuildConsts.cs
+../Microsoft.Build.Engine/Microsoft.Build.BuildEngine/DirectoryScanner.cs
+../Microsoft.Build.Engine/Microsoft.Build.BuildEngine/EventSource.cs
+../Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ColorSetter.cs
+../Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ColorResetter.cs
+../Microsoft.Build.Engine/Microsoft.Build.BuildEngine/ConsoleLogger.cs
+../Microsoft.Build.Engine/Microsoft.Build.BuildEngine/FileLogger.cs
+../Microsoft.Build.Engine/Microsoft.Build.BuildEngine/WriteHandler.cs
+../Microsoft.Build.Utilities/Mono.XBuild.Utilities/MSBuildUtils.cs
+../Microsoft.Build.Utilities/Mono.XBuild.Utilities/ReservedNameUtils.cs
+../Microsoft.Build.Utilities/Microsoft.Build.Utilities/TaskItem.cs
+../Microsoft.Build.Utilities/Microsoft.Build.Utilities/ToolLocationHelper.cs
+../Microsoft.Build.Utilities/Microsoft.Build.Utilities/TargetDotNetFrameworkVersion.cs
Microsoft.Build.Construction/ElementLocation.cs
Microsoft.Build.Construction/ProjectChooseElement.cs
Microsoft.Build.Construction/ProjectCommentElement.cs
@@ -28,16 +41,24 @@ Microsoft.Build.Construction/ProjectUsingTaskParameterElement.cs
Microsoft.Build.Construction/ProjectWhenElement.cs
Microsoft.Build.Construction/UsingTaskParameterGroupElement.cs
Microsoft.Build.Evaluation/Project.cs
+Microsoft.Build.Evaluation/ProjectChangedEventArgs.cs
Microsoft.Build.Evaluation/ProjectCollection.cs
+Microsoft.Build.Evaluation/ProjectCollectionChangedEventArgs.cs
+Microsoft.Build.Evaluation/ProjectCollectionChangedState.cs
Microsoft.Build.Evaluation/ProjectItem.cs
Microsoft.Build.Evaluation/ProjectItemDefinition.cs
Microsoft.Build.Evaluation/ProjectLoadSettings.cs
Microsoft.Build.Evaluation/ProjectMetadata.cs
Microsoft.Build.Evaluation/ProjectProperty.cs
+Microsoft.Build.Evaluation/ProjectXmlChangedEventArgs.cs
Microsoft.Build.Evaluation/ResolvedImport.cs
+Microsoft.Build.Evaluation/SubToolset.cs
Microsoft.Build.Evaluation/Toolset.cs
Microsoft.Build.Evaluation/ToolsetDefinitionLocations.cs
+Microsoft.Build.Exceptions/BuildAbortedException.cs
+Microsoft.Build.Exceptions/InternalLoggerException.cs
Microsoft.Build.Exceptions/InvalidProjectFileException.cs
+Microsoft.Build.Exceptions/InvalidToolsetDefinitionException.cs
Microsoft.Build.Execution/BuildManager.cs
Microsoft.Build.Execution/BuildParameters.cs
Microsoft.Build.Execution/BuildRequestData.cs
@@ -45,24 +66,44 @@ Microsoft.Build.Execution/BuildRequestDataFlags.cs
Microsoft.Build.Execution/BuildResult.cs
Microsoft.Build.Execution/BuildResultCode.cs
Microsoft.Build.Execution/BuildSubmission.cs
+Microsoft.Build.Execution/BuildSubmissionCompleteCallback.cs
Microsoft.Build.Execution/HostServices.cs
Microsoft.Build.Execution/ITargetResult.cs
Microsoft.Build.Execution/NodeAffinity.cs
+Microsoft.Build.Execution/NodeEngineShutdownReason.cs
+Microsoft.Build.Execution/OutOfProcNode.cs
Microsoft.Build.Execution/ProjectInstance.cs
Microsoft.Build.Execution/ProjectItemDefinitionInstance.cs
+Microsoft.Build.Execution/ProjectItemGroupTaskInstance.cs
+Microsoft.Build.Execution/ProjectItemGroupTaskItemInstance.cs
+Microsoft.Build.Execution/ProjectItemGroupTaskMetadataInstance.cs
Microsoft.Build.Execution/ProjectItemInstance.cs
Microsoft.Build.Execution/ProjectMetadataInstance.cs
+Microsoft.Build.Execution/ProjectOnErrorInstance.cs
+Microsoft.Build.Execution/ProjectPropertyGroupTaskInstance.cs
+Microsoft.Build.Execution/ProjectPropertyGroupTaskPropertyInstance.cs
Microsoft.Build.Execution/ProjectPropertyInstance.cs
Microsoft.Build.Execution/ProjectTargetInstance.cs
+Microsoft.Build.Execution/ProjectTaskInstance.cs
+Microsoft.Build.Execution/ProjectTaskInstanceChild.cs
+Microsoft.Build.Execution/ProjectTaskOutputItemInstance.cs
+Microsoft.Build.Execution/ProjectTaskOutputPropertyInstance.cs
+Microsoft.Build.Execution/ProjectTargetInstanceChild.cs
Microsoft.Build.Execution/TargetResult.cs
Microsoft.Build.Execution/TargetResultCode.cs
+Microsoft.Build.Internal/BuildEngine4.cs
+Microsoft.Build.Internal/BuildNodeManager.cs
+Microsoft.Build.Internal/BuildTaskDatabase.cs
+Microsoft.Build.Internal/BuildTaskFactory.cs
Microsoft.Build.Internal/CollectionFromEnumerable.cs
+Microsoft.Build.Internal/ExpressionConstructs.cs
+Microsoft.Build.Internal/ExpressionEvaluator.cs
+Microsoft.Build.Internal/ExpressionParserManual.cs
+Microsoft.Build.Internal/ExpressionTokenizer.cs
Microsoft.Build.Internal/FilteredEnumerable.cs
+Microsoft.Build.Internal/ProjectTaskItem.cs
Microsoft.Build.Internal/ReverseEnumerable.cs
-Microsoft.Build.Logging/ColorResetter.cs
-Microsoft.Build.Logging/ColorSetter.cs
-Microsoft.Build.Logging/ConsoleLogger.cs
-Microsoft.Build.Logging/FileLogger.cs
+Microsoft.Build.Internal/WindowsCompatibilityExtensions.cs
+Microsoft.Build.Logging/ConfigurableForwardingLogger.cs
Microsoft.Build.Logging/ForwardingLoggerRecord.cs
Microsoft.Build.Logging/LoggerDescription.cs
-Microsoft.Build.Logging/WriteHandler.cs
diff --git a/mcs/class/Microsoft.Build/Microsoft.Build_test.dll.sources b/mcs/class/Microsoft.Build/Microsoft.Build_test.dll.sources
index 0258038dcc..86971873d0 100644
--- a/mcs/class/Microsoft.Build/Microsoft.Build_test.dll.sources
+++ b/mcs/class/Microsoft.Build/Microsoft.Build_test.dll.sources
@@ -1,2 +1,21 @@
FunctionalTest.cs
+Microsoft.Build.Construction/ProjectItemElementTest.cs
+Microsoft.Build.Construction/ProjectRootElementTest.cs
+Microsoft.Build.Evaluation/ProjectCollectionTest.cs
+Microsoft.Build.Evaluation/ProjectItemDefinitionTest.cs
+Microsoft.Build.Evaluation/ProjectItemTest.cs
+Microsoft.Build.Evaluation/ProjectTest.cs
+Microsoft.Build.Evaluation/ProjectPropertyTest.cs
+Microsoft.Build.Evaluation/ResolvedImportTest.cs
+Microsoft.Build.Evaluation/ToolsetTest.cs
+Microsoft.Build.Execution/BuildParametersTest.cs
+Microsoft.Build.Execution/BuildManagerTest.cs
+Microsoft.Build.Execution/BuildSubmissionTest.cs
+Microsoft.Build.Execution/ProjectInstanceTest.cs
+Microsoft.Build.Execution/ProjectMetadataInstanceTest.cs
+Microsoft.Build.Execution/ProjectTargetInstanceTest.cs
Microsoft.Build.Internal/CollectionFromEnumerableTest.cs
+Microsoft.Build.Internal/ExpressionParserTest.cs
+Microsoft.Build.Logging/ConsoleLoggerTest.cs
+Microsoft.Build.Logging/LoggerDescriptionTest.cs
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectItemElementTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectItemElementTest.cs
new file mode 100644
index 0000000000..caa93f1b76
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectItemElementTest.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using NUnit.Framework;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Exceptions;
+
+namespace MonoTests.Microsoft.Build.Construction
+{
+ [TestFixture]
+ public class ProjectItemElementTest
+ {
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void EmptyInclude ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include='' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ ProjectRootElement.Create (xml);
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void MissingInclude ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ ProjectRootElement.Create (xml);
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectRootElementTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectRootElementTest.cs
new file mode 100644
index 0000000000..e864f4dfb3
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Construction/ProjectRootElementTest.cs
@@ -0,0 +1,222 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using NUnit.Framework;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Exceptions;
+
+namespace MonoTests.Microsoft.Build.Construction
+{
+ [TestFixture]
+ public class ProjectRootElementTest
+ {
+ const string empty_project_xml = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+
+ [Test]
+ [ExpectedException (typeof (UriFormatException))]
+ [Category ("NotWorking")] // URL is constructed for ElementLocation, which we don't support yet.
+ public void CreateExpectsAbsoluteUri ()
+ {
+ var xml = XmlReader.Create (new StringReader (empty_project_xml), null, "foo.xml");
+ ProjectRootElement.Create (xml);
+ }
+
+ [Test]
+ public void CreateAndPaths ()
+ {
+ Assert.IsNull (ProjectRootElement.Create ().FullPath, "#1");
+ var xml = XmlReader.Create (new StringReader (empty_project_xml), null, "file:///foo.xml");
+ // This creator does not fill FullPath...
+ var root = ProjectRootElement.Create (xml);
+ Assert.IsNull (root.FullPath, "#2");
+ Assert.AreEqual (Path.GetDirectoryName (new Uri (GetType ().Assembly.CodeBase).LocalPath), root.DirectoryPath, "#3");
+ }
+
+ [Test]
+ public void FullPathSetter ()
+ {
+ var root = ProjectRootElement.Create ();
+ root.FullPath = "test" + Path.DirectorySeparatorChar + "foo.xml";
+ var full = Path.Combine (Path.GetDirectoryName (new Uri (GetType ().Assembly.CodeBase).LocalPath), "test", "foo.xml");
+ Assert.AreEqual (full, root.FullPath, "#1");
+ Assert.AreEqual (Path.GetDirectoryName (full), root.DirectoryPath, "#1");
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void FullPathSetNull ()
+ {
+ ProjectRootElement.Create ().FullPath = null;
+ }
+
+ [Test]
+ public void InvalidProject ()
+ {
+ try {
+ ProjectRootElement.Create (XmlReader.Create (new StringReader (" <root/>")));
+ Assert.Fail ("should throw InvalidProjectFileException");
+ } catch (InvalidProjectFileException ex) {
+ #if NET_4_5
+ Assert.AreEqual (1, ex.LineNumber, "#1");
+ // it is very interesting, but unlike XmlReader.LinePosition it returns the position for '<'.
+ Assert.AreEqual (2, ex.ColumnNumber, "#2");
+ #endif
+ }
+ }
+
+ [Test]
+ public void CreateWithXmlLoads ()
+ {
+ string project_xml_1 = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'><ItemGroup><None Include='bar.txt' /></ItemGroup></Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml_1), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ Assert.AreEqual (1, root.Items.Count, "#1");
+ }
+
+ [Test]
+ public void ToolsVersionDefault ()
+ {
+ var g = ProjectCollection.GlobalProjectCollection;
+ var root = ProjectRootElement.Create ();
+ // this will be wrong in the future version, but since .NET 4.5 still expects "4.0" we can't say for sure.
+ Assert.AreEqual ("4.0", root.ToolsVersion, "#1");
+ }
+
+ [Test]
+ public void ToolsVersionIsEmptyWithXml ()
+ {
+ string project_xml_1 = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'><ItemGroup><None Include='bar.txt' /></ItemGroup></Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml_1), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ Assert.AreEqual (string.Empty, root.ToolsVersion, "#1");
+ }
+
+ [Test]
+ public void LoadUnknownChild ()
+ {
+ string project_xml_1 = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'><Unknown /></Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml_1), null, "file://localhost/foo.xml");
+ try {
+ ProjectRootElement.Create (xml);
+ Assert.Fail ("should throw InvalidProjectFileException");
+ } catch (InvalidProjectFileException ex) {
+ #if NET_4_5
+ Assert.AreEqual (1, ex.LineNumber, "#1");
+ // unlike unexpected element case which returned the position for '<', it does return the name start char...
+ Assert.AreEqual (70, ex.ColumnNumber, "#2");
+ #endif
+ }
+ }
+
+ [Test]
+ public void LoadUnregisteredItem ()
+ {
+ string project_xml_1 = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'><ItemGroup><UnregisteredItem Include='bar.txt' /></ItemGroup></Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml_1), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ Assert.AreEqual (1, root.Items.Count, "#1");
+ }
+
+ [Test]
+ public void LoadInvalidProjectForBadCondition ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Foo>What are 'ESCAPE' &amp; ""EVALUATE"" ? $ # % ^</Foo>
+ <!-- Note that this contains invalid Condition expression. Project.ctor() fails to load. -->
+ <Baz Condition=""$(Void)=="">$(FOO)</Baz>
+ </PropertyGroup>
+</Project>";
+ var path = "file://localhost/foo.xml";
+ var reader = XmlReader.Create (new StringReader (xml), null, path);
+ var root = ProjectRootElement.Create (reader);
+ Assert.AreEqual (2, root.Properties.Count, "#1");
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void LoadInvalidProjectGroupInProjectGroup ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.CSharp.targets' />
+ <PropertyGroup>
+ <Foo>Bar</Foo>
+ <PropertyGroup>
+ <X>x</X>
+ <Y>y</Y>
+ <Z>z</Z>
+ </PropertyGroup>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ ProjectRootElement.Create (xml);
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void LoadInvalidItemGroupInProjectGroup ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.CSharp.targets' />
+ <PropertyGroup>
+ <Foo>Bar</Foo>
+ <ItemGroup/>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ ProjectRootElement.Create (xml);
+ }
+
+ [Test]
+ public void ChildAndAllChildren ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.CSharp.targets' />
+ <PropertyGroup>
+ <Foo>Bar</Foo>
+ <Item/>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ Assert.AreEqual (2, root.Children.Count, "#1");
+ // AllChildren expands descendants
+ Assert.AreEqual (4, root.AllChildren.Count (), "#2");
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void SaveWithoutFullPath ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ root.Save ();
+ }
+
+ [Test]
+ public void SaveToWriter ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ var sw = new StringWriter ();
+ root.Save (sw);
+ // CRLF? mmm, k...
+ Assert.AreEqual ("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n" + project_xml.Replace ('\'', '"'), sw.ToString (), "#1");
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void ImportsMissingProject ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='' />
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ ProjectRootElement.Create (xml);
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectCollectionTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectCollectionTest.cs
new file mode 100644
index 0000000000..700a41663d
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectCollectionTest.cs
@@ -0,0 +1,150 @@
+//
+// ProjectCollectionTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using NUnit.Framework;
+using Microsoft.Build.Execution;
+
+namespace MonoTests.Microsoft.Build.Evaluation
+{
+ [TestFixture]
+ public class ProjectCollectionTest
+ {
+ [Test]
+ public void GlobalProperties ()
+ {
+ var g = ProjectCollection.GlobalProjectCollection;
+ Assert.AreEqual (0, g.GlobalProperties.Count, "#1");
+ Assert.IsTrue (g.GlobalProperties.IsReadOnly, "#2");
+ }
+
+ [Test]
+ public void DefaultToolsVersion ()
+ {
+ var pc = ProjectCollection.GlobalProjectCollection;
+ Assert.AreEqual (pc.Toolsets.First ().ToolsVersion, pc.DefaultToolsVersion, "#1");
+ }
+
+ [Test]
+ public void Toolsets ()
+ {
+ var pc = ProjectCollection.GlobalProjectCollection;
+ Assert.IsNotNull (pc.Toolsets, "#1-1");
+ Assert.IsTrue (pc.Toolsets.Any (), "#1-2");
+ pc = new ProjectCollection ();
+ Assert.IsNotNull (pc.Toolsets, "#2-1");
+ Assert.IsTrue (pc.Toolsets.Any (), "#2-2");
+ }
+
+ [Test]
+ public void BuildDoesNotIncreaseCollectionContent ()
+ {
+ string empty_project_xml = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (empty_project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var coll = new ProjectCollection ();
+ var inst = new ProjectInstance (root, null, null, coll);
+ root.FullPath = "ProjectCollectionTest.BuildDoesNotIncreaseCollectionContent.proj";
+ Assert.AreEqual (0, coll.Count, "#1");
+ inst.Build ();
+ Assert.AreEqual (0, coll.Count, "#2");
+ }
+
+ [Test]
+ public void GetLoadedProjectsWithoutFullPath ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ string path = Path.GetFullPath ("foo.xml");
+ var pc = new ProjectCollection ();
+
+ pc.LoadProject (XmlReader.Create (new StringReader (project_xml), null, path));
+ Assert.AreEqual (0, pc.GetLoadedProjects (path).Count, "#1"); // huh?
+ Assert.AreEqual (0, pc.LoadedProjects.Count, "#1.1");
+
+ new Project (root, null, null, pc);
+ Assert.AreEqual (0, pc.GetLoadedProjects (path).Count, "#2"); // huh?
+ }
+
+ [Test]
+ public void GetLoadedProjectsSuccess ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ string path = Path.GetFullPath ("foo.xml");
+ var pc = new ProjectCollection ();
+
+ var proj = new Project (root, null, null, pc);
+ // this order also matters for test; It sets FullPath after Project.ctor(), and should still work.
+ root.FullPath = "foo.xml";
+
+ Assert.AreEqual (1, pc.GetLoadedProjects (path).Count, "#1"); // wow ok...
+ Assert.AreEqual (proj, pc.GetLoadedProjects (path).First (), "#2");
+ }
+
+ [Test]
+ public void GetLoadedProjectsSuccess2 ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ string path = Path.GetFullPath ("GetLoadedProjectsSuccess2.xml");
+ var pc = new ProjectCollection ();
+
+ using (var fs = File.CreateText (path))
+ fs.Write (project_xml);
+ try {
+ var proj = pc.LoadProject (path);
+
+ Assert.AreEqual (1, pc.GetLoadedProjects (path).Count, "#1"); // ok... LoadProject (with filename) adds it to the collection.
+ Assert.AreEqual (proj, pc.GetLoadedProjects (path).First (), "#2");
+ } finally {
+ File.Delete (path);
+ }
+ }
+
+ [Test]
+ public void GetLoadedProjectsForProjectInstance ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ string path = Path.GetFullPath ("foo.xml");
+ var pc = new ProjectCollection ();
+ root.FullPath = "foo.xml";
+
+ new ProjectInstance (root, null, null, pc);
+ Assert.AreEqual (0, pc.GetLoadedProjects (path).Count, "#1"); // so, ProjectInstance does not actually load Project...
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemDefinitionTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemDefinitionTest.cs
new file mode 100644
index 0000000000..cd86873c84
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemDefinitionTest.cs
@@ -0,0 +1,104 @@
+//
+// ProjectItemDefinitionTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+using System.Collections.Generic;
+
+namespace MonoTests.Microsoft.Build.Evaluation
+{
+ [TestFixture]
+ public class ProjectItemDefinitionTest
+ {
+ [Test]
+ public void Properties ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemDefinitionGroup>
+ <Foo>
+ <prop1>value1</prop1>
+ <prop2>value1</prop2>
+ </Foo>
+ <!-- This one is merged into existing Foo definition above. -->
+ <Foo>
+ <prop1>valueX1</prop1><!-- this one overrides value1. -->
+ <prop3>value3</prop3>
+ </Foo>
+ </ItemDefinitionGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ Assert.AreEqual (1, proj.ItemDefinitions.Count, "#1"); // Foo
+ var def = proj.ItemDefinitions ["Foo"];
+ Assert.AreEqual ("Foo", def.ItemType, "#1x");
+ Assert.AreEqual (3, def.MetadataCount, "#2");
+ var md1 = def.Metadata.First (m => m.Name == "prop1");
+ Assert.AreEqual ("Foo", md1.ItemType, "#2x");
+ Assert.AreEqual ("valueX1", md1.UnevaluatedValue, "#3");
+ // FIXME: enable it once we implemented it.
+ //Assert.AreEqual ("valueX1", md1.EvaluatedValue, "#4");
+ Assert.IsNotNull (md1.Predecessor, "#5");
+ Assert.AreEqual ("value1", md1.Predecessor.UnevaluatedValue, "#6");
+ // FIXME: enable it once we implemented it.
+ //Assert.AreEqual ("value1", md1.Predecessor.EvaluatedValue, "#7");
+ }
+
+ [Test]
+ public void Condition ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemDefinitionGroup>
+ <I Condition='{0}'>
+ <DefinedMetadata>X</DefinedMetadata>
+ </I>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <I Include='foo' />
+ </ItemGroup>
+</Project>";
+ var reader = XmlReader.Create (new StringReader (string.Format (xml, "True")));
+ var root = ProjectRootElement.Create (reader);
+ var proj = new Project (root);
+ var i = proj.GetItems ("I").First ();
+ Assert.AreEqual ("X", i.GetMetadataValue ("DefinedMetadata"), "#1");
+
+ reader = XmlReader.Create (new StringReader (string.Format (xml, "False")));
+ root = ProjectRootElement.Create (reader);
+ proj = new Project (root);
+ i = proj.GetItems ("I").First ();
+ Assert.AreEqual (string.Empty, i.GetMetadataValue ("DefinedMetadata"), "#2");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemTest.cs
new file mode 100644
index 0000000000..61973b7ceb
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectItemTest.cs
@@ -0,0 +1,212 @@
+//
+// ProjectItemTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+using System.Collections.Generic;
+
+namespace MonoTests.Microsoft.Build.Evaluation
+{
+ [TestFixture]
+ public class ProjectItemTest
+ {
+ [Test]
+ public void SetUnevaluatedInclude ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include='foo/bar.txt' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ Assert.AreEqual (1, root.ItemGroups.Count, "#1");
+ var g = root.ItemGroups.First ();
+ Assert.AreEqual (1, g.Items.Count, "#2");
+ var xitem = g.Items.First ();
+ var proj = new Project (root);
+ var item = proj.ItemsIgnoringCondition.First ();
+ string inc = "foo/bar.txt";
+ Assert.AreEqual (inc, xitem.Include, "#3");
+ Assert.AreEqual (inc, item.UnevaluatedInclude, "#4");
+ string inc2 = "foo/bar.ext.txt";
+ item.UnevaluatedInclude = inc2;
+ Assert.AreEqual (inc2, xitem.Include, "#5");
+ Assert.AreEqual (inc2, item.UnevaluatedInclude, "#6");
+ }
+
+ void SetupTemporaryDirectoriesAndFiles ()
+ {
+ Directory.CreateDirectory ("Test/ProjectItemTestTemporary");
+ Directory.CreateDirectory ("Test/ProjectItemTestTemporary/parent");
+ Directory.CreateDirectory ("Test/ProjectItemTestTemporary/parent/dir1");
+ Directory.CreateDirectory ("Test/ProjectItemTestTemporary/parent/dir2");
+ File.CreateText ("Test/ProjectItemTestTemporary/x.cs").Close ();
+ File.CreateText ("Test/ProjectItemTestTemporary/parent/dir1/a.cs").Close ();
+ File.CreateText ("Test/ProjectItemTestTemporary/parent/dir1/a1.cs").Close ();
+ File.CreateText ("Test/ProjectItemTestTemporary/parent/dir1/b.cs").Close ();
+ File.CreateText ("Test/ProjectItemTestTemporary/parent/dir2/a2.cs").Close ();
+ File.CreateText ("Test/ProjectItemTestTemporary/parent/dir2/a.cs").Close ();
+ File.CreateText ("Test/ProjectItemTestTemporary/parent/dir2/b.cs").Close ();
+ }
+
+ void CleanupTemporaryDirectories ()
+ {
+ Directory.Delete ("Test/ProjectItemTestTemporary", true);
+ }
+
+ [Test]
+ public void WildcardExpansion ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include='Test/ProjectItemTestTemporary/parent/dir*/a*.cs;Test/ProjectItemTestTemporary/x.cs' />
+ </ItemGroup>
+</Project>";
+ try {
+ SetupTemporaryDirectoriesAndFiles ();
+ WildcardExpansionCommon (project_xml, false);
+ } finally {
+ CleanupTemporaryDirectories ();
+ }
+ }
+
+ [Test]
+ public void WildcardExpansionRecursive ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include='Test/ProjectItemTestTemporary/parent/**/a*.cs;Test/ProjectItemTestTemporary/x.cs' />
+ </ItemGroup>
+</Project>";
+ try {
+ SetupTemporaryDirectoriesAndFiles ();
+ WildcardExpansionCommon (project_xml, true);
+ } finally {
+ CleanupTemporaryDirectories ();
+ }
+ }
+
+ void WildcardExpansionCommon (string xmlString, bool hasRecursiveDir)
+ {
+ char sep = Path.DirectorySeparatorChar;
+ var xml = XmlReader.Create (new StringReader (xmlString));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ var xitem = proj.Xml.Items.First ();
+ // sort is needed because they are only sorted by ItemType.
+ var items = proj.Items.OrderBy (p => p.EvaluatedInclude).ToArray ();
+ Assert.AreEqual (5, items.Length, "#1");
+ Assert.AreEqual (string.Format ("Test/ProjectItemTestTemporary/parent/dir1{0}a.cs", Path.DirectorySeparatorChar), items [0].EvaluatedInclude, "#2");
+ Assert.AreEqual ("a", items [0].GetMetadataValue ("Filename"), "#3");
+ if (hasRecursiveDir)
+ Assert.AreEqual ("dir1" + sep, items [0].GetMetadataValue ("RecursiveDir"), "#3.2");
+ Assert.AreEqual (string.Format ("Test/ProjectItemTestTemporary/parent/dir1{0}a1.cs", Path.DirectorySeparatorChar), items [1].EvaluatedInclude, "#4");
+ Assert.AreEqual ("a1", items [1].GetMetadataValue ("Filename"), "#5");
+ if (hasRecursiveDir)
+ Assert.AreEqual ("dir1" + sep, items [1].GetMetadataValue ("RecursiveDir"), "#5.2");
+ Assert.AreEqual (string.Format ("Test/ProjectItemTestTemporary/parent/dir2{0}a.cs", Path.DirectorySeparatorChar), items [2].EvaluatedInclude, "#6");
+ Assert.AreEqual ("a", items [2].GetMetadataValue ("Filename"), "#7");
+ if (hasRecursiveDir)
+ Assert.AreEqual ("dir2" + sep, items [2].GetMetadataValue ("RecursiveDir"), "#7.2");
+ Assert.AreEqual (string.Format ("Test/ProjectItemTestTemporary/parent/dir2{0}a2.cs", Path.DirectorySeparatorChar), items [3].EvaluatedInclude, "#8");
+ Assert.AreEqual ("a2", items [3].GetMetadataValue ("Filename"), "#9");
+ if (hasRecursiveDir)
+ Assert.AreEqual ("dir2" + sep, items [3].GetMetadataValue ("RecursiveDir"), "#9.2");
+ Assert.AreEqual ("Test/ProjectItemTestTemporary/x.cs", items [4].EvaluatedInclude, "#10");
+ for (int i = 0; i < items.Length; i++)
+ Assert.AreEqual (xitem, items [i].Xml, "#11:" + i);
+ }
+
+ [Test]
+ public void Metadata ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemDefinitionGroup>
+ <Foo>
+ <prop1>value1</prop1>
+ </Foo>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Foo Include='foo/bar.txt'>
+ <prop1>valueX1</prop1>
+ </Foo>
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ Assert.AreEqual (1, root.ItemGroups.Count, "#1");
+ var g = root.ItemGroups.First ();
+ Assert.AreEqual (1, g.Items.Count, "#2");
+ var proj = new Project (root);
+ var item = proj.ItemsIgnoringCondition.First ();
+ var meta = item.GetMetadata ("prop1");
+ Assert.IsNotNull (meta, "#3");
+ Assert.AreEqual ("valueX1", meta.UnevaluatedValue, "#4");
+ Assert.IsNotNull (meta.Predecessor, "#5");
+ Assert.AreEqual ("value1", meta.Predecessor.UnevaluatedValue, "#6");
+
+ // Well-known metadata don't show up via GetMetadata(), but does show up via GetMetadataValue().
+ Assert.AreEqual (null, item.GetMetadata ("Filename"), "#7");
+ Assert.AreEqual ("bar", item.GetMetadataValue ("Filename"), "#8");
+ }
+
+ [Test]
+ public void ExpandPropertyThenTrim ()
+ {
+ string test = @"A
+B
+C
+ ";
+ string project_xml = string.Format (@"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Test>{0}</Test>
+ <Test2>$(TEST)</Test2>
+ </PropertyGroup>
+ <ItemGroup>
+ <X Include='$(TEST)' />
+ <X2 Include='$(TEST)z' />
+ </ItemGroup>
+</Project>", test);
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "ProjectItemTest.ExpandPropertyThenTrim.proj";
+ var proj = new ProjectInstance (root);
+ Assert.AreEqual (test, proj.GetPropertyValue ("TEST"), "#1");
+ Assert.AreEqual (test, proj.GetPropertyValue ("TEST2"), "#2");
+ Assert.AreEqual (test.Trim (), proj.GetItems ("X").First ().EvaluatedInclude, "#3");
+ Assert.AreEqual (test + "z", proj.GetItems ("X2").First ().EvaluatedInclude, "#4");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectPropertyTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectPropertyTest.cs
new file mode 100644
index 0000000000..9c89ef09f6
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectPropertyTest.cs
@@ -0,0 +1,130 @@
+//
+// ProjectPropertyTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+using System.Collections.Generic;
+
+namespace MonoTests.Microsoft.Build.Evaluation
+{
+ [TestFixture]
+ public class ProjectPropertyTest
+ {
+ [Test]
+ public void SetUnevaluatedValueOverwritesElementValue ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Foo>Bar</Foo>
+ <Item/>
+ <X>1</X>
+ <X>2</X>
+ <PATH>overriden</PATH>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var pe = root.Properties.First ();
+ Assert.AreEqual ("Bar", pe.Value, "#1");
+ var proj = new Project (root);
+ var prop = proj.Properties.First (p => p.Name == "Foo");
+ Assert.AreEqual ("Bar", prop.UnevaluatedValue, "#2");
+ prop.UnevaluatedValue = "x";
+ Assert.AreEqual ("x", pe.Value, "#3");
+
+ prop = proj.Properties.First (p => p.Name == "X");
+ Assert.AreEqual ("2", prop.UnevaluatedValue, "#4");
+ Assert.IsNotNull (prop.Predecessor, "#5");
+ Assert.AreEqual ("1", prop.Predecessor.UnevaluatedValue, "#6");
+
+ // environment property could also be Predecessor (and removed...maybe.
+ // I could reproduce only NRE = .NET bug with environment property so far.)
+ prop = proj.Properties.First (p => p.Name == "PATH");
+ Assert.AreEqual ("overriden", prop.UnevaluatedValue, "#7");
+ Assert.IsNotNull (prop.Predecessor, "#8");
+ }
+
+ [Test]
+ [ExpectedException (typeof(InvalidOperationException))]
+ public void UpdateGlobalPropertyValue ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var props = new Dictionary<string, string> ();
+ props.Add ("GP", "GV");
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root, props, null);
+ var pe = proj.Properties.First (p => p.IsGlobalProperty);
+ pe.UnevaluatedValue = "UPDATED";
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void UpdateEnvironmentPropertyValue ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ var pe = proj.Properties.First (p => p.IsEnvironmentProperty);
+ pe.UnevaluatedValue = "UPDATED";
+ }
+
+ [Test]
+ public void DeepReferences ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <A>1</A>
+ <B>$(A)+1</B>
+ <C>$(B)+2</C>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ Assert.AreEqual ("1+1+2", new Project (root).GetProperty ("C").EvaluatedValue, "#1");
+ Assert.AreEqual ("1+1+2", new ProjectInstance (root).GetProperty ("C").EvaluatedValue, "#1");
+ }
+
+ [Test]
+ public void PlatformPropertyEmptyByDefault ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ Assert.IsNull (proj.GetProperty ("PLATFORM"), "#1");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectTest.cs
new file mode 100644
index 0000000000..4ea4551816
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ProjectTest.cs
@@ -0,0 +1,253 @@
+//
+// ProjectTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using NUnit.Framework;
+using Microsoft.Build.Exceptions;
+using Microsoft.Build.Logging;
+using Microsoft.Build.Framework;
+
+namespace MonoTests.Microsoft.Build.Evaluation
+{
+ [TestFixture]
+ public class ProjectTest
+ {
+ [Test]
+ public void EscapeDoesWTF ()
+ {
+ string value_xml = "What are 'ESCAPE' &amp; \"EVALUATE\" ? $ # % ^";
+ string value = "What are 'ESCAPE' & \"EVALUATE\" ? $ # % ^";
+ string escaped = "What are %27ESCAPE%27 & \"EVALUATE\" %3f %24 # %25 ^";
+ string xml = string.Format (@"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Foo>{0}</Foo>
+ <Baz>$(FOO)</Baz>
+ </PropertyGroup>
+</Project>", value_xml);
+ var path = "file://localhost/foo.xml";
+ var reader = XmlReader.Create (new StringReader (xml), null, path);
+ var root = ProjectRootElement.Create (reader);
+ var proj = new Project (root);
+ var prop = proj.Properties.First (p => p.Name == "Foo");
+ Assert.AreEqual (value, prop.UnevaluatedValue, "#1");
+ Assert.AreEqual (value, prop.EvaluatedValue, "#2");
+ // eh?
+ Assert.AreEqual (value, Project.GetPropertyValueEscaped (prop), "#3");
+ prop = proj.Properties.First (p => p.Name == "Baz");
+ Assert.AreEqual ("$(FOO)", prop.UnevaluatedValue, "#4");
+ Assert.AreEqual (value, prop.EvaluatedValue, "#5");
+ // eh?
+ Assert.AreEqual (value, Project.GetPropertyValueEscaped (prop), "#6");
+
+ // OK you are fine.
+ Assert.AreEqual (escaped, ProjectCollection.Escape (value), "#7");
+ }
+
+ [Test]
+ public void FullPath ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ proj.FullPath = "ABC";
+ Assert.IsTrue (proj.FullPath.EndsWith (Path.DirectorySeparatorChar + "ABC"), "#1");
+ Assert.AreEqual (root.FullPath, proj.FullPath, "#2");
+ }
+
+ [Test]
+ public void BuildEmptyProject ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "ProjectTest.BuildEmptyProject.proj";
+
+ // This seems to do nothing and still returns true
+ Assert.IsTrue (new Project (root) { FullPath = "ProjectTest.BuildEmptyProject.1.proj" }.Build (), "#1");
+ // This seems to fail to find the appropriate target
+ Assert.IsFalse (new Project (root) { FullPath = "ProjectTest.BuildEmptyProject.2.proj" }.Build ("Build", null), "#2");
+ // Thus, this tries to build all the targets (empty) and no one failed, so returns true(!)
+ Assert.IsTrue (new Project (root) { FullPath = "ProjectTest.BuildEmptyProject.3.proj" }.Build (new string [0], null), "#3");
+ // Actially null "targets" is accepted and returns true(!!)
+ Assert.IsTrue (new Project (root) { FullPath = "ProjectTest.BuildEmptyProject.4.proj" }.Build ((string []) null, null), "#4");
+ // matching seems to be blindly done, null string also results in true(!!)
+ Assert.IsTrue (new Project (root) { FullPath = "ProjectTest.BuildEmptyProject.5.proj" }.Build ((string) null, null), "#5");
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void LoadInvalidProjectForBadCondition ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Foo>What are 'ESCAPE' &amp; ""EVALUATE"" ? $ # % ^</Foo>
+ <!-- Note that this contains invalid Condition expression, yet ProjectElement.Create() does NOT fail. -->
+ <Baz Condition=""$(Void)=="">$(FOO)</Baz>
+ </PropertyGroup>
+</Project>";
+ var reader = XmlReader.Create (new StringReader (xml));
+ var root = ProjectRootElement.Create (reader);
+ new Project (root);
+ }
+
+ [Test]
+ public void ExpandString ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Foo>What are 'ESCAPE' &amp; ""EVALUATE"" ? $ # % ^</Foo>
+ <Bar>y</Bar>
+ <Baz Condition=""$(Void)==''"">$(FOO)</Baz>
+ </PropertyGroup>
+</Project>";
+ var reader = XmlReader.Create (new StringReader (xml));
+ var root = ProjectRootElement.Create (reader);
+ var proj = new Project (root);
+ root.FullPath = "ProjectTest.ExpandString.proj";
+ Assert.AreEqual ("xyz", proj.ExpandString ("x$(BAR)z"), "#1");
+ Assert.AreEqual ("x$(BARz", proj.ExpandString ("x$(BARz"), "#2"); // incomplete
+ Assert.AreEqual ("xz", proj.ExpandString ("x@(BAR)z"), "#3"); // not an item
+ }
+
+ [Test]
+ public void BuildCSharpTargetGetFrameworkPaths ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.CSharp.targets' />
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ root.FullPath = "ProjectTest.BuildCSharpTargetGetFrameworkPaths.proj";
+ Assert.IsTrue (proj.Build ("GetFrameworkPaths", new ILogger [] {/*new ConsoleLogger ()*/}));
+ }
+
+ [Test]
+ public void BuildCSharpTargetBuild ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <AssemblyName>Foo</AssemblyName>
+ </PropertyGroup>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.CSharp.targets' />
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "ProjectTest.BuildCSharpTargetBuild.proj";
+ var proj = new Project (root, null, "4.0");
+ Assert.IsFalse (proj.Build ("Build", new ILogger [] {/*new ConsoleLogger (LoggerVerbosity.Diagnostic)*/})); // missing mandatory properties
+ }
+
+ [Test]
+ public void EvaluateItemConditionThenIgnored ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <P></P>
+ </PropertyGroup>
+ <ItemGroup>
+ <Foo Condition='' Include='x' />
+ <Bar Include='$(P)' />
+ <Baz Include='z' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ // note that Foo is ignored BUT Bar is NOT ignored.
+ Assert.AreEqual (2, proj.ItemsIgnoringCondition.Count, "#1");
+ Assert.IsNotNull ("Bar", proj.ItemsIgnoringCondition.First ().ItemType, "#2");
+ Assert.IsNotNull ("Baz", proj.ItemsIgnoringCondition.Last ().ItemType, "#3");
+ }
+
+ [Test]
+ public void EvaluateSamePropertiesInOrder ()
+ {
+ // used in Microsoft.Common.targets
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <BaseIntermediateOutputPath Condition=""'$(BaseIntermediateOutputPath)' == ''"">obj\</BaseIntermediateOutputPath>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ Assert.AreEqual ("obj\\", proj.GetPropertyValue ("BaseIntermediateOutputPath"), "#1");
+ }
+
+ [Test]
+ public void DirtyMarking ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ Assert.IsFalse (proj.IsDirty, "#1");
+ proj.MarkDirty ();
+ Assert.IsTrue (proj.IsDirty, "#2");
+ }
+
+ [Test]
+ public void DirtyMarking2 ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root);
+ proj.DisableMarkDirty = true;
+ proj.MarkDirty ();
+ Assert.IsFalse (proj.IsDirty, "#1"); // not rejected, just ignored.
+ proj.DisableMarkDirty = false;
+ Assert.IsFalse (proj.IsDirty, "#2"); // not like status pending
+ proj.MarkDirty ();
+ Assert.IsTrue (proj.IsDirty, "#3");
+ }
+
+ [Test]
+ public void CreateProjectInstance ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <AssemblyName>Foo</AssemblyName>
+ </PropertyGroup>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.CSharp.targets' />
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root, null, "4.0");
+ var inst = proj.CreateProjectInstance ();
+ Assert.AreEqual ("4.0", inst.ToolsVersion, "#1");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ResolvedImportTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ResolvedImportTest.cs
new file mode 100644
index 0000000000..9c4359ee7a
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ResolvedImportTest.cs
@@ -0,0 +1,181 @@
+//
+// ResolvedImportTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using NUnit.Framework;
+using Microsoft.Build.Exceptions;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Execution;
+
+namespace MonoTests.Microsoft.Build.Evaluation
+{
+ [TestFixture]
+ public class ResolvedImportTest
+ {
+ const string temp_file_name = "test_imported.proj";
+
+ [Test]
+ public void SimpleImportAndSemanticValues ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='test_imported.proj' />
+</Project>";
+ string imported = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <A>x</A>
+ <B>y</B>
+ </PropertyGroup>
+ <ItemGroup>
+ <X Include=""included.txt"" />
+ </ItemGroup>
+</Project>";
+ using (var ts = File.CreateText (temp_file_name))
+ ts.Write (imported);
+ try {
+ var reader = XmlReader.Create (new StringReader (xml));
+ var root = ProjectRootElement.Create (reader);
+ Assert.AreEqual (temp_file_name, root.Imports.First ().Project, "#1");
+ var proj = new Project (root);
+ var prop = proj.GetProperty ("A");
+ Assert.IsNotNull (prop, "#2");
+ Assert.IsTrue (prop.IsImported, "#3");
+ var item = proj.GetItems ("X").FirstOrDefault ();
+ Assert.IsNotNull (item, "#4");
+ Assert.AreEqual ("included.txt", item.EvaluatedInclude, "#5");
+ } finally {
+ File.Delete (temp_file_name);
+ }
+ }
+
+ string import_overrides_test_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <A>X</A>
+ </PropertyGroup>
+ <Import Condition=""{0}"" Project='test_imported.proj' />
+ <PropertyGroup>
+ <B>Y</B>
+ </PropertyGroup>
+</Project>";
+ string import_overrides_test_imported = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <C Condition='$(A)==x'>c</C>
+ <A>a</A>
+ <B>b</B>
+ </PropertyGroup>
+ <ItemGroup>
+ <X Include=""included.txt"" />
+ </ItemGroup>
+</Project>";
+
+ void ImportAndPropertyOverrides (string label, string condition, string valueA, string valueB, string valueAPredecessor, bool existsC)
+ {
+ using (var ts = File.CreateText (temp_file_name))
+ ts.Write (import_overrides_test_imported);
+ try {
+ string xml = string.Format (import_overrides_test_xml, condition);
+ var reader = XmlReader.Create (new StringReader (xml));
+ var root = ProjectRootElement.Create (reader);
+ var proj = new Project (root);
+ var a = proj.GetProperty ("A");
+ Assert.IsNotNull (a, label + "#2");
+ Assert.AreEqual (valueA, a.EvaluatedValue, label + "#3");
+ if (valueAPredecessor == null)
+ Assert.IsNull (a.Predecessor, label + "#3.1");
+ else {
+ Assert.IsNotNull (a.Predecessor, label + "#3.2");
+ Assert.AreEqual (valueAPredecessor, a.Predecessor.EvaluatedValue, label + "#3.3");
+ }
+ var b = proj.GetProperty ("B");
+ Assert.IsNotNull (b, label + "#4");
+ Assert.AreEqual (valueB, b.EvaluatedValue, label + "#5");
+ var c = proj.GetProperty ("C"); // yes it can be retrieved.
+ if (existsC) {
+ Assert.IsNotNull (c, label + "#6");
+ Assert.AreEqual ("c", c.EvaluatedValue, label + "#7");
+ }
+ else
+ Assert.IsNull (c, label + "#8");
+ } finally {
+ File.Delete (temp_file_name);
+ }
+ }
+
+ [Test]
+ public void ImportAndPropertyOverrides ()
+ {
+ ImportAndPropertyOverrides ("[1]", "'True'", "a", "Y", "X", true);
+ ImportAndPropertyOverrides ("[2]", "$(A)=='X'", "a", "Y", "X", true); // evaluated as true
+ ImportAndPropertyOverrides ("[3]", "$(B)=='Y'", "X", "Y", null, false); // evaluated as false
+ ImportAndPropertyOverrides ("[4]", "$(B)=='b'", "X", "Y", null, false); // of course not evaluated with imported value
+ }
+
+ // FIXME:
+ // Looks like $(MSBuildThisFile) is available only within property value, not via .NET MSBuild API.
+ // Right now our variable is added as a Reserved property, but we will have to hide it.
+ //
+ [Test]
+ public void EvaluateMSBuildThisFileProperty ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <A>$(MSBuildThisFile)</A>
+ </PropertyGroup>
+ <Import Project='test_imported.proj' />
+</Project>";
+ string imported = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <B>$(MSBuildThisFile)</B>
+ </PropertyGroup>
+</Project>";
+ using (var ts = File.CreateText (temp_file_name))
+ ts.Write (imported);
+ try {
+ var reader = XmlReader.Create (new StringReader (xml));
+ var root = ProjectRootElement.Create (reader);
+ var proj = new Project (root);
+ var a = proj.GetProperty ("A");
+ Assert.AreEqual (string.Empty, a.EvaluatedValue, "#1");
+ var b = proj.GetProperty ("B");
+ Assert.AreEqual (temp_file_name, b.EvaluatedValue, "#2");
+
+ var pi = new ProjectInstance (root);
+ var ai = pi.GetProperty ("A");
+ Assert.AreEqual (string.Empty, ai.EvaluatedValue, "#3");
+ var bi = pi.GetProperty ("B");
+ Assert.AreEqual (temp_file_name, bi.EvaluatedValue, "#4");
+ } finally {
+ File.Delete (temp_file_name);
+ }
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ToolsetTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ToolsetTest.cs
new file mode 100644
index 0000000000..ac4e2d6610
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Evaluation/ToolsetTest.cs
@@ -0,0 +1,53 @@
+//
+// ToolSetTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using Microsoft.Build.Evaluation;
+using NUnit.Framework;
+
+namespace MonoTests.Microsoft.Build.Evaluation
+{
+ [TestFixture]
+ public class ToolsetTest
+ {
+ [Test]
+ public void Constructor ()
+ {
+ var ts = new Toolset ("4.3", "c:\\", ProjectCollection.GlobalProjectCollection, null);
+ Assert.IsNotNull (ts.Properties, "#1");
+ Assert.AreEqual (0, ts.Properties.Count, "#2");
+#if NET_4_5
+ Assert.IsNull (ts.DefaultSubToolsetVersion, "#3");
+ Assert.IsNotNull (ts.SubToolsets, "#4");
+ Assert.AreEqual (0, ts.SubToolsets.Count, "#5");
+#endif
+ Assert.AreEqual ("c:\\", ts.ToolsPath, "#6");
+ Assert.AreEqual ("4.3", ts.ToolsVersion, "#7");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildManagerTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildManagerTest.cs
new file mode 100644
index 0000000000..0fcbdf7560
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildManagerTest.cs
@@ -0,0 +1,201 @@
+//
+// BuildManagerTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Logging;
+
+namespace MonoTests.Microsoft.Build.Execution
+{
+ [TestFixture]
+ public class BuildManagerTest
+ {
+ Project GetDummyProject ()
+ {
+ string empty_project_xml = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var path = "file://localhost/foo.xml";
+ var xml = XmlReader.Create (new StringReader (empty_project_xml), null, path);
+ var root = ProjectRootElement.Create (xml);
+ return new Project (root);
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void GetProjectInstanceForBuildNullFullPath ()
+ {
+ var manager = new BuildManager ();
+ manager.GetProjectInstanceForBuild (GetDummyProject ());
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentException))]
+ public void GetProjectInstanceForBuildEmptyFullPath ()
+ {
+ var proj = GetDummyProject ();
+ proj.FullPath = "";
+ var manager = new BuildManager ();
+ manager.GetProjectInstanceForBuild (proj);
+ }
+
+ [Test]
+ public void GetProjectInstanceForBuild ()
+ {
+ string empty_project_xml = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var path = "file://localhost/foo.xml";
+ var xml = XmlReader.Create (new StringReader(empty_project_xml), null, path);
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = path;
+ var proj = new Project (root);
+ var manager = new BuildManager ();
+ var inst = manager.GetProjectInstanceForBuild (proj);
+ Assert.AreEqual (inst, manager.GetProjectInstanceForBuild (proj), "#1");
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void PendBuildRequestBeforeBeginBuild ()
+ {
+ string empty_project_xml = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var path = "file://localhost/foo.xml";
+ var xml = XmlReader.Create (new StringReader (empty_project_xml), null, path);
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ new BuildManager ().PendBuildRequest (new BuildRequestData (proj, new string [0]));
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidOperationException))]
+ public void ResetCachesDuringBuildIsInvalid ()
+ {
+ // Windows does not have useful sleep or alternative, so skip it
+ bool is_windows = true;
+ switch (Environment.OSVersion.Platform) {
+ case PlatformID.Unix:
+ case PlatformID.MacOSX:
+ is_windows = false;
+ break;
+ }
+ string project_xml = string.Format (@"<Project DefaultTargets='Wait1Sec' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Target Name='Wait1Sec'>
+ <Exec Command='{0}' />
+ </Target>
+</Project>", is_windows ? "powershell -command \"Start-Sleep -s 1\"" : "/bin/sleep 1");
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ var bm = new BuildManager ();
+ bm.BeginBuild (new BuildParameters ());
+ var sub = bm.PendBuildRequest (new BuildRequestData (proj, new string [] { "Wait1Sec" }));
+ sub.ExecuteAsync (delegate {}, null);
+ try {
+ bm.ResetCaches ();
+ } finally {
+ bm.EndBuild (); // yes, it should work even after invalid ResetCaches call... at least on .NET it does.
+ }
+ }
+
+ [Test]
+ public void BasicManualParallelBuilds ()
+ {
+ string project_xml = @"<Project DefaultTargets='Wait1Sec' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Target Name='Wait1Sec'>
+ <!-- Exec Command='ping 10.1.1.1 -n 1 -w 1' /-->
+ <Exec Command='/bin/sleep 1' />
+ </Target>
+</Project>";
+ switch (Environment.OSVersion.Platform) {
+ case PlatformID.MacOSX:
+ case PlatformID.Unix:
+ break;
+ default:
+ return; // ignore, cannot run it
+ }
+
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ var bm = new BuildManager ();
+ bm.BeginBuild (new BuildParameters () { Loggers = new ILogger [] {new ConsoleLogger (LoggerVerbosity.Diagnostic, TextWriter.Null.WriteLine, null, null)} });
+ DateTime waitDone = DateTime.MinValue;
+ DateTime beforeExec = DateTime.Now;
+ var l = new List<BuildSubmission> ();
+ for (int i = 0; i < 10; i++) {
+ var sub = bm.PendBuildRequest (new BuildRequestData (proj, new string [] { "Wait1Sec" }));
+ l.Add (sub);
+ sub.ExecuteAsync (delegate { waitDone = DateTime.Now; }, null);
+ }
+ bm.EndBuild ();
+ Assert.IsTrue (l.All (s => s.BuildResult.OverallResult == BuildResultCode.Success), "#1");
+ DateTime endBuildDone = DateTime.Now;
+ Assert.IsTrue (endBuildDone - beforeExec >= TimeSpan.FromSeconds (1), "#2");
+ Assert.IsTrue (endBuildDone > waitDone, "#3");
+ }
+
+ [Test]
+ public void BuildCommonResolveAssemblyReferences ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.Common.targets' />
+ <ItemGroup>
+ <Reference Include='System.Core' />
+ <Reference Include='System.Xml' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "BuildManagerTest.BuildCommonResolveAssemblyReferences.proj";
+ var proj = new ProjectInstance (root);
+ var manager = new BuildManager ();
+ var parameters = new BuildParameters () { Loggers = new ILogger [] {new ConsoleLogger (LoggerVerbosity.Diagnostic)} };
+ var request = new BuildRequestData (proj, new string [] {"ResolveAssemblyReferences"});
+ Assert.AreEqual (string.Empty, proj.GetPropertyValue ("TargetFrameworkDirectory"), "#1-1");
+ var result = manager.Build (parameters, request);
+ Assert.AreNotEqual (string.Empty, proj.GetPropertyValue ("TargetFrameworkDirectory"), "#1-2"); // filled during build.
+ Assert.IsTrue (result.ResultsByTarget.ContainsKey ("GetFrameworkPaths"), "#2-1");
+ Assert.IsTrue (result.ResultsByTarget.ContainsKey ("PrepareForBuild"), "#2-2");
+ Assert.IsTrue (result.ResultsByTarget.ContainsKey ("ResolveAssemblyReferences"), "#2-3");
+ var items = proj.GetItems ("ReferencePath");
+ Assert.AreEqual (2, items.Count (), "#3");
+ var syscore = items.FirstOrDefault (i => Path.GetFileName (i.EvaluatedInclude) == "System.Core.dll");
+ var sysxml = items.FirstOrDefault (i => Path.GetFileName (i.EvaluatedInclude) == "System.Xml.dll");
+ Assert.IsNotNull (syscore, "#4-1");
+ Assert.IsNotNull (sysxml, "#4-2");
+ Assert.IsTrue (File.Exists (syscore.EvaluatedInclude), "#5-1");
+ Assert.IsTrue (File.Exists (sysxml.EvaluatedInclude), "#5-1");
+ Assert.AreEqual (BuildResultCode.Success, result.OverallResult, "#6");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildParametersTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildParametersTest.cs
new file mode 100644
index 0000000000..47cd5f9007
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildParametersTest.cs
@@ -0,0 +1,66 @@
+//
+// BuildParametersTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+
+namespace MonoTests.Microsoft.Build.Execution
+{
+ [TestFixture]
+ public class BuildParametersTest
+ {
+ [Test]
+ public void GetToolset ()
+ {
+ var bp = new BuildParameters (ProjectCollection.GlobalProjectCollection);
+ Assert.IsNull (bp.GetToolset ("0.1"), "#1");
+ var ts = bp.GetToolset ("2.0");
+ // They are equal
+ Assert.AreEqual (ProjectCollection.GlobalProjectCollection.Toolsets.First (t => t.ToolsVersion == "2.0"), ts, "#2");
+
+ bp = new BuildParameters ();
+ Assert.IsNull (bp.GetToolset ("0.1"), "#1");
+ ts = bp.GetToolset ("2.0");
+ // They are NOT equal, because ProjectCollection seems to be different.
+ Assert.AreNotEqual (ProjectCollection.GlobalProjectCollection.Toolsets.First (t => t.ToolsVersion == "2.0"), ts, "#2");
+ }
+
+ [Test]
+ public void PropertiesDefault ()
+ {
+ var bp = new BuildParameters ();
+ Assert.IsTrue (bp.EnableNodeReuse, "#1");
+ Assert.IsTrue (bp.EnvironmentProperties.Count > 0, "#2");
+ Assert.AreEqual (CultureInfo.CurrentCulture, bp.Culture, "#3");
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildSubmissionTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildSubmissionTest.cs
new file mode 100644
index 0000000000..df2febcbf7
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/BuildSubmissionTest.cs
@@ -0,0 +1,115 @@
+//
+// BuildSubmissionTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+using Microsoft.Build.Logging;
+using Microsoft.Build.Framework;
+using System.Collections.Generic;
+
+namespace MonoTests.Microsoft.Build.Execution
+{
+ [TestFixture]
+ public class BuildSubmissionTest
+ {
+ [Test]
+ public void ResultBeforeExecute ()
+ {
+ string empty_project_xml = "<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var path = "file://localhost/foo.xml";
+ var xml = XmlReader.Create (new StringReader (empty_project_xml), null, path);
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ var bm = new BuildManager ();
+ bm.BeginBuild (new BuildParameters ());
+ var sub = bm.PendBuildRequest (new BuildRequestData (proj, new string [0]));
+ Assert.IsNull (sub.BuildResult, "#1");
+ }
+
+ // This checks if the build output for each task is written to the loggers and not directly thrown as a Project loader error.
+ [Test]
+ public void TaskOutputsToLoggers ()
+ {
+ string project_xml = @"<Project DefaultTargets='Foo' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.Common.targets' />
+ <Target Name='Foo'>
+ <ItemGroup>
+ <Foo Condition='$(X)' Include='foo.txt' />
+ </ItemGroup>
+ </Target>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "BuildSubmissionTest.TaskOutputsToLoggers.proj";
+ var proj = new ProjectInstance (root);
+ Assert.AreEqual ("$(X)", root.Targets.First ().ItemGroups.First ().Items.First ().Condition, "#0");
+ var sw = new StringWriter ();
+ Assert.IsFalse (proj.Build (new ILogger [] {new ConsoleLogger (LoggerVerbosity.Diagnostic, sw.WriteLine, null, null)}), "#1");
+ Assert.IsTrue (sw.ToString ().Contains ("$(X)"), "#2");
+ }
+
+ [Test]
+ public void EndBuildWaitsForSubmissionCompletion ()
+ {
+ // Windows does not have useful sleep or alternative, so skip it
+ bool is_windows = true;
+ switch (Environment.OSVersion.Platform) {
+ case PlatformID.Unix:
+ case PlatformID.MacOSX:
+ is_windows = false;
+ break;
+ }
+ string project_xml = string.Format (@"<Project DefaultTargets='Wait1Sec' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Target Name='Wait1Sec'>
+ <Exec Command='{0}' />
+ </Target>
+</Project>", is_windows ? "powershell -command \"Start-Sleep -s 1\"" : "/bin/sleep 1");
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "BuildSubmissionTest.EndBuildWaitsForSubmissionCompletion.proj";
+ var proj = new ProjectInstance (root);
+ var bm = new BuildManager ();
+ bm.BeginBuild (new BuildParameters ());
+ DateTime waitDone = DateTime.MinValue;
+ DateTime beforeExec = DateTime.Now;
+ var sub = bm.PendBuildRequest (new BuildRequestData (proj, new string [] { "Wait1Sec" }));
+ sub.ExecuteAsync (delegate { waitDone = DateTime.Now; }, null);
+ bm.EndBuild ();
+ Assert.IsTrue (sub.BuildResult.OverallResult == BuildResultCode.Success, "#1");
+ DateTime endBuildDone = DateTime.Now;
+ Assert.IsTrue (endBuildDone - beforeExec >= TimeSpan.FromSeconds (1), "#2");
+ Assert.IsTrue (endBuildDone > waitDone, "#3");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectInstanceTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectInstanceTest.cs
new file mode 100644
index 0000000000..327aa389c4
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectInstanceTest.cs
@@ -0,0 +1,103 @@
+//
+// ProjectInstanceTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+using Microsoft.Build.Evaluation;
+
+namespace MonoTests.Microsoft.Build.Execution
+{
+ [TestFixture]
+ public class ProjectInstanceTest
+ {
+ [Test]
+ public void ItemsAndProperties ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <X Condition='false' Include='bar.txt' />
+ <X Include='foo.txt'>
+ <M>m</M>
+ <N>=</N>
+ </X>
+ </ItemGroup>
+ <PropertyGroup>
+ <P Condition='false'>void</P>
+ <P Condition='true'>valid</P>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader(project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ var item = proj.Items.First ();
+ Assert.AreEqual ("foo.txt", item.EvaluatedInclude, "#1");
+ var prop = proj.Properties.First (p => p.Name=="P");
+ Assert.AreEqual ("valid", prop.EvaluatedValue, "#2");
+ Assert.IsNotNull (proj.GetProperty ("MSBuildProjectDirectory"), "#3");
+ Assert.AreEqual ("2.0", proj.ToolsVersion, "#4");
+ }
+
+ [Test]
+ public void ExplicitToolsVersion ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader(project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root, null, "4.0", new ProjectCollection ());
+ Assert.AreEqual ("4.0", proj.ToolsVersion, "#1");
+ }
+
+ [Test]
+ public void BuildEmptyProject ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' />";
+ var xml = XmlReader.Create (new StringReader (project_xml), null, "file://localhost/foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ // This seems to do nothing and still returns true
+ root.FullPath = "ProjectInstanceTest.BuildEmptyProject.1.proj";
+ Assert.IsTrue (new ProjectInstance (root).Build (), "#1");
+ // This seems to fail to find the appropriate target
+ root.FullPath = "ProjectInstanceTest.BuildEmptyProject.2.proj";
+ Assert.IsFalse (new ProjectInstance (root).Build ("Build", null), "#2");
+ // Thus, this tries to build all the targets (empty) and no one failed, so returns true(!)
+ root.FullPath = "ProjectInstanceTest.BuildEmptyProject.3.proj";
+ Assert.IsTrue (new ProjectInstance (root).Build (new string [0], null), "#3");
+ // Actially null "targets" is accepted and returns true(!!)
+ root.FullPath = "ProjectInstanceTest.BuildEmptyProject.4.proj";
+ Assert.IsTrue (new ProjectInstance (root).Build ((string []) null, null), "#4");
+ // matching seems to be blindly done, null string also results in true(!!)
+ root.FullPath = "ProjectInstanceTest.BuildEmptyProject.5.proj";
+ Assert.IsTrue (new ProjectInstance (root).Build ((string) null, null), "#5");
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectMetadataInstanceTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectMetadataInstanceTest.cs
new file mode 100644
index 0000000000..5c136d6b60
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectMetadataInstanceTest.cs
@@ -0,0 +1,78 @@
+//
+// ProjectMetadataInstanceTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+
+namespace MonoTests.Microsoft.Build.Execution
+{
+ [TestFixture]
+ public class ProjectMetadataInstanceTest
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <X Include='foo.txt'>
+ <M>m</M>
+ <N>=</N>
+ </X>
+ </ItemGroup>
+</Project>";
+
+ [Test]
+ public void PropertiesCopiesValues ()
+ {
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ string path = Path.GetFullPath ("foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ var item = proj.Items.First ();
+ var md = item.Metadata.First ();
+ Assert.AreEqual ("m", item.Metadata.First ().EvaluatedValue, "#1");
+ Assert.AreEqual ("m", root.ItemGroups.First ().Items.First ().Metadata.First ().Value, "#2");
+ root.ItemGroups.First ().Items.First ().Metadata.First ().Value = "X";
+ Assert.AreEqual ("m", item.Metadata.First ().EvaluatedValue, "#3");
+ }
+
+ [Test]
+ public void ToStringOverride ()
+ {
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ string path = Path.GetFullPath ("foo.xml");
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ var item = proj.Items.First ();
+ Assert.AreEqual ("M=m", item.Metadata.First ().ToString (), "#1");
+ Assert.AreEqual ("N==", item.Metadata.Last ().ToString (), "#2"); // haha
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectTargetInstanceTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectTargetInstanceTest.cs
new file mode 100644
index 0000000000..2ae7148c8f
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Execution/ProjectTargetInstanceTest.cs
@@ -0,0 +1,172 @@
+//
+// ProjectTargetInstanceTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Execution;
+using NUnit.Framework;
+using Microsoft.Build.Logging;
+using Microsoft.Build.Framework;
+
+namespace MonoTests.Microsoft.Build.Execution
+{
+ [TestFixture]
+ public class ProjectTargetInstanceTest
+ {
+ [Test]
+ public void DefaultTargetsEmpty ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ Assert.AreEqual (new string [0], proj.DefaultTargets, "#1");
+ }
+
+ [Test]
+ public void DefaultTargetsFromAttribute ()
+ {
+ string project_xml = @"<Project DefaultTargets='Foo Bar Baz;Foo' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ string [] expected = {"Foo Bar Baz", "Foo"};
+ Assert.AreEqual (expected, proj.DefaultTargets, "#1");
+ }
+
+ [Test]
+ public void DefaultTargetsFromElements ()
+ {
+ string [] defaultTargetAtts = {string.Empty, "DefaultTargets=''"};
+
+ for (int i = 0; i < defaultTargetAtts.Length; i++) {
+ string project_xml = string.Format (@"<Project {0} xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Target Name='Foo' />
+ <Target Name='Bar' />
+</Project>", defaultTargetAtts [i]);
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ string [] expected = {"Foo"}; // Bar is not included
+ Assert.AreEqual (expected, proj.DefaultTargets, "#1-" + i);
+ }
+ }
+
+ [Test]
+ public void MicrosoftCommonTargets ()
+ {
+ string [] defaultTargetAtts = { string.Empty, "DefaultTargets=''" };
+
+ for (int i = 0; i < defaultTargetAtts.Length; i++) {
+ string project_xml = string.Format (@"<Project {0} xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.Common.targets' />
+</Project>", defaultTargetAtts [i]);
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ Assert.AreEqual ("Build", proj.DefaultTargets.FirstOrDefault (), "#1-" + i);
+ }
+ }
+
+ [Test]
+ public void DefaultTargetsOverride ()
+ {
+ string project_xml = @"<Project DefaultTargets='Foo' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.Common.targets' />
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new ProjectInstance (root);
+ Assert.AreEqual ("Foo", proj.DefaultTargets.FirstOrDefault (), "#1");
+ }
+
+ [Test]
+ public void MultipleDefaultTargets ()
+ {
+ bool[] expected = { true, false, true };
+ string [] defaultTargets = {"Foo", "Foo;Bar", "Foo;Bar"};
+ string [] targets = { string.Empty, string.Empty, "<Target Name='Bar' />" };
+ for (int i = 0; i < expected.Length; i++) {
+ string project_xml = string.Format (@"<Project DefaultTargets='{0}' xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Import Project='$(MSBuildToolsPath)\Microsoft.Common.targets' />
+ <Target Name='Foo' />
+ {1}
+</Project>", defaultTargets [i], targets [i]);
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = string.Format ("ProjectInstanceTest.MultipleDefaultTargets.{0}.proj", i);
+ var proj = new ProjectInstance (root);
+ Assert.AreEqual ("Foo", proj.DefaultTargets.FirstOrDefault (), "#1-" + i);
+ Assert.AreEqual (expected [i], proj.Build (), "#2-" + i);
+ }
+ }
+
+ [Test]
+ public void DependsOnTargets ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Target Name='Bar' DependsOnTargets='Foo' />
+ <Target Name='Foo'>
+ <Error Text='expected error' />
+ </Target>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "ProjectInstanceTest.DependsOnTargets.proj";
+ var proj = new ProjectInstance (root);
+ Assert.AreEqual (2, proj.Targets.Count, "#1");
+ Assert.IsFalse (proj.Build ("Bar", new ILogger [0]), "#2");
+ }
+
+ [Test]
+ public void InputsAndOutputs ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <Target Name='Foo' Inputs='inputsandoutputstest.txt' Outputs='inputsandoutputstest.txt'>
+ <Error Text='error' />
+ </Target>
+</Project>";
+ try {
+ if (!File.Exists ("inputsandoutputstest.txt"))
+ File.CreateText ("inputsandoutputstest.txt").Close ();
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ root.FullPath = "ProjectTargetInstanceTest.InputsAndOutputs.proj";
+ var proj = new ProjectInstance (root);
+ Assert.IsTrue (proj.Build (), "#1"); // if it does not skip Foo, it results in an error.
+ } finally {
+ if (File.Exists ("inputsandoutputstest.txt"))
+ File.Delete ("inputsandoutputstest.txt");
+ }
+ }
+ }
+}
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs
new file mode 100644
index 0000000000..7654538a1a
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Internal/ExpressionParserTest.cs
@@ -0,0 +1,287 @@
+//
+// ExpressionParserTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Evaluation;
+using NUnit.Framework;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Exceptions;
+using System.Collections.Generic;
+
+namespace MonoTests.Microsoft.Build.Internal
+{
+ [TestFixture]
+ public class ExpressionParserTest
+ {
+ string [] invalid_always = {
+ "$(Foo..Bar)",
+ "$([DateTime.Now])", // fullname required
+ "$([System.DateTime.Now])", // member cannot be invoked with '.'
+ };
+ string [] invalid_as_boolean = {
+ "$",
+ "@",
+ "%",
+ "%-1",
+ "$(",
+ "%(",
+ "$)",
+ "%)",
+ "%24",
+ "()",
+ "{}",
+ "A", // must be evaluated as a boolean
+ "1", // ditto (no default conversion to bool)
+ "$ (foo) == ''",
+ "@ (foo) == ''",
+ "$(1)",
+ "$(Foo) == And", // reserved keyword 'and'
+ "$(Foo) == Or", // reserved keyword 'or'
+ "$(Foo) == $(Bar) == $(Baz)", // unexpected '=='
+ "$([System.DateTime]::Now)", // it is DateTime
+ "$([System.String]::Format('Tr'))$([System.String]::Format('ue'))", // only one expression is accepted
+ "$([System.String]::Format(null))", // causing ANE, wrapped by InvalidProjectFileException
+ "yep",
+ "nope",
+ "ONN",
+ "OFFF",
+ };
+ string [] valid = {
+ "'%24' == 0",
+ "true",
+ "fAlSe",
+ "(false)",
+ "A==A",
+ "A ==A",
+ "A== A",
+ "A=='A'",
+ "A==\tA",
+ "\tA== A",
+ "$([System.String]::Format('True'))",
+ "$([System.String]::Format('True', null))",
+ "False And True == True And True",
+ "True or True or False",
+ "(True or True or False)",
+ "True and False",
+ "(True) and (False)",
+ "yes",
+ "nO",
+ "oN",
+ "oFf",
+ };
+ string [] depends = {
+ // valid only if evaluated to boolean
+ "$(foo)",
+ "@(foo)",
+ };
+
+ [Test]
+ public void EvaluateAsBoolean ()
+ {
+ foreach (var expr in invalid_always.Concat (invalid_as_boolean).Concat (valid)) {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Condition=""{0}"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
+ var root = ProjectRootElement.Create (xml);
+ try {
+ new Project (root);
+ if (invalid_as_boolean.Contains (expr) || invalid_always.Contains (expr))
+ Assert.Fail ("Parsing Condition '{0}' should fail", expr);
+ } catch (Exception ex) {
+ if (valid.Contains (expr))
+ throw new Exception (string.Format ("failed to parse '{0}'", expr), ex);
+ else if (ex is InvalidProjectFileException)
+ continue;
+ throw new Exception (string.Format ("unexpected exception to parse '{0}'", expr), ex);
+ }
+ }
+ }
+
+ [Test]
+ public void EvaluateAsString ()
+ {
+ foreach (var expr in invalid_always.Concat (invalid_as_boolean).Concat (valid)) {
+ try {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include=""{0}"" />
+ </ItemGroup>
+ </Project>";
+ var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
+ var root = ProjectRootElement.Create (xml);
+ // everything but 'invalid_always' should pass
+ new Project (root);
+ } catch (Exception ex) {
+ if (!invalid_always.Contains (expr))
+ throw new Exception (string.Format ("failed to parse '{0}'", expr), ex);
+ }
+ }
+ }
+
+ [Test]
+ public void EvaluatePropertyReferencesWithProperties ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Condition=""$(foo)"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var props = new Dictionary<string,string> ();
+ props ["foo"] = "true";
+ new Project (root, props, null);
+ }
+
+ [Test]
+ public void EvaluateItemReferences ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Include='false' />
+ <!-- by the time Bar is evaluated, Foo is already evaluated and taken into consideration in expansion -->
+ <Bar Condition=""@(foo)"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ new Project (root);
+ }
+
+ [Test]
+ public void EvaluateReferencesWithoutProperties ()
+ {
+ foreach (var expr in depends) {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Foo Condition=""{0}"" Include='x' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (string.Format (project_xml, expr)));
+ var root = ProjectRootElement.Create (xml);
+ try {
+ new Project (root);
+ Assert.Fail ("Parsing Condition '{0}' should fail", expr);
+ } catch (InvalidProjectFileException) {
+ continue;
+ }
+ }
+ }
+
+ [Test]
+ public void SemicolonHandling ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <Foo Condition=""'A;B'=='A;B'"">'A;B'</Foo>
+ </PropertyGroup>
+ <ItemGroup>
+ <Bar Include='$(Foo)' />
+ </ItemGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root); // at this state property is parsed without error i.e. Condition evaluates fine.
+ var prop = proj.GetProperty ("Foo");
+ Assert.AreEqual ("'A;B'", prop.EvaluatedValue, "#1");
+ var items = proj.GetItems ("Bar");
+ Assert.AreEqual ("'A", items.First ().EvaluatedInclude, "#2");
+ Assert.AreEqual ("$(Foo)", items.First ().UnevaluatedInclude, "#3");
+ Assert.AreEqual (2, items.Count, "#4");
+ Assert.AreEqual ("B'", items.Last ().EvaluatedInclude, "#5");
+ Assert.AreEqual ("$(Foo)", items.Last ().UnevaluatedInclude, "#6");
+ Assert.IsTrue (items.First ().Xml == items.Last ().Xml, "#7");
+ }
+
+ // the same as above except that ItemGroup goes first (and yet evaluated the same).
+ [Test]
+ public void EvaluationOrderPropertiesPrecedesItems ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Bar Include='$(Foo)' />
+ </ItemGroup>
+ <PropertyGroup>
+ <Foo Condition=""'A;B'=='A;B'"">'A;B'</Foo>
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ var proj = new Project (root); // at this state property is parsed without error i.e. Condition evaluates fine.
+ var prop = proj.GetProperty ("Foo");
+ Assert.AreEqual ("'A;B'", prop.EvaluatedValue, "#1");
+ var items = proj.GetItems ("Bar");
+ Assert.AreEqual ("'A", items.First ().EvaluatedInclude, "#2");
+ Assert.AreEqual ("$(Foo)", items.First ().UnevaluatedInclude, "#3");
+ Assert.AreEqual (2, items.Count, "#4");
+ Assert.AreEqual ("B'", items.Last ().EvaluatedInclude, "#5");
+ Assert.AreEqual ("$(Foo)", items.Last ().UnevaluatedInclude, "#6");
+ Assert.IsTrue (items.First ().Xml == items.Last ().Xml, "#7");
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void PropertyReferencesItem ()
+ {
+ string project_xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <ItemGroup>
+ <Bar Include='True' />
+ </ItemGroup>
+ <PropertyGroup>
+ <Foo Condition='@(Bar)'>X</Foo><!-- not allowed -->
+ </PropertyGroup>
+</Project>";
+ var xml = XmlReader.Create (new StringReader (project_xml));
+ var root = ProjectRootElement.Create (xml);
+ new Project (root);
+ }
+
+ [Test]
+ [ExpectedException (typeof (InvalidProjectFileException))]
+ public void SequentialPropertyReferenceNotAllowed ()
+ {
+ string xml = @"<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003'>
+ <PropertyGroup>
+ <A>x</A>
+ <B>y</B>
+ <C Condition=""$(A)$(B)==''"">z</C>
+ </PropertyGroup>
+</Project>";
+ var reader = XmlReader.Create (new StringReader (xml));
+ var root = ProjectRootElement.Create (reader);
+ new Project (root);
+ }
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/ConsoleLoggerTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/ConsoleLoggerTest.cs
new file mode 100644
index 0000000000..0135393d9e
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/ConsoleLoggerTest.cs
@@ -0,0 +1,102 @@
+//
+// ConsoleLoggerTest.cs
+//
+// Author:
+// Atsushi Enomoto (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Logging;
+using NUnit.Framework;
+
+namespace MonoTests.Microsoft.Build.Logging
+{
+ [TestFixture]
+ public class ConsoleLoggerTest
+ {
+ // Unfortunately, the existing code in MS.Build.Engine.dll has slightly different
+ // format. We'd rather use already complete implementation, just disabled this test.
+ [Test]
+ [Category ("NotWorking")]
+ public void BasicLoggerUsage ()
+ {
+ string expected = @"file : cat error code: msg
+
+file : cat warning code: msg
+
+__________________________________________________
+
+Project ""project.txt"" (target target(s)):
+
+
+
+Build started 2013/01/01 00:00:00.
+
+Target ""target"" in file ""target.txt"":
+
+ start task
+
+ finished task
+
+finished target
+
+
+
+finished project
+
+
+
+finished build
+
+
+
+Time Elapsed 00:00:00.01
+
+".Replace ("\r\n", "\n");
+ var sw = new StringWriter();
+ var e = new ConsoleLogger(LoggerVerbosity.Diagnostic, msg => sw.WriteLine(msg), c => {}, () => {});
+ e.Verbosity = LoggerVerbosity.Diagnostic;
+ e.ErrorHandler (null, new BuildErrorEventArgs ("cat", "code", "file", 0, 0, 0, 0, "msg", "help", "sender"));
+ e.WarningHandler (null, new BuildWarningEventArgs ("cat", "code", "file", 0, 0, 0, 0, "msg", "help", "sender"));
+ e.ProjectStartedHandler (null, new ProjectStartedEventArgs ("start project", "HELPME", "project.txt", "target", null, null));
+ e.BuildStartedHandler (null, new BuildStartedEventArgs ("start build", "HELPME", new DateTime (2013, 1, 1)));
+ e.TargetStartedHandler (null, new TargetStartedEventArgs ("start target", "HELPME", "target", "project.txt", "target.txt"/*, "parent"*/));
+ e.TaskStartedHandler (null, new TaskStartedEventArgs ("start task", "HELPME", "project.txt", "task.txt", "task"));
+ e.TaskFinishedHandler (null, new TaskFinishedEventArgs ("finished task", "HELPME", "project.txt", "task.txt", "task", false));
+ e.TargetFinishedHandler (null, new TargetFinishedEventArgs ("finished target", "HELPME", "target", "project.txt", "target.txt", false));
+ e.ProjectFinishedHandler (null, new ProjectFinishedEventArgs ("finished project", "HELPME", "project.txt", false));
+ e.BuildFinishedHandler (null, new BuildFinishedEventArgs ("finished build", "HELPME", false, new DateTime (2013, 1, 1).AddMilliseconds (1)));
+
+ e.CustomEventHandler(null, new MyCustomBuildEventArgs ());
+ Assert.AreEqual (expected, sw.ToString ().Replace ("\r\n", "\n"), "#1");
+ }
+ }
+
+ class MyCustomBuildEventArgs : CustomBuildEventArgs
+ {
+ }
+}
+
diff --git a/mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/LoggerDescriptionTest.cs b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/LoggerDescriptionTest.cs
new file mode 100644
index 0000000000..e062fc93b4
--- /dev/null
+++ b/mcs/class/Microsoft.Build/Test/Microsoft.Build.Logging/LoggerDescriptionTest.cs
@@ -0,0 +1,49 @@
+//
+// LoggerDescriptionTest.cs
+//
+// Author:
+// Atsushi Eno (atsushi@xamarin.com)
+//
+// Copyright (C) 2013 Xamarin Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System;
+using Microsoft.Build.Logging;
+using Microsoft.Build.Framework;
+using NUnit.Framework;
+
+namespace MonoTests.Microsoft.Build.Logging
+{
+ [TestFixture]
+ public class LoggerDescriptionTest
+ {
+ [Test]
+ public void CreateLogger ()
+ {
+ new LoggerDescription ("Microsoft.Build.Logging.ConsoleLogger",
+ typeof (ConsoleLogger).Assembly.FullName,
+ null,
+ null,
+ LoggerVerbosity.Normal)
+ .CreateLogger();
+ }
+ }
+}