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;
76 List<string> builtTargetKeys;
78 BuildSettings current_settings;
80 static string extensions_path;
81 static XmlNamespaceManager manager;
82 static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
85 : this (Engine.GlobalEngine)
89 public Project (Engine engine)
91 parentEngine = engine;
93 buildEnabled = ParentEngine.BuildEnabled;
94 xmlDocument = new XmlDocument ();
95 xmlDocument.PreserveWhitespace = false;
96 xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
97 xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
99 fullFileName = String.Empty;
100 timeOfLastDirty = DateTime.Now;
101 current_settings = BuildSettings.None;
103 builtTargetKeys = new List<string> ();
104 initialTargets = new List<string> ();
105 defaultTargets = new string [0];
107 globalProperties = new BuildPropertyGroup (null, this, null, false);
108 foreach (BuildProperty bp in parentEngine.GlobalProperties)
109 GlobalProperties.AddProperty (bp.Clone (true));
114 [MonoTODO ("Not tested")]
115 public void AddNewImport (string importLocation,
116 string importCondition)
118 if (importLocation == null)
119 throw new ArgumentNullException ("importLocation");
121 XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
122 xmlDocument.DocumentElement.AppendChild (importElement);
123 importElement.SetAttribute ("Project", importLocation);
124 if (!String.IsNullOrEmpty (importCondition))
125 importElement.SetAttribute ("Condition", importCondition);
127 Import import = new Import (importElement, this, null);
128 imports.Add (import);
129 MarkProjectAsDirty ();
133 public BuildItem AddNewItem (string itemName,
136 return AddNewItem (itemName, itemInclude, false);
139 [MonoTODO ("Adds item not in the same place as MS")]
140 public BuildItem AddNewItem (string itemName,
142 bool treatItemIncludeAsLiteral)
146 if (itemGroups.Count == 0)
147 big = AddNewItemGroup ();
149 if (last_item_group_containing.ContainsKey (itemName)) {
150 big = last_item_group_containing [itemName];
153 BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
154 itemGroups.CopyTo (groups, 0);
159 BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
161 MarkProjectAsDirty ();
167 [MonoTODO ("Not tested")]
168 public BuildItemGroup AddNewItemGroup ()
170 XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
171 xmlDocument.DocumentElement.AppendChild (element);
173 BuildItemGroup big = new BuildItemGroup (element, this, null, false);
174 itemGroups.Add (big);
175 MarkProjectAsDirty ();
181 [MonoTODO ("Ignores insertAtEndOfProject")]
182 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
184 XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
185 xmlDocument.DocumentElement.AppendChild (element);
187 BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
188 propertyGroups.Add (bpg);
189 MarkProjectAsDirty ();
195 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
196 public void AddNewUsingTaskFromAssemblyFile (string taskName,
199 if (taskName == null)
200 throw new ArgumentNullException ("taskName");
201 if (assemblyFile == null)
202 throw new ArgumentNullException ("assemblyFile");
204 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
205 xmlDocument.DocumentElement.AppendChild (element);
206 element.SetAttribute ("TaskName", taskName);
207 element.SetAttribute ("AssemblyFile", assemblyFile);
209 UsingTask ut = new UsingTask (element, this, null);
211 MarkProjectAsDirty ();
214 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
215 public void AddNewUsingTaskFromAssemblyName (string taskName,
218 if (taskName == null)
219 throw new ArgumentNullException ("taskName");
220 if (assemblyName == null)
221 throw new ArgumentNullException ("assemblyName");
223 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
224 xmlDocument.DocumentElement.AppendChild (element);
225 element.SetAttribute ("TaskName", taskName);
226 element.SetAttribute ("AssemblyName", assemblyName);
228 UsingTask ut = new UsingTask (element, this, null);
230 MarkProjectAsDirty ();
233 [MonoTODO ("Not tested")]
236 return Build (new string [0]);
239 [MonoTODO ("Not tested")]
240 public bool Build (string targetName)
242 if (targetName == null)
243 return Build ((string[]) null);
245 return Build (new string [1] { targetName });
248 [MonoTODO ("Not tested")]
249 public bool Build (string [] targetNames)
251 return Build (targetNames, null);
254 [MonoTODO ("Not tested")]
255 public bool Build (string [] targetNames,
256 IDictionary targetOutputs)
258 return Build (targetNames, targetOutputs, BuildSettings.None);
261 [MonoTODO ("Not tested")]
262 public bool Build (string [] targetNames,
263 IDictionary targetOutputs,
264 BuildSettings buildFlags)
268 ParentEngine.StartProjectBuild (this, targetNames);
269 string current_directory = Environment.CurrentDirectory;
271 current_settings = buildFlags;
272 if (!String.IsNullOrEmpty (fullFileName))
273 Directory.SetCurrentDirectory (Path.GetDirectoryName (fullFileName));
275 result = BuildInternal (targetNames, targetOutputs, buildFlags);
277 ParentEngine.EndProjectBuild (this, result);
278 current_settings = BuildSettings.None;
279 Directory.SetCurrentDirectory (current_directory);
286 bool BuildInternal (string [] targetNames,
287 IDictionary targetOutputs,
288 BuildSettings buildFlags)
291 if (buildFlags == BuildSettings.None)
294 if (targetNames == null || targetNames.Length == 0) {
295 if (defaultTargets != null && defaultTargets.Length != 0)
296 targetNames = defaultTargets;
297 else if (firstTargetName != null)
298 targetNames = new string [1] { firstTargetName};
303 if (!initialTargetsBuilt) {
304 foreach (string target in initialTargets) {
305 if (!BuildTarget (target.Trim (), targetOutputs))
308 initialTargetsBuilt = true;
311 foreach (string target in targetNames)
312 if (!BuildTarget (target.Trim (), targetOutputs))
318 bool BuildTarget (string target_name, IDictionary targetOutputs)
320 if (target_name == null)
321 throw new ArgumentException ("targetNames cannot contain null strings");
323 if (!targets.Exists (target_name)) {
324 LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
328 string key = GetKeyForTarget (target_name);
329 if (!targets [target_name].Build (key))
333 if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
334 if (targetOutputs != null)
335 targetOutputs.Add (target_name, outputs);
340 internal string GetKeyForTarget (string target_name)
342 // target name is case insensitive
343 return fullFileName + ":" + target_name.ToLower () + ":" + GlobalPropertiesToString (GlobalProperties);
346 string GlobalPropertiesToString (BuildPropertyGroup bgp)
348 StringBuilder sb = new StringBuilder ();
349 foreach (BuildProperty bp in bgp)
350 sb.AppendFormat (" {0}:{1}", bp.Name, bp.FinalValue);
351 return sb.ToString ();
355 public string [] GetConditionedPropertyValues (string propertyName)
357 if (conditionedProperties.ContainsKey (propertyName))
358 return conditionedProperties [propertyName].ToArray ();
360 return new string [0];
363 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
365 if (needToReevaluate) {
366 needToReevaluate = false;
370 if (evaluatedItemsByName.ContainsKey (itemName))
371 return evaluatedItemsByName [itemName];
373 return new BuildItemGroup (this);
376 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
378 if (needToReevaluate) {
379 needToReevaluate = false;
383 if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
384 return evaluatedItemsByNameIgnoringCondition [itemName];
386 return new BuildItemGroup (this);
389 public string GetEvaluatedProperty (string propertyName)
391 if (needToReevaluate) {
392 needToReevaluate = false;
396 if (propertyName == null)
397 throw new ArgumentNullException ("propertyName");
399 BuildProperty bp = evaluatedProperties [propertyName];
401 return bp == null ? null : (string) bp;
404 [MonoTODO ("We should remember that node and not use XPath to get it")]
405 public string GetProjectExtensions (string id)
407 if (id == null || id == String.Empty)
410 XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
415 return node.InnerXml;
419 public void Load (string projectFileName)
421 this.fullFileName = Utilities.FromMSBuildPath (Path.GetFullPath (projectFileName));
423 string filename = fullFileName;
424 if (String.Compare (Path.GetExtension (fullFileName), ".sln", true) == 0) {
425 Project tmp_project = ParentEngine.CreateNewProject ();
426 SolutionParser sln_parser = new SolutionParser ();
427 sln_parser.ParseSolution (fullFileName, tmp_project, delegate (int errorNumber, string message) {
428 LogWarning (filename, message);
430 filename = fullFileName + ".proj";
432 tmp_project.Save (filename);
433 ParentEngine.RemoveLoadedProject (tmp_project);
434 DoLoad (new StreamReader (filename));
436 if (Environment.GetEnvironmentVariable ("XBUILD_EMIT_SOLUTION") == null)
437 File.Delete (filename);
440 DoLoad (new StreamReader (filename));
444 [MonoTODO ("Not tested")]
445 public void Load (TextReader textReader)
447 fullFileName = String.Empty;
451 public void LoadXml (string projectXml)
453 fullFileName = String.Empty;
454 DoLoad (new StringReader (projectXml));
455 MarkProjectAsDirty ();
459 public void MarkProjectAsDirty ()
462 timeOfLastDirty = DateTime.Now;
465 [MonoTODO ("Not tested")]
466 public void RemoveAllItemGroups ()
468 int length = ItemGroups.Count;
469 BuildItemGroup [] groups = new BuildItemGroup [length];
470 ItemGroups.CopyTo (groups, 0);
472 for (int i = 0; i < length; i++)
473 RemoveItemGroup (groups [i]);
475 MarkProjectAsDirty ();
479 [MonoTODO ("Not tested")]
480 public void RemoveAllPropertyGroups ()
482 int length = PropertyGroups.Count;
483 BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
484 PropertyGroups.CopyTo (groups, 0);
486 for (int i = 0; i < length; i++)
487 RemovePropertyGroup (groups [i]);
489 MarkProjectAsDirty ();
494 public void RemoveItem (BuildItem itemToRemove)
496 if (itemToRemove == null)
497 throw new ArgumentNullException ("itemToRemove");
499 if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
500 throw new InvalidOperationException ("The object passed in is not part of the project.");
502 BuildItemGroup big = itemToRemove.ParentItemGroup;
504 if (big.Count == 1) {
505 // ParentItemGroup for items from xml and that have parent is the same
506 groupingCollection.Remove (big);
508 if (big.ParentProject != this)
509 throw new InvalidOperationException ("The object passed in is not part of the project.");
511 if (itemToRemove.FromXml)
512 big.RemoveItem (itemToRemove);
514 big.RemoveItem (itemToRemove.ParentItem);
517 MarkProjectAsDirty ();
521 [MonoTODO ("Not tested")]
522 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
524 if (itemGroupToRemove == null)
525 throw new ArgumentNullException ("itemGroupToRemove");
527 groupingCollection.Remove (itemGroupToRemove);
528 MarkProjectAsDirty ();
532 // NOTE: does not modify imported projects
533 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
535 throw new NotImplementedException ();
539 public void RemoveItemsByName (string itemName)
541 if (itemName == null)
542 throw new ArgumentNullException ("itemName");
544 throw new NotImplementedException ();
547 [MonoTODO ("Not tested")]
548 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
550 if (propertyGroupToRemove == null)
551 throw new ArgumentNullException ("propertyGroupToRemove");
553 groupingCollection.Remove (propertyGroupToRemove);
554 MarkProjectAsDirty ();
558 // NOTE: does not modify imported projects
559 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
561 throw new NotImplementedException ();
565 public void ResetBuildStatus ()
567 // hack to allow built targets to be removed
573 public void Save (string projectFileName)
575 Save (projectFileName, Encoding.Default);
579 [MonoTODO ("Ignores encoding")]
580 public void Save (string projectFileName, Encoding encoding)
582 xmlDocument.Save (projectFileName);
586 public void Save (TextWriter outTextWriter)
588 xmlDocument.Save (outTextWriter);
592 public void SetImportedProperty (string propertyName,
593 string propertyValue,
595 Project importProject)
597 SetImportedProperty (propertyName, propertyValue, condition, importProject,
598 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
601 public void SetImportedProperty (string propertyName,
602 string propertyValue,
604 Project importedProject,
605 PropertyPosition position)
607 SetImportedProperty (propertyName, propertyValue, condition, importedProject,
608 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
612 public void SetImportedProperty (string propertyName,
613 string propertyValue,
615 Project importedProject,
616 PropertyPosition position,
617 bool treatPropertyValueAsLiteral)
619 throw new NotImplementedException ();
622 public void SetProjectExtensions (string id, string xmlText)
625 throw new ArgumentNullException ("id");
627 throw new ArgumentNullException ("xmlText");
629 XmlNode projectExtensions, node;
631 projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
633 if (projectExtensions == null) {
634 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
635 xmlDocument.DocumentElement.AppendChild (projectExtensions);
637 node = xmlDocument.CreateElement (id, XmlNamespace);
638 node.InnerXml = xmlText;
639 projectExtensions.AppendChild (node);
641 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
644 node = xmlDocument.CreateElement (id, XmlNamespace);
645 projectExtensions.AppendChild (node);
648 node.InnerXml = xmlText;
652 MarkProjectAsDirty ();
655 public void SetProperty (string propertyName,
656 string propertyValue)
658 SetProperty (propertyName, propertyValue, "true",
659 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
662 public void SetProperty (string propertyName,
663 string propertyValue,
666 SetProperty (propertyName, propertyValue, condition,
667 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
670 public void SetProperty (string propertyName,
671 string propertyValue,
673 PropertyPosition position)
675 SetProperty (propertyName, propertyValue, condition,
676 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
680 public void SetProperty (string propertyName,
681 string propertyValue,
683 PropertyPosition position,
684 bool treatPropertyValueAsLiteral)
686 throw new NotImplementedException ();
689 internal void Unload ()
694 internal void CheckUnloaded ()
697 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
700 internal void NeedToReevaluate ()
702 needToReevaluate = true;
705 // Does the actual loading.
706 void DoLoad (TextReader textReader)
709 ParentEngine.RemoveLoadedProject (this);
711 xmlDocument.Load (textReader);
713 if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
714 throw new InvalidProjectFileException (String.Format (
715 "Project file '{0}' is a VS2003 project, which is not " +
716 "supported by xbuild. You need to convert it to msbuild " +
717 "format to build with xbuild.", fullFileName));
719 if (SchemaFile != null) {
720 xmlDocument.Schemas.Add (XmlSchema.Read (
721 new StreamReader (SchemaFile), ValidationCallBack));
722 xmlDocument.Validate (ValidationCallBack);
725 if (xmlDocument.DocumentElement.Name != "Project") {
726 throw new InvalidProjectFileException (String.Format (
727 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
730 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
731 throw new InvalidProjectFileException (
732 @"The default XML namespace of the project must be the MSBuild XML namespace." +
733 " If the project is authored in the MSBuild 2003 format, please add " +
734 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
735 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
738 ParentEngine.AddLoadedProject (this);
739 } catch (Exception e) {
740 throw new InvalidProjectFileException (String.Format ("{0}: {1}",
741 fullFileName, e.Message), e);
743 if (textReader != null)
755 groupingCollection = new GroupingCollection (this);
756 imports = new ImportCollection (groupingCollection);
757 usingTasks = new UsingTaskCollection (this);
758 itemGroups = new BuildItemGroupCollection (groupingCollection);
759 propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
760 targets = new TargetCollection (this);
761 last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
763 taskDatabase = new TaskDatabase ();
764 if (ParentEngine.DefaultTasksRegistered)
765 taskDatabase.CopyTasks (ParentEngine.DefaultTasks);
767 initialTargets = new List<string> ();
768 defaultTargets = new string [0];
769 PrepareForEvaluate ();
770 ProcessElements (xmlDocument.DocumentElement, null);
776 void ProcessProjectAttributes (XmlAttributeCollection attributes)
778 foreach (XmlAttribute attr in attributes) {
780 case "InitialTargets":
781 initialTargets.AddRange (attr.Value.Split (
782 new char [] {';', ' '},
783 StringSplitOptions.RemoveEmptyEntries));
785 case "DefaultTargets":
786 // first non-empty DefaultTargets found is used
787 if (defaultTargets == null || defaultTargets.Length == 0)
788 defaultTargets = attr.Value.Split (new char [] {';', ' '},
789 StringSplitOptions.RemoveEmptyEntries);
790 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
791 DefaultTargets, PropertyType.Reserved));
797 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
799 ProcessProjectAttributes (rootElement.Attributes);
800 foreach (XmlNode xn in rootElement.ChildNodes) {
801 if (xn is XmlElement) {
802 XmlElement xe = (XmlElement) xn;
804 case "ProjectExtensions":
805 AddProjectExtensions (xe);
816 AddUsingTask (xe, ip);
822 AddItemGroup (xe, ip);
824 case "PropertyGroup":
825 AddPropertyGroup (xe, ip);
831 throw new InvalidProjectFileException ("Invalid element in project file.");
837 void PrepareForEvaluate ()
839 evaluatedItems = new BuildItemGroup (null, this, null, true);
840 evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
841 evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
842 evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
843 if (building && current_settings == BuildSettings.None)
844 RemoveBuiltTargets ();
846 InitializeProperties ();
851 groupingCollection.Evaluate ();
853 //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
854 foreach (UsingTask usingTask in UsingTasks)
855 usingTask.Evaluate ();
858 // Removes entries of all earlier built targets for this project
859 void RemoveBuiltTargets ()
861 foreach (string key in builtTargetKeys)
862 ParentEngine.BuiltTargetsOutputByName.Remove (key);
865 void InitializeProperties ()
869 evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
871 foreach (BuildProperty gp in GlobalProperties) {
872 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
873 EvaluatedProperties.AddProperty (bp);
876 foreach (BuildProperty gp in GlobalProperties)
877 ParentEngine.GlobalProperties.AddProperty (gp);
879 // add properties that we dont have from parent engine's
881 foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
882 if (EvaluatedProperties [gp.Name] == null) {
883 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
884 EvaluatedProperties.AddProperty (bp);
888 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
889 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
890 EvaluatedProperties.AddProperty (bp);
893 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
894 PropertyType.Reserved));
895 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
896 Path.GetFileNameWithoutExtension (fullFileName),
897 PropertyType.Reserved));
898 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved));
899 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", parentEngine.BinPath, PropertyType.Reserved));
900 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
901 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
902 EvaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
904 // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
906 if (FullFileName == String.Empty)
907 projectDir = Environment.CurrentDirectory;
909 projectDir = Path.GetDirectoryName (FullFileName);
911 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
914 void AddProjectExtensions (XmlElement xmlElement)
918 void AddMessage (XmlElement xmlElement)
922 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
924 Target target = new Target (xmlElement, this, importedProject);
925 targets.AddTarget (target);
927 if (firstTargetName == null)
928 firstTargetName = target.Name;
931 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
935 usingTask = new UsingTask (xmlElement, this, importedProject);
936 UsingTasks.Add (usingTask);
939 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
941 // eval all the properties etc till the import
942 groupingCollection.Evaluate (EvaluationType.Property);
944 Import import = new Import (xmlElement, this, importingProject);
945 if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
948 if (Imports.Contains (import)) {
949 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
950 "A circular reference was found involving the import of {0}. Only" +
951 " the first import of this file will be used, ignoring others.",
957 if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
958 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
959 "The main project file was imported here, which creates a circular " +
960 "reference. Ignoring this import.");
965 Imports.Add (import);
969 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
971 BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
972 ItemGroups.Add (big);
975 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
977 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
978 PropertyGroups.Add (bpg);
981 void AddChoose (XmlElement xmlElement)
983 BuildChoose bc = new BuildChoose (xmlElement, this);
984 groupingCollection.Add (bc);
987 static void ValidationCallBack (object sender, ValidationEventArgs e)
989 Console.WriteLine ("Validation Error: {0}", e.Message);
992 public bool BuildEnabled {
997 buildEnabled = value;
1002 public Encoding Encoding {
1003 get { return encoding; }
1006 public string DefaultTargets {
1008 return String.Join ("; ", defaultTargets);
1011 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
1013 defaultTargets = value.Split (new char [] {';', ' '},
1014 StringSplitOptions.RemoveEmptyEntries);
1018 public BuildItemGroup EvaluatedItems {
1020 if (needToReevaluate) {
1021 needToReevaluate = false;
1024 return evaluatedItems;
1028 public BuildItemGroup EvaluatedItemsIgnoringCondition {
1030 if (needToReevaluate) {
1031 needToReevaluate = false;
1034 return evaluatedItemsIgnoringCondition;
1038 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
1040 // FIXME: do we need to do this here?
1041 if (needToReevaluate) {
1042 needToReevaluate = false;
1045 return evaluatedItemsByName;
1049 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
1051 // FIXME: do we need to do this here?
1052 if (needToReevaluate) {
1053 needToReevaluate = false;
1056 return evaluatedItemsByNameIgnoringCondition;
1060 // For batching implementation
1061 Dictionary<string, BuildItemGroup> perBatchItemsByName;
1062 Dictionary<string, BuildItemGroup> commonItemsByName;
1064 internal void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1066 this.perBatchItemsByName = perBatchItemsByName;
1067 this.commonItemsByName = commonItemsByName;
1071 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
1073 if (perBatchItemsByName == null && commonItemsByName == null)
1074 return EvaluatedItemsByName.TryGetValue (itemName, out group);
1076 if (perBatchItemsByName != null)
1077 return perBatchItemsByName.TryGetValue (itemName, out group);
1079 if (commonItemsByName != null)
1080 return commonItemsByName.TryGetValue (itemName, out group);
1086 internal string GetMetadataBatched (string itemName, string metadataName)
1088 BuildItemGroup group = null;
1089 if (itemName == null) {
1090 //unqualified, all items in a batch(bucket) have the
1091 //same metadata values
1092 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
1094 group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
1097 TryGetEvaluatedItemByNameBatched (itemName, out group);
1100 if (group != null) {
1101 foreach (BuildItem item in group) {
1102 if (item.HasMetadata (metadataName))
1103 return item.GetEvaluatedMetadata (metadataName);
1106 return String.Empty;
1109 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
1111 if (perBatchItemsByName == null && commonItemsByName == null)
1112 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
1115 if (perBatchItemsByName != null)
1116 foreach (BuildItemGroup group in perBatchItemsByName.Values)
1119 if (commonItemsByName != null)
1120 foreach (BuildItemGroup group in commonItemsByName.Values)
1124 T GetFirst<T> (ICollection<T> list)
1129 foreach (T t in list)
1135 void LogWarning (string filename, string message, params object[] messageArgs)
1137 BuildWarningEventArgs bwea = new BuildWarningEventArgs (
1138 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1140 ParentEngine.EventSource.FireWarningRaised (this, bwea);
1143 void LogError (string filename, string message,
1144 params object[] messageArgs)
1146 BuildErrorEventArgs beea = new BuildErrorEventArgs (
1147 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1149 ParentEngine.EventSource.FireErrorRaised (this, beea);
1152 static string ExtensionsPath {
1154 if (extensions_path == null) {
1155 // NOTE: code from mcs/tools/gacutil/driver.cs
1156 PropertyInfo gac = typeof (System.Environment).GetProperty (
1157 "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
1160 MethodInfo get_gac = gac.GetGetMethod (true);
1161 string gac_path = (string) get_gac.Invoke (null, null);
1162 extensions_path = Path.GetFullPath (Path.Combine (
1163 gac_path, Path.Combine ("..", "xbuild")));
1166 return extensions_path;
1170 public BuildPropertyGroup EvaluatedProperties {
1172 if (needToReevaluate) {
1173 needToReevaluate = false;
1176 return evaluatedProperties;
1180 public string FullFileName {
1181 get { return fullFileName; }
1182 set { fullFileName = value; }
1185 public BuildPropertyGroup GlobalProperties {
1186 get { return globalProperties; }
1189 throw new ArgumentNullException ("value");
1192 throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1194 globalProperties = value;
1195 NeedToReevaluate ();
1199 public bool IsDirty {
1200 get { return isDirty; }
1203 public bool IsValidated {
1204 get { return isValidated; }
1205 set { isValidated = value; }
1208 public BuildItemGroupCollection ItemGroups {
1209 get { return itemGroups; }
1212 public ImportCollection Imports {
1213 get { return imports; }
1216 public string InitialTargets {
1218 return String.Join ("; ", initialTargets.ToArray ());
1221 initialTargets.Clear ();
1222 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1224 initialTargets.AddRange (value.Split (
1225 new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
1229 public Engine ParentEngine {
1230 get { return parentEngine; }
1233 public BuildPropertyGroupCollection PropertyGroups {
1234 get { return propertyGroups; }
1237 public string SchemaFile {
1238 get { return schemaFile; }
1239 set { schemaFile = value; }
1242 public TargetCollection Targets {
1243 get { return targets; }
1246 public DateTime TimeOfLastDirty {
1247 get { return timeOfLastDirty; }
1250 public UsingTaskCollection UsingTasks {
1251 get { return usingTasks; }
1256 get { return xmlDocument.InnerXml; }
1259 internal List<string> BuiltTargetKeys {
1260 get { return builtTargetKeys; }
1263 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1264 get { return last_item_group_containing; }
1267 internal static XmlNamespaceManager XmlNamespaceManager {
1269 if (manager == null) {
1270 manager = new XmlNamespaceManager (new NameTable ());
1271 manager.AddNamespace ("tns", ns);
1278 internal TaskDatabase TaskDatabase {
1279 get { return taskDatabase; }
1282 internal XmlDocument XmlDocument {
1283 get { return xmlDocument; }
1286 internal static string XmlNamespace {
1292 PlatformID pid = Environment.OSVersion.Platform;
1300 return "Windows_NT";