2 // Project.cs: Project class
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Collections.Specialized;
35 using System.Reflection;
38 using System.Xml.Schema;
39 using Microsoft.Build.Framework;
40 using Mono.XBuild.Framework;
41 using Mono.XBuild.CommandLine;
43 namespace Microsoft.Build.BuildEngine {
44 public class Project {
47 Dictionary <string, List <string>> conditionedProperties;
48 string[] defaultTargets;
50 BuildItemGroup evaluatedItems;
51 BuildItemGroup evaluatedItemsIgnoringCondition;
52 Dictionary <string, BuildItemGroup> evaluatedItemsByName;
53 Dictionary <string, BuildItemGroup> evaluatedItemsByNameIgnoringCondition;
54 BuildPropertyGroup evaluatedProperties;
55 string firstTargetName;
57 BuildPropertyGroup globalProperties;
58 GroupingCollection groupingCollection;
61 BuildItemGroupCollection itemGroups;
62 ImportCollection imports;
63 List<string> initialTargets;
64 Dictionary <string, BuildItemGroup> last_item_group_containing;
65 bool needToReevaluate;
67 BuildPropertyGroupCollection propertyGroups;
69 TaskDatabase taskDatabase;
70 TargetCollection targets;
71 DateTime timeOfLastDirty;
72 UsingTaskCollection usingTasks;
73 XmlDocument xmlDocument;
75 bool initialTargetsBuilt;
77 BuildSettings current_settings;
79 ProjectLoadSettings project_load_settings;
82 static string extensions_path;
83 static XmlNamespaceManager manager;
84 static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
87 : this (Engine.GlobalEngine)
91 public Project (Engine engine) : this (engine, null)
95 public Project (Engine engine, string toolsVersion)
97 parentEngine = engine;
98 ToolsVersion = toolsVersion;
100 buildEnabled = ParentEngine.BuildEnabled;
101 xmlDocument = new XmlDocument ();
102 xmlDocument.PreserveWhitespace = false;
103 xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
104 xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
106 fullFileName = String.Empty;
107 timeOfLastDirty = DateTime.Now;
108 current_settings = BuildSettings.None;
109 project_load_settings = ProjectLoadSettings.None;
113 initialTargets = new List<string> ();
114 defaultTargets = new string [0];
115 batches = new Stack<Batch> ();
117 globalProperties = new BuildPropertyGroup (null, this, null, false);
118 foreach (BuildProperty bp in parentEngine.GlobalProperties)
119 GlobalProperties.AddProperty (bp.Clone (true));
125 [MonoTODO ("Not tested")]
126 public void AddNewImport (string importLocation,
127 string importCondition)
129 if (importLocation == null)
130 throw new ArgumentNullException ("importLocation");
132 XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
133 xmlDocument.DocumentElement.AppendChild (importElement);
134 importElement.SetAttribute ("Project", importLocation);
135 if (!String.IsNullOrEmpty (importCondition))
136 importElement.SetAttribute ("Condition", importCondition);
138 Import import = new Import (importElement, this, null);
139 imports.Add (import);
140 MarkProjectAsDirty ();
144 public BuildItem AddNewItem (string itemName,
147 return AddNewItem (itemName, itemInclude, false);
150 [MonoTODO ("Adds item not in the same place as MS")]
151 public BuildItem AddNewItem (string itemName,
153 bool treatItemIncludeAsLiteral)
157 if (itemGroups.Count == 0)
158 big = AddNewItemGroup ();
160 if (last_item_group_containing.ContainsKey (itemName)) {
161 big = last_item_group_containing [itemName];
164 BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
165 itemGroups.CopyTo (groups, 0);
170 BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
172 MarkProjectAsDirty ();
178 [MonoTODO ("Not tested")]
179 public BuildItemGroup AddNewItemGroup ()
181 XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
182 xmlDocument.DocumentElement.AppendChild (element);
184 BuildItemGroup big = new BuildItemGroup (element, this, null, false);
185 itemGroups.Add (big);
186 MarkProjectAsDirty ();
192 [MonoTODO ("Ignores insertAtEndOfProject")]
193 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
195 XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
196 xmlDocument.DocumentElement.AppendChild (element);
198 BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
199 propertyGroups.Add (bpg);
200 MarkProjectAsDirty ();
206 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
207 public void AddNewUsingTaskFromAssemblyFile (string taskName,
210 if (taskName == null)
211 throw new ArgumentNullException ("taskName");
212 if (assemblyFile == null)
213 throw new ArgumentNullException ("assemblyFile");
215 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
216 xmlDocument.DocumentElement.AppendChild (element);
217 element.SetAttribute ("TaskName", taskName);
218 element.SetAttribute ("AssemblyFile", assemblyFile);
220 UsingTask ut = new UsingTask (element, this, null);
222 MarkProjectAsDirty ();
225 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
226 public void AddNewUsingTaskFromAssemblyName (string taskName,
229 if (taskName == null)
230 throw new ArgumentNullException ("taskName");
231 if (assemblyName == null)
232 throw new ArgumentNullException ("assemblyName");
234 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
235 xmlDocument.DocumentElement.AppendChild (element);
236 element.SetAttribute ("TaskName", taskName);
237 element.SetAttribute ("AssemblyName", assemblyName);
239 UsingTask ut = new UsingTask (element, this, null);
241 MarkProjectAsDirty ();
244 [MonoTODO ("Not tested")]
247 return Build (new string [0]);
250 [MonoTODO ("Not tested")]
251 public bool Build (string targetName)
253 if (targetName == null)
254 return Build ((string[]) null);
256 return Build (new string [1] { targetName });
259 [MonoTODO ("Not tested")]
260 public bool Build (string [] targetNames)
262 return Build (targetNames, null);
265 [MonoTODO ("Not tested")]
266 public bool Build (string [] targetNames,
267 IDictionary targetOutputs)
269 return Build (targetNames, targetOutputs, BuildSettings.None);
272 [MonoTODO ("Not tested")]
273 public bool Build (string [] targetNames,
274 IDictionary targetOutputs,
275 BuildSettings buildFlags)
279 ParentEngine.StartProjectBuild (this, targetNames);
281 // Invoking this to emit a warning in case of unsupported
283 GetToolsVersionToUse (true);
285 string current_directory = Environment.CurrentDirectory;
287 current_settings = buildFlags;
288 if (!String.IsNullOrEmpty (fullFileName))
289 Directory.SetCurrentDirectory (Path.GetDirectoryName (fullFileName));
291 result = BuildInternal (targetNames, targetOutputs, buildFlags);
293 ParentEngine.EndProjectBuild (this, result);
294 current_settings = BuildSettings.None;
295 Directory.SetCurrentDirectory (current_directory);
302 bool BuildInternal (string [] targetNames,
303 IDictionary targetOutputs,
304 BuildSettings buildFlags)
307 if (buildFlags == BuildSettings.None) {
308 needToReevaluate = false;
312 if (targetNames == null || targetNames.Length == 0) {
313 if (defaultTargets != null && defaultTargets.Length != 0) {
314 targetNames = defaultTargets;
315 } else if (firstTargetName != null) {
316 targetNames = new string [1] { firstTargetName};
318 if (targets == null || targets.Count == 0) {
319 LogError (fullFileName, "No target found in the project");
327 if (!initialTargetsBuilt) {
328 foreach (string target in initialTargets) {
329 if (!BuildTarget (target.Trim (), targetOutputs))
332 initialTargetsBuilt = true;
335 foreach (string target in targetNames)
336 if (!BuildTarget (target.Trim (), targetOutputs))
342 bool BuildTarget (string target_name, IDictionary targetOutputs)
344 if (target_name == null)
345 throw new ArgumentException ("targetNames cannot contain null strings");
347 if (!targets.Exists (target_name)) {
348 LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
352 string key = GetKeyForTarget (target_name);
353 if (!targets [target_name].Build (key))
357 if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
358 if (targetOutputs != null)
359 targetOutputs.Add (target_name, outputs);
364 internal string GetKeyForTarget (string target_name)
366 return GetKeyForTarget (target_name, true);
369 internal string GetKeyForTarget (string target_name, bool include_global_properties)
371 // target name is case insensitive
372 return fullFileName + ":" + target_name.ToLower () +
373 (include_global_properties ? (":" + GlobalPropertiesToString (GlobalProperties))
377 string GlobalPropertiesToString (BuildPropertyGroup bgp)
379 StringBuilder sb = new StringBuilder ();
380 foreach (BuildProperty bp in bgp)
381 sb.AppendFormat (" {0}:{1}", bp.Name, bp.FinalValue);
382 return sb.ToString ();
386 public string [] GetConditionedPropertyValues (string propertyName)
388 if (conditionedProperties.ContainsKey (propertyName))
389 return conditionedProperties [propertyName].ToArray ();
391 return new string [0];
394 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
396 if (needToReevaluate) {
397 needToReevaluate = false;
401 if (evaluatedItemsByName.ContainsKey (itemName))
402 return evaluatedItemsByName [itemName];
404 return new BuildItemGroup (this);
407 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
409 if (needToReevaluate) {
410 needToReevaluate = false;
414 if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
415 return evaluatedItemsByNameIgnoringCondition [itemName];
417 return new BuildItemGroup (this);
420 public string GetEvaluatedProperty (string propertyName)
422 if (needToReevaluate) {
423 needToReevaluate = false;
427 if (propertyName == null)
428 throw new ArgumentNullException ("propertyName");
430 BuildProperty bp = evaluatedProperties [propertyName];
432 return bp == null ? null : (string) bp;
435 [MonoTODO ("We should remember that node and not use XPath to get it")]
436 public string GetProjectExtensions (string id)
438 if (id == null || id == String.Empty)
441 XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
446 return node.InnerXml;
450 public void Load (string projectFileName)
452 Load (projectFileName, ProjectLoadSettings.None);
455 public void Load (string projectFileName, ProjectLoadSettings settings)
457 project_load_settings = settings;
458 if (String.IsNullOrEmpty (projectFileName))
459 throw new ArgumentNullException ("projectFileName");
461 if (!File.Exists (projectFileName))
462 throw new ArgumentException (String.Format ("Project file {0} not found", projectFileName),
465 this.fullFileName = Utilities.FromMSBuildPath (Path.GetFullPath (projectFileName));
467 string filename = fullFileName;
468 if (String.Compare (Path.GetExtension (fullFileName), ".sln", true) == 0) {
469 Project tmp_project = ParentEngine.CreateNewProject ();
470 SolutionParser sln_parser = new SolutionParser ();
471 sln_parser.ParseSolution (fullFileName, tmp_project, delegate (int errorNumber, string message) {
472 LogWarning (filename, message);
474 filename = fullFileName + ".proj";
476 tmp_project.Save (filename);
477 ParentEngine.RemoveLoadedProject (tmp_project);
478 DoLoad (new StreamReader (filename));
480 if (Environment.GetEnvironmentVariable ("XBUILD_EMIT_SOLUTION") == null)
481 File.Delete (filename);
484 DoLoad (new StreamReader (filename));
488 [MonoTODO ("Not tested")]
489 public void Load (TextReader textReader)
491 Load (textReader, ProjectLoadSettings.None);
494 public void Load (TextReader textReader, ProjectLoadSettings projectLoadSettings)
496 project_load_settings = projectLoadSettings;
497 fullFileName = String.Empty;
501 public void LoadXml (string projectXml)
503 LoadXml (projectXml, ProjectLoadSettings.None);
506 public void LoadXml (string projectXml, ProjectLoadSettings projectLoadSettings)
508 project_load_settings = projectLoadSettings;
509 fullFileName = String.Empty;
510 DoLoad (new StringReader (projectXml));
511 MarkProjectAsDirty ();
515 public void MarkProjectAsDirty ()
518 timeOfLastDirty = DateTime.Now;
521 [MonoTODO ("Not tested")]
522 public void RemoveAllItemGroups ()
524 int length = ItemGroups.Count;
525 BuildItemGroup [] groups = new BuildItemGroup [length];
526 ItemGroups.CopyTo (groups, 0);
528 for (int i = 0; i < length; i++)
529 RemoveItemGroup (groups [i]);
531 MarkProjectAsDirty ();
535 [MonoTODO ("Not tested")]
536 public void RemoveAllPropertyGroups ()
538 int length = PropertyGroups.Count;
539 BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
540 PropertyGroups.CopyTo (groups, 0);
542 for (int i = 0; i < length; i++)
543 RemovePropertyGroup (groups [i]);
545 MarkProjectAsDirty ();
550 public void RemoveItem (BuildItem itemToRemove)
552 if (itemToRemove == null)
553 throw new ArgumentNullException ("itemToRemove");
555 if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
556 throw new InvalidOperationException ("The object passed in is not part of the project.");
558 BuildItemGroup big = itemToRemove.ParentItemGroup;
560 if (big.Count == 1) {
561 // ParentItemGroup for items from xml and that have parent is the same
562 groupingCollection.Remove (big);
564 if (big.ParentProject != this)
565 throw new InvalidOperationException ("The object passed in is not part of the project.");
567 if (itemToRemove.FromXml)
568 big.RemoveItem (itemToRemove);
570 big.RemoveItem (itemToRemove.ParentItem);
573 MarkProjectAsDirty ();
577 [MonoTODO ("Not tested")]
578 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
580 if (itemGroupToRemove == null)
581 throw new ArgumentNullException ("itemGroupToRemove");
583 groupingCollection.Remove (itemGroupToRemove);
584 MarkProjectAsDirty ();
588 // NOTE: does not modify imported projects
589 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
591 throw new NotImplementedException ();
595 public void RemoveItemsByName (string itemName)
597 if (itemName == null)
598 throw new ArgumentNullException ("itemName");
600 throw new NotImplementedException ();
603 [MonoTODO ("Not tested")]
604 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
606 if (propertyGroupToRemove == null)
607 throw new ArgumentNullException ("propertyGroupToRemove");
609 groupingCollection.Remove (propertyGroupToRemove);
610 MarkProjectAsDirty ();
614 // NOTE: does not modify imported projects
615 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
617 throw new NotImplementedException ();
621 public void ResetBuildStatus ()
623 // hack to allow built targets to be removed
629 public void Save (string projectFileName)
631 Save (projectFileName, Encoding.Default);
635 [MonoTODO ("Ignores encoding")]
636 public void Save (string projectFileName, Encoding encoding)
638 xmlDocument.Save (projectFileName);
642 public void Save (TextWriter outTextWriter)
644 xmlDocument.Save (outTextWriter);
648 public void SetImportedProperty (string propertyName,
649 string propertyValue,
651 Project importProject)
653 SetImportedProperty (propertyName, propertyValue, condition, importProject,
654 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
657 public void SetImportedProperty (string propertyName,
658 string propertyValue,
660 Project importedProject,
661 PropertyPosition position)
663 SetImportedProperty (propertyName, propertyValue, condition, importedProject,
664 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
668 public void SetImportedProperty (string propertyName,
669 string propertyValue,
671 Project importedProject,
672 PropertyPosition position,
673 bool treatPropertyValueAsLiteral)
675 throw new NotImplementedException ();
678 public void SetProjectExtensions (string id, string xmlText)
681 throw new ArgumentNullException ("id");
683 throw new ArgumentNullException ("xmlText");
685 XmlNode projectExtensions, node;
687 projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
689 if (projectExtensions == null) {
690 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
691 xmlDocument.DocumentElement.AppendChild (projectExtensions);
693 node = xmlDocument.CreateElement (id, XmlNamespace);
694 node.InnerXml = xmlText;
695 projectExtensions.AppendChild (node);
697 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
700 node = xmlDocument.CreateElement (id, XmlNamespace);
701 projectExtensions.AppendChild (node);
704 node.InnerXml = xmlText;
708 MarkProjectAsDirty ();
711 public void SetProperty (string propertyName,
712 string propertyValue)
714 SetProperty (propertyName, propertyValue, "true",
715 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
718 public void SetProperty (string propertyName,
719 string propertyValue,
722 SetProperty (propertyName, propertyValue, condition,
723 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
726 public void SetProperty (string propertyName,
727 string propertyValue,
729 PropertyPosition position)
731 SetProperty (propertyName, propertyValue, condition,
732 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
736 public void SetProperty (string propertyName,
737 string propertyValue,
739 PropertyPosition position,
740 bool treatPropertyValueAsLiteral)
742 throw new NotImplementedException ();
745 internal void Unload ()
750 internal void CheckUnloaded ()
753 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
756 internal void NeedToReevaluate ()
758 needToReevaluate = true;
761 // Does the actual loading.
762 void DoLoad (TextReader textReader)
765 ParentEngine.RemoveLoadedProject (this);
767 xmlDocument.Load (textReader);
769 if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
770 throw new InvalidProjectFileException (String.Format (
771 "Project file '{0}' is a VS2003 project, which is not " +
772 "supported by xbuild. You need to convert it to msbuild " +
773 "format to build with xbuild.", fullFileName));
775 if (SchemaFile != null) {
776 xmlDocument.Schemas.Add (XmlSchema.Read (
777 new StreamReader (SchemaFile), ValidationCallBack));
778 xmlDocument.Validate (ValidationCallBack);
781 if (xmlDocument.DocumentElement.Name != "Project") {
782 throw new InvalidProjectFileException (String.Format (
783 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
786 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
787 throw new InvalidProjectFileException (
788 @"The default XML namespace of the project must be the MSBuild XML namespace." +
789 " If the project is authored in the MSBuild 2003 format, please add " +
790 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
791 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
794 ParentEngine.AddLoadedProject (this);
795 } catch (Exception e) {
796 throw new InvalidProjectFileException (String.Format ("{0}: {1}",
797 fullFileName, e.Message), e);
799 if (textReader != null)
811 groupingCollection = new GroupingCollection (this);
812 imports = new ImportCollection (groupingCollection);
813 usingTasks = new UsingTaskCollection (this);
814 itemGroups = new BuildItemGroupCollection (groupingCollection);
815 propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
816 targets = new TargetCollection (this);
817 last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
819 string effective_tools_version = GetToolsVersionToUse (false);
820 taskDatabase = new TaskDatabase ();
821 taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
823 initialTargets = new List<string> ();
824 defaultTargets = new string [0];
825 PrepareForEvaluate (effective_tools_version);
826 ProcessElements (xmlDocument.DocumentElement, null);
832 void ProcessProjectAttributes (XmlAttributeCollection attributes)
834 foreach (XmlAttribute attr in attributes) {
836 case "InitialTargets":
837 initialTargets.AddRange (attr.Value.Split (
838 new char [] {';', ' '},
839 StringSplitOptions.RemoveEmptyEntries));
841 case "DefaultTargets":
842 // first non-empty DefaultTargets found is used
843 if (defaultTargets == null || defaultTargets.Length == 0)
844 defaultTargets = attr.Value.Split (new char [] {';', ' '},
845 StringSplitOptions.RemoveEmptyEntries);
846 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
847 DefaultTargets, PropertyType.Reserved));
853 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
855 ProcessProjectAttributes (rootElement.Attributes);
856 foreach (XmlNode xn in rootElement.ChildNodes) {
857 if (xn is XmlElement) {
858 XmlElement xe = (XmlElement) xn;
860 case "ProjectExtensions":
861 AddProjectExtensions (xe);
872 AddUsingTask (xe, ip);
878 AddItemGroup (xe, ip);
880 case "PropertyGroup":
881 AddPropertyGroup (xe, ip);
887 throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file.", xe.Name));
893 void PrepareForEvaluate (string effective_tools_version)
895 evaluatedItems = new BuildItemGroup (null, this, null, true);
896 evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
897 evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
898 evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
899 if (building && current_settings == BuildSettings.None)
900 RemoveBuiltTargets ();
902 InitializeProperties (effective_tools_version);
907 groupingCollection.Evaluate ();
909 //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
910 foreach (UsingTask usingTask in UsingTasks)
911 usingTask.Evaluate ();
914 // Removes entries of all earlier built targets for this project
915 void RemoveBuiltTargets ()
917 ParentEngine.ClearBuiltTargetsForProject (this);
920 void InitializeProperties (string effective_tools_version)
924 evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
926 foreach (BuildProperty gp in GlobalProperties) {
927 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
928 evaluatedProperties.AddProperty (bp);
931 foreach (BuildProperty gp in GlobalProperties)
932 ParentEngine.GlobalProperties.AddProperty (gp);
934 // add properties that we dont have from parent engine's
936 foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
937 if (evaluatedProperties [gp.Name] == null) {
938 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
939 evaluatedProperties.AddProperty (bp);
943 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
944 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
945 evaluatedProperties.AddProperty (bp);
948 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
949 PropertyType.Reserved));
950 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFullPath", fullFileName, PropertyType.Reserved));
951 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
952 Path.GetFileNameWithoutExtension (fullFileName),
953 PropertyType.Reserved));
954 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectExtension",
955 Path.GetExtension (fullFileName),
956 PropertyType.Reserved));
958 string toolsPath = parentEngine.Toolsets [effective_tools_version].ToolsPath;
959 if (toolsPath == null)
960 throw new Exception (String.Format ("Invalid tools version '{0}', no tools path set for this.", effective_tools_version));
961 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", toolsPath, PropertyType.Reserved));
962 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
963 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", effective_tools_version, PropertyType.Reserved));
964 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
965 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", ExtensionsPath, PropertyType.Reserved));
966 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath64", ExtensionsPath, PropertyType.Reserved));
967 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
968 evaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
970 // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
972 if (FullFileName == String.Empty)
973 projectDir = Environment.CurrentDirectory;
975 projectDir = Path.GetDirectoryName (FullFileName);
977 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
981 // ToolsVersion property
982 // ToolsVersion attribute on the project
983 // parentEngine's DefaultToolsVersion
984 string GetToolsVersionToUse (bool emitWarning)
986 if (!String.IsNullOrEmpty (ToolsVersion))
989 if (!HasToolsVersionAttribute)
990 return parentEngine.DefaultToolsVersion;
992 if (parentEngine.Toolsets [DefaultToolsVersion] == null) {
994 LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
995 DefaultToolsVersion, parentEngine.DefaultToolsVersion);
996 return parentEngine.DefaultToolsVersion;
999 return DefaultToolsVersion;
1002 void AddProjectExtensions (XmlElement xmlElement)
1006 void AddMessage (XmlElement xmlElement)
1010 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
1012 Target target = new Target (xmlElement, this, importedProject);
1013 targets.AddTarget (target);
1015 if (firstTargetName == null)
1016 firstTargetName = target.Name;
1019 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
1021 UsingTask usingTask;
1023 usingTask = new UsingTask (xmlElement, this, importedProject);
1024 UsingTasks.Add (usingTask);
1027 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
1029 // eval all the properties etc till the import
1030 groupingCollection.Evaluate (EvaluationType.Property);
1032 Import import = new Import (xmlElement, this, importingProject);
1033 if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
1036 if (Imports.Contains (import)) {
1037 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1038 "A circular reference was found involving the import of {0}. Only" +
1039 " the first import of this file will be used, ignoring others.",
1040 import.ProjectPath);
1045 if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
1046 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1047 "The main project file was imported here, which creates a circular " +
1048 "reference. Ignoring this import.");
1053 Imports.Add (import);
1054 import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
1057 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
1059 BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
1060 ItemGroups.Add (big);
1063 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
1065 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
1066 PropertyGroups.Add (bpg);
1069 void AddChoose (XmlElement xmlElement)
1071 BuildChoose bc = new BuildChoose (xmlElement, this);
1072 groupingCollection.Add (bc);
1075 static void ValidationCallBack (object sender, ValidationEventArgs e)
1077 Console.WriteLine ("Validation Error: {0}", e.Message);
1080 public bool BuildEnabled {
1082 return buildEnabled;
1085 buildEnabled = value;
1090 public Encoding Encoding {
1091 get { return encoding; }
1094 public string DefaultTargets {
1096 return String.Join ("; ", defaultTargets);
1099 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
1101 defaultTargets = value.Split (new char [] {';', ' '},
1102 StringSplitOptions.RemoveEmptyEntries);
1106 public BuildItemGroup EvaluatedItems {
1108 if (needToReevaluate) {
1109 needToReevaluate = false;
1112 return evaluatedItems;
1116 public BuildItemGroup EvaluatedItemsIgnoringCondition {
1118 if (needToReevaluate) {
1119 needToReevaluate = false;
1122 return evaluatedItemsIgnoringCondition;
1126 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
1128 // FIXME: do we need to do this here?
1129 if (needToReevaluate) {
1130 needToReevaluate = false;
1133 return evaluatedItemsByName;
1137 internal IEnumerable EvaluatedItemsByNameAsDictionaryEntries {
1139 if (EvaluatedItemsByName.Count == 0)
1142 foreach (KeyValuePair<string, BuildItemGroup> pair in EvaluatedItemsByName) {
1143 foreach (BuildItem bi in pair.Value)
1144 yield return new DictionaryEntry (pair.Key, bi.ConvertToITaskItem (null, ExpressionOptions.ExpandItemRefs));
1149 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
1151 // FIXME: do we need to do this here?
1152 if (needToReevaluate) {
1153 needToReevaluate = false;
1156 return evaluatedItemsByNameIgnoringCondition;
1160 // For batching implementation
1161 Dictionary<string, BuildItemGroup> perBatchItemsByName;
1162 Dictionary<string, BuildItemGroup> commonItemsByName;
1165 public Dictionary<string, BuildItemGroup> perBatchItemsByName;
1166 public Dictionary<string, BuildItemGroup> commonItemsByName;
1168 public Batch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1170 this.perBatchItemsByName = perBatchItemsByName;
1171 this.commonItemsByName = commonItemsByName;
1175 Stack<Batch> Batches {
1176 get { return batches; }
1179 internal void PushBatch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1181 batches.Push (new Batch (perBatchItemsByName, commonItemsByName));
1182 SetBatchedItems (perBatchItemsByName, commonItemsByName);
1185 internal void PopBatch ()
1188 if (batches.Count > 0) {
1189 Batch b = batches.Peek ();
1190 SetBatchedItems (b.perBatchItemsByName, b.commonItemsByName);
1192 SetBatchedItems (null, null);
1196 void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1198 this.perBatchItemsByName = perBatchItemsByName;
1199 this.commonItemsByName = commonItemsByName;
1203 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
1205 if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
1208 if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
1212 return EvaluatedItemsByName.TryGetValue (itemName, out group);
1215 internal string GetMetadataBatched (string itemName, string metadataName)
1217 BuildItemGroup group = null;
1218 if (itemName == null) {
1219 //unqualified, all items in a batch(bucket) have the
1220 //same metadata values
1221 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
1223 group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
1226 TryGetEvaluatedItemByNameBatched (itemName, out group);
1229 if (group != null) {
1230 foreach (BuildItem item in group) {
1231 if (item.HasMetadata (metadataName))
1232 return item.GetEvaluatedMetadata (metadataName);
1235 return String.Empty;
1238 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
1240 if (perBatchItemsByName == null && commonItemsByName == null)
1241 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
1244 if (perBatchItemsByName != null)
1245 foreach (BuildItemGroup group in perBatchItemsByName.Values)
1248 if (commonItemsByName != null)
1249 foreach (BuildItemGroup group in commonItemsByName.Values)
1253 T GetFirst<T> (ICollection<T> list)
1258 foreach (T t in list)
1264 internal void LogWarning (string filename, string message, params object[] messageArgs)
1266 BuildWarningEventArgs bwea = new BuildWarningEventArgs (
1267 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1269 ParentEngine.EventSource.FireWarningRaised (this, bwea);
1272 internal void LogError (string filename, string message,
1273 params object[] messageArgs)
1275 BuildErrorEventArgs beea = new BuildErrorEventArgs (
1276 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1278 ParentEngine.EventSource.FireErrorRaised (this, beea);
1281 static string ExtensionsPath {
1283 if (extensions_path == null) {
1284 // NOTE: code from mcs/tools/gacutil/driver.cs
1285 PropertyInfo gac = typeof (System.Environment).GetProperty (
1286 "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
1289 MethodInfo get_gac = gac.GetGetMethod (true);
1290 string gac_path = (string) get_gac.Invoke (null, null);
1291 extensions_path = Path.GetFullPath (Path.Combine (
1292 gac_path, Path.Combine ("..", "xbuild")));
1295 return extensions_path;
1299 public BuildPropertyGroup EvaluatedProperties {
1301 if (needToReevaluate) {
1302 needToReevaluate = false;
1305 return evaluatedProperties;
1309 internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
1311 foreach (BuildProperty bp in EvaluatedProperties)
1312 yield return new DictionaryEntry (bp.Name, bp.Value);
1316 public string FullFileName {
1317 get { return fullFileName; }
1318 set { fullFileName = value; }
1321 public BuildPropertyGroup GlobalProperties {
1322 get { return globalProperties; }
1325 throw new ArgumentNullException ("value");
1328 throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1330 globalProperties = value;
1334 public bool IsDirty {
1335 get { return isDirty; }
1338 public bool IsValidated {
1339 get { return isValidated; }
1340 set { isValidated = value; }
1343 public BuildItemGroupCollection ItemGroups {
1344 get { return itemGroups; }
1347 public ImportCollection Imports {
1348 get { return imports; }
1351 public string InitialTargets {
1353 return String.Join ("; ", initialTargets.ToArray ());
1356 initialTargets.Clear ();
1357 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1359 initialTargets.AddRange (value.Split (
1360 new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
1364 public Engine ParentEngine {
1365 get { return parentEngine; }
1368 public BuildPropertyGroupCollection PropertyGroups {
1369 get { return propertyGroups; }
1372 public string SchemaFile {
1373 get { return schemaFile; }
1374 set { schemaFile = value; }
1377 public TargetCollection Targets {
1378 get { return targets; }
1381 public DateTime TimeOfLastDirty {
1382 get { return timeOfLastDirty; }
1385 public UsingTaskCollection UsingTasks {
1386 get { return usingTasks; }
1391 get { return xmlDocument.InnerXml; }
1394 // corresponds to the xml attribute
1395 public string DefaultToolsVersion {
1397 if (xmlDocument != null)
1398 return xmlDocument.DocumentElement.GetAttribute ("ToolsVersion");
1402 if (xmlDocument != null)
1403 xmlDocument.DocumentElement.SetAttribute ("ToolsVersion", value);
1407 public bool HasToolsVersionAttribute {
1409 return xmlDocument != null && xmlDocument.DocumentElement.HasAttribute ("ToolsVersion");
1413 public string ToolsVersion {
1417 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1418 get { return last_item_group_containing; }
1421 internal static XmlNamespaceManager XmlNamespaceManager {
1423 if (manager == null) {
1424 manager = new XmlNamespaceManager (new NameTable ());
1425 manager.AddNamespace ("tns", ns);
1432 internal TaskDatabase TaskDatabase {
1433 get { return taskDatabase; }
1436 internal XmlDocument XmlDocument {
1437 get { return xmlDocument; }
1440 internal static string XmlNamespace {
1446 PlatformID pid = Environment.OSVersion.Platform;
1454 return "Windows_NT";