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 ProjectLoadSettings project_load_settings;
83 static string extensions_path;
84 static XmlNamespaceManager manager;
85 static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
88 : this (Engine.GlobalEngine)
92 public Project (Engine engine) : this (engine, null)
96 public Project (Engine engine, string toolsVersion)
98 parentEngine = engine;
99 ToolsVersion = toolsVersion;
101 buildEnabled = ParentEngine.BuildEnabled;
102 xmlDocument = new XmlDocument ();
103 xmlDocument.PreserveWhitespace = false;
104 xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
105 xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
107 fullFileName = String.Empty;
108 timeOfLastDirty = DateTime.Now;
109 current_settings = BuildSettings.None;
110 project_load_settings = ProjectLoadSettings.None;
114 builtTargetKeys = new List<string> ();
115 initialTargets = new List<string> ();
116 defaultTargets = new string [0];
117 batches = new Stack<Batch> ();
119 globalProperties = new BuildPropertyGroup (null, this, null, false);
120 foreach (BuildProperty bp in parentEngine.GlobalProperties)
121 GlobalProperties.AddProperty (bp.Clone (true));
127 [MonoTODO ("Not tested")]
128 public void AddNewImport (string importLocation,
129 string importCondition)
131 if (importLocation == null)
132 throw new ArgumentNullException ("importLocation");
134 XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
135 xmlDocument.DocumentElement.AppendChild (importElement);
136 importElement.SetAttribute ("Project", importLocation);
137 if (!String.IsNullOrEmpty (importCondition))
138 importElement.SetAttribute ("Condition", importCondition);
140 Import import = new Import (importElement, this, null);
141 imports.Add (import);
142 MarkProjectAsDirty ();
146 public BuildItem AddNewItem (string itemName,
149 return AddNewItem (itemName, itemInclude, false);
152 [MonoTODO ("Adds item not in the same place as MS")]
153 public BuildItem AddNewItem (string itemName,
155 bool treatItemIncludeAsLiteral)
159 if (itemGroups.Count == 0)
160 big = AddNewItemGroup ();
162 if (last_item_group_containing.ContainsKey (itemName)) {
163 big = last_item_group_containing [itemName];
166 BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
167 itemGroups.CopyTo (groups, 0);
172 BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
174 MarkProjectAsDirty ();
180 [MonoTODO ("Not tested")]
181 public BuildItemGroup AddNewItemGroup ()
183 XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
184 xmlDocument.DocumentElement.AppendChild (element);
186 BuildItemGroup big = new BuildItemGroup (element, this, null, false);
187 itemGroups.Add (big);
188 MarkProjectAsDirty ();
194 [MonoTODO ("Ignores insertAtEndOfProject")]
195 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
197 XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
198 xmlDocument.DocumentElement.AppendChild (element);
200 BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
201 propertyGroups.Add (bpg);
202 MarkProjectAsDirty ();
208 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
209 public void AddNewUsingTaskFromAssemblyFile (string taskName,
212 if (taskName == null)
213 throw new ArgumentNullException ("taskName");
214 if (assemblyFile == null)
215 throw new ArgumentNullException ("assemblyFile");
217 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
218 xmlDocument.DocumentElement.AppendChild (element);
219 element.SetAttribute ("TaskName", taskName);
220 element.SetAttribute ("AssemblyFile", assemblyFile);
222 UsingTask ut = new UsingTask (element, this, null);
224 MarkProjectAsDirty ();
227 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
228 public void AddNewUsingTaskFromAssemblyName (string taskName,
231 if (taskName == null)
232 throw new ArgumentNullException ("taskName");
233 if (assemblyName == null)
234 throw new ArgumentNullException ("assemblyName");
236 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
237 xmlDocument.DocumentElement.AppendChild (element);
238 element.SetAttribute ("TaskName", taskName);
239 element.SetAttribute ("AssemblyName", assemblyName);
241 UsingTask ut = new UsingTask (element, this, null);
243 MarkProjectAsDirty ();
246 [MonoTODO ("Not tested")]
249 return Build (new string [0]);
252 [MonoTODO ("Not tested")]
253 public bool Build (string targetName)
255 if (targetName == null)
256 return Build ((string[]) null);
258 return Build (new string [1] { targetName });
261 [MonoTODO ("Not tested")]
262 public bool Build (string [] targetNames)
264 return Build (targetNames, null);
267 [MonoTODO ("Not tested")]
268 public bool Build (string [] targetNames,
269 IDictionary targetOutputs)
271 return Build (targetNames, targetOutputs, BuildSettings.None);
274 [MonoTODO ("Not tested")]
275 public bool Build (string [] targetNames,
276 IDictionary targetOutputs,
277 BuildSettings buildFlags)
281 ParentEngine.StartProjectBuild (this, targetNames);
282 string current_directory = Environment.CurrentDirectory;
284 current_settings = buildFlags;
285 if (!String.IsNullOrEmpty (fullFileName))
286 Directory.SetCurrentDirectory (Path.GetDirectoryName (fullFileName));
288 result = BuildInternal (targetNames, targetOutputs, buildFlags);
290 ParentEngine.EndProjectBuild (this, result);
291 current_settings = BuildSettings.None;
292 Directory.SetCurrentDirectory (current_directory);
299 bool BuildInternal (string [] targetNames,
300 IDictionary targetOutputs,
301 BuildSettings buildFlags)
304 if (buildFlags == BuildSettings.None)
307 if (targetNames == null || targetNames.Length == 0) {
308 if (defaultTargets != null && defaultTargets.Length != 0)
309 targetNames = defaultTargets;
310 else if (firstTargetName != null)
311 targetNames = new string [1] { firstTargetName};
316 if (!initialTargetsBuilt) {
317 foreach (string target in initialTargets) {
318 if (!BuildTarget (target.Trim (), targetOutputs))
321 initialTargetsBuilt = true;
324 foreach (string target in targetNames)
325 if (!BuildTarget (target.Trim (), targetOutputs))
331 bool BuildTarget (string target_name, IDictionary targetOutputs)
333 if (target_name == null)
334 throw new ArgumentException ("targetNames cannot contain null strings");
336 if (!targets.Exists (target_name)) {
337 LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
341 string key = GetKeyForTarget (target_name);
342 if (!targets [target_name].Build (key))
346 if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
347 if (targetOutputs != null)
348 targetOutputs.Add (target_name, outputs);
353 internal string GetKeyForTarget (string target_name)
355 // target name is case insensitive
356 return fullFileName + ":" + target_name.ToLower () + ":" + GlobalPropertiesToString (GlobalProperties);
359 string GlobalPropertiesToString (BuildPropertyGroup bgp)
361 StringBuilder sb = new StringBuilder ();
362 foreach (BuildProperty bp in bgp)
363 sb.AppendFormat (" {0}:{1}", bp.Name, bp.FinalValue);
364 return sb.ToString ();
368 public string [] GetConditionedPropertyValues (string propertyName)
370 if (conditionedProperties.ContainsKey (propertyName))
371 return conditionedProperties [propertyName].ToArray ();
373 return new string [0];
376 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
378 if (needToReevaluate) {
379 needToReevaluate = false;
383 if (evaluatedItemsByName.ContainsKey (itemName))
384 return evaluatedItemsByName [itemName];
386 return new BuildItemGroup (this);
389 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
391 if (needToReevaluate) {
392 needToReevaluate = false;
396 if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
397 return evaluatedItemsByNameIgnoringCondition [itemName];
399 return new BuildItemGroup (this);
402 public string GetEvaluatedProperty (string propertyName)
404 if (needToReevaluate) {
405 needToReevaluate = false;
409 if (propertyName == null)
410 throw new ArgumentNullException ("propertyName");
412 BuildProperty bp = evaluatedProperties [propertyName];
414 return bp == null ? null : (string) bp;
417 [MonoTODO ("We should remember that node and not use XPath to get it")]
418 public string GetProjectExtensions (string id)
420 if (id == null || id == String.Empty)
423 XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
428 return node.InnerXml;
432 public void Load (string projectFileName)
434 Load (projectFileName, ProjectLoadSettings.None);
437 public void Load (string projectFileName, ProjectLoadSettings settings)
439 project_load_settings = settings;
440 if (String.IsNullOrEmpty (projectFileName))
441 throw new ArgumentNullException ("projectFileName");
443 if (!File.Exists (projectFileName))
444 throw new ArgumentException (String.Format ("Project file {0} not found", projectFileName),
447 this.fullFileName = Utilities.FromMSBuildPath (Path.GetFullPath (projectFileName));
449 string filename = fullFileName;
450 if (String.Compare (Path.GetExtension (fullFileName), ".sln", true) == 0) {
451 Project tmp_project = ParentEngine.CreateNewProject ();
452 SolutionParser sln_parser = new SolutionParser ();
453 sln_parser.ParseSolution (fullFileName, tmp_project, delegate (int errorNumber, string message) {
454 LogWarning (filename, message);
456 filename = fullFileName + ".proj";
458 tmp_project.Save (filename);
459 ParentEngine.RemoveLoadedProject (tmp_project);
460 DoLoad (new StreamReader (filename));
462 if (Environment.GetEnvironmentVariable ("XBUILD_EMIT_SOLUTION") == null)
463 File.Delete (filename);
466 DoLoad (new StreamReader (filename));
470 [MonoTODO ("Not tested")]
471 public void Load (TextReader textReader)
473 Load (textReader, ProjectLoadSettings.None);
476 public void Load (TextReader textReader, ProjectLoadSettings projectLoadSettings)
478 project_load_settings = projectLoadSettings;
479 fullFileName = String.Empty;
483 public void LoadXml (string projectXml)
485 LoadXml (projectXml, ProjectLoadSettings.None);
488 public void LoadXml (string projectXml, ProjectLoadSettings projectLoadSettings)
490 project_load_settings = projectLoadSettings;
491 fullFileName = String.Empty;
492 DoLoad (new StringReader (projectXml));
493 MarkProjectAsDirty ();
497 public void MarkProjectAsDirty ()
500 timeOfLastDirty = DateTime.Now;
503 [MonoTODO ("Not tested")]
504 public void RemoveAllItemGroups ()
506 int length = ItemGroups.Count;
507 BuildItemGroup [] groups = new BuildItemGroup [length];
508 ItemGroups.CopyTo (groups, 0);
510 for (int i = 0; i < length; i++)
511 RemoveItemGroup (groups [i]);
513 MarkProjectAsDirty ();
517 [MonoTODO ("Not tested")]
518 public void RemoveAllPropertyGroups ()
520 int length = PropertyGroups.Count;
521 BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
522 PropertyGroups.CopyTo (groups, 0);
524 for (int i = 0; i < length; i++)
525 RemovePropertyGroup (groups [i]);
527 MarkProjectAsDirty ();
532 public void RemoveItem (BuildItem itemToRemove)
534 if (itemToRemove == null)
535 throw new ArgumentNullException ("itemToRemove");
537 if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
538 throw new InvalidOperationException ("The object passed in is not part of the project.");
540 BuildItemGroup big = itemToRemove.ParentItemGroup;
542 if (big.Count == 1) {
543 // ParentItemGroup for items from xml and that have parent is the same
544 groupingCollection.Remove (big);
546 if (big.ParentProject != this)
547 throw new InvalidOperationException ("The object passed in is not part of the project.");
549 if (itemToRemove.FromXml)
550 big.RemoveItem (itemToRemove);
552 big.RemoveItem (itemToRemove.ParentItem);
555 MarkProjectAsDirty ();
559 [MonoTODO ("Not tested")]
560 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
562 if (itemGroupToRemove == null)
563 throw new ArgumentNullException ("itemGroupToRemove");
565 groupingCollection.Remove (itemGroupToRemove);
566 MarkProjectAsDirty ();
570 // NOTE: does not modify imported projects
571 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
573 throw new NotImplementedException ();
577 public void RemoveItemsByName (string itemName)
579 if (itemName == null)
580 throw new ArgumentNullException ("itemName");
582 throw new NotImplementedException ();
585 [MonoTODO ("Not tested")]
586 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
588 if (propertyGroupToRemove == null)
589 throw new ArgumentNullException ("propertyGroupToRemove");
591 groupingCollection.Remove (propertyGroupToRemove);
592 MarkProjectAsDirty ();
596 // NOTE: does not modify imported projects
597 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
599 throw new NotImplementedException ();
603 public void ResetBuildStatus ()
605 // hack to allow built targets to be removed
611 public void Save (string projectFileName)
613 Save (projectFileName, Encoding.Default);
617 [MonoTODO ("Ignores encoding")]
618 public void Save (string projectFileName, Encoding encoding)
620 xmlDocument.Save (projectFileName);
624 public void Save (TextWriter outTextWriter)
626 xmlDocument.Save (outTextWriter);
630 public void SetImportedProperty (string propertyName,
631 string propertyValue,
633 Project importProject)
635 SetImportedProperty (propertyName, propertyValue, condition, importProject,
636 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
639 public void SetImportedProperty (string propertyName,
640 string propertyValue,
642 Project importedProject,
643 PropertyPosition position)
645 SetImportedProperty (propertyName, propertyValue, condition, importedProject,
646 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
650 public void SetImportedProperty (string propertyName,
651 string propertyValue,
653 Project importedProject,
654 PropertyPosition position,
655 bool treatPropertyValueAsLiteral)
657 throw new NotImplementedException ();
660 public void SetProjectExtensions (string id, string xmlText)
663 throw new ArgumentNullException ("id");
665 throw new ArgumentNullException ("xmlText");
667 XmlNode projectExtensions, node;
669 projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
671 if (projectExtensions == null) {
672 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
673 xmlDocument.DocumentElement.AppendChild (projectExtensions);
675 node = xmlDocument.CreateElement (id, XmlNamespace);
676 node.InnerXml = xmlText;
677 projectExtensions.AppendChild (node);
679 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
682 node = xmlDocument.CreateElement (id, XmlNamespace);
683 projectExtensions.AppendChild (node);
686 node.InnerXml = xmlText;
690 MarkProjectAsDirty ();
693 public void SetProperty (string propertyName,
694 string propertyValue)
696 SetProperty (propertyName, propertyValue, "true",
697 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
700 public void SetProperty (string propertyName,
701 string propertyValue,
704 SetProperty (propertyName, propertyValue, condition,
705 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
708 public void SetProperty (string propertyName,
709 string propertyValue,
711 PropertyPosition position)
713 SetProperty (propertyName, propertyValue, condition,
714 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
718 public void SetProperty (string propertyName,
719 string propertyValue,
721 PropertyPosition position,
722 bool treatPropertyValueAsLiteral)
724 throw new NotImplementedException ();
727 internal void Unload ()
732 internal void CheckUnloaded ()
735 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
738 internal void NeedToReevaluate ()
740 needToReevaluate = true;
743 // Does the actual loading.
744 void DoLoad (TextReader textReader)
747 ParentEngine.RemoveLoadedProject (this);
749 xmlDocument.Load (textReader);
751 if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
752 throw new InvalidProjectFileException (String.Format (
753 "Project file '{0}' is a VS2003 project, which is not " +
754 "supported by xbuild. You need to convert it to msbuild " +
755 "format to build with xbuild.", fullFileName));
757 if (SchemaFile != null) {
758 xmlDocument.Schemas.Add (XmlSchema.Read (
759 new StreamReader (SchemaFile), ValidationCallBack));
760 xmlDocument.Validate (ValidationCallBack);
763 if (xmlDocument.DocumentElement.Name != "Project") {
764 throw new InvalidProjectFileException (String.Format (
765 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
768 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
769 throw new InvalidProjectFileException (
770 @"The default XML namespace of the project must be the MSBuild XML namespace." +
771 " If the project is authored in the MSBuild 2003 format, please add " +
772 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
773 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
776 ParentEngine.AddLoadedProject (this);
777 } catch (Exception e) {
778 throw new InvalidProjectFileException (String.Format ("{0}: {1}",
779 fullFileName, e.Message), e);
781 if (textReader != null)
793 groupingCollection = new GroupingCollection (this);
794 imports = new ImportCollection (groupingCollection);
795 usingTasks = new UsingTaskCollection (this);
796 itemGroups = new BuildItemGroupCollection (groupingCollection);
797 propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
798 targets = new TargetCollection (this);
799 last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
801 taskDatabase = new TaskDatabase ();
802 taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (GetToolsVersionToUse ()));
804 initialTargets = new List<string> ();
805 defaultTargets = new string [0];
806 PrepareForEvaluate ();
807 ProcessElements (xmlDocument.DocumentElement, null);
813 void ProcessProjectAttributes (XmlAttributeCollection attributes)
815 foreach (XmlAttribute attr in attributes) {
817 case "InitialTargets":
818 initialTargets.AddRange (attr.Value.Split (
819 new char [] {';', ' '},
820 StringSplitOptions.RemoveEmptyEntries));
822 case "DefaultTargets":
823 // first non-empty DefaultTargets found is used
824 if (defaultTargets == null || defaultTargets.Length == 0)
825 defaultTargets = attr.Value.Split (new char [] {';', ' '},
826 StringSplitOptions.RemoveEmptyEntries);
827 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
828 DefaultTargets, PropertyType.Reserved));
834 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
836 ProcessProjectAttributes (rootElement.Attributes);
837 foreach (XmlNode xn in rootElement.ChildNodes) {
838 if (xn is XmlElement) {
839 XmlElement xe = (XmlElement) xn;
841 case "ProjectExtensions":
842 AddProjectExtensions (xe);
853 AddUsingTask (xe, ip);
859 AddItemGroup (xe, ip);
861 case "PropertyGroup":
862 AddPropertyGroup (xe, ip);
868 throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file.", xe.Name));
874 void PrepareForEvaluate ()
876 evaluatedItems = new BuildItemGroup (null, this, null, true);
877 evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
878 evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
879 evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
880 if (building && current_settings == BuildSettings.None)
881 RemoveBuiltTargets ();
883 InitializeProperties ();
888 groupingCollection.Evaluate ();
890 //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
891 foreach (UsingTask usingTask in UsingTasks)
892 usingTask.Evaluate ();
895 // Removes entries of all earlier built targets for this project
896 void RemoveBuiltTargets ()
898 foreach (string key in builtTargetKeys)
899 ParentEngine.BuiltTargetsOutputByName.Remove (key);
902 void InitializeProperties ()
906 evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
908 foreach (BuildProperty gp in GlobalProperties) {
909 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
910 EvaluatedProperties.AddProperty (bp);
913 foreach (BuildProperty gp in GlobalProperties)
914 ParentEngine.GlobalProperties.AddProperty (gp);
916 // add properties that we dont have from parent engine's
918 foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
919 if (EvaluatedProperties [gp.Name] == null) {
920 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
921 EvaluatedProperties.AddProperty (bp);
925 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
926 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
927 EvaluatedProperties.AddProperty (bp);
930 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
931 PropertyType.Reserved));
932 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
933 Path.GetFileNameWithoutExtension (fullFileName),
934 PropertyType.Reserved));
935 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved));
936 string toolsVersionToUse = GetToolsVersionToUse ();
937 string toolsPath = parentEngine.Toolsets [toolsVersionToUse].ToolsPath;
938 if (toolsPath == null)
939 throw new Exception ("Unknown toolsVersion: " + toolsVersionToUse);
940 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
941 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", toolsVersionToUse, PropertyType.Reserved));
942 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
943 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", ExtensionsPath, PropertyType.Reserved));
944 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
945 EvaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
947 // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
949 if (FullFileName == String.Empty)
950 projectDir = Environment.CurrentDirectory;
952 projectDir = Path.GetDirectoryName (FullFileName);
954 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
957 string GetToolsVersionToUse ()
959 if (String.IsNullOrEmpty (ToolsVersion)) {
960 if (HasToolsVersionAttribute)
961 return DefaultToolsVersion;
963 return parentEngine.DefaultToolsVersion;
969 void AddProjectExtensions (XmlElement xmlElement)
973 void AddMessage (XmlElement xmlElement)
977 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
979 Target target = new Target (xmlElement, this, importedProject);
980 targets.AddTarget (target);
982 if (firstTargetName == null)
983 firstTargetName = target.Name;
986 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
990 usingTask = new UsingTask (xmlElement, this, importedProject);
991 UsingTasks.Add (usingTask);
994 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
996 // eval all the properties etc till the import
997 groupingCollection.Evaluate (EvaluationType.Property);
999 Import import = new Import (xmlElement, this, importingProject);
1000 if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
1003 if (Imports.Contains (import)) {
1004 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1005 "A circular reference was found involving the import of {0}. Only" +
1006 " the first import of this file will be used, ignoring others.",
1007 import.ProjectPath);
1012 if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
1013 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1014 "The main project file was imported here, which creates a circular " +
1015 "reference. Ignoring this import.");
1020 Imports.Add (import);
1021 import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
1024 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
1026 BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
1027 ItemGroups.Add (big);
1030 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
1032 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
1033 PropertyGroups.Add (bpg);
1036 void AddChoose (XmlElement xmlElement)
1038 BuildChoose bc = new BuildChoose (xmlElement, this);
1039 groupingCollection.Add (bc);
1042 static void ValidationCallBack (object sender, ValidationEventArgs e)
1044 Console.WriteLine ("Validation Error: {0}", e.Message);
1047 public bool BuildEnabled {
1049 return buildEnabled;
1052 buildEnabled = value;
1057 public Encoding Encoding {
1058 get { return encoding; }
1061 public string DefaultTargets {
1063 return String.Join ("; ", defaultTargets);
1066 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
1068 defaultTargets = value.Split (new char [] {';', ' '},
1069 StringSplitOptions.RemoveEmptyEntries);
1073 public BuildItemGroup EvaluatedItems {
1075 if (needToReevaluate) {
1076 needToReevaluate = false;
1079 return evaluatedItems;
1083 public BuildItemGroup EvaluatedItemsIgnoringCondition {
1085 if (needToReevaluate) {
1086 needToReevaluate = false;
1089 return evaluatedItemsIgnoringCondition;
1093 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
1095 // FIXME: do we need to do this here?
1096 if (needToReevaluate) {
1097 needToReevaluate = false;
1100 return evaluatedItemsByName;
1104 internal IEnumerable EvaluatedItemsByNameAsDictionaryEntries {
1106 if (EvaluatedItemsByName.Count == 0)
1109 foreach (KeyValuePair<string, BuildItemGroup> pair in EvaluatedItemsByName) {
1110 foreach (BuildItem bi in pair.Value)
1111 yield return new DictionaryEntry (pair.Key, bi.ConvertToITaskItem (null, ExpressionOptions.ExpandItemRefs));
1116 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
1118 // FIXME: do we need to do this here?
1119 if (needToReevaluate) {
1120 needToReevaluate = false;
1123 return evaluatedItemsByNameIgnoringCondition;
1127 // For batching implementation
1128 Dictionary<string, BuildItemGroup> perBatchItemsByName;
1129 Dictionary<string, BuildItemGroup> commonItemsByName;
1132 public Dictionary<string, BuildItemGroup> perBatchItemsByName;
1133 public Dictionary<string, BuildItemGroup> commonItemsByName;
1135 public Batch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1137 this.perBatchItemsByName = perBatchItemsByName;
1138 this.commonItemsByName = commonItemsByName;
1142 Stack<Batch> Batches {
1143 get { return batches; }
1146 internal void PushBatch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1148 batches.Push (new Batch (perBatchItemsByName, commonItemsByName));
1149 SetBatchedItems (perBatchItemsByName, commonItemsByName);
1152 internal void PopBatch ()
1155 if (batches.Count > 0) {
1156 Batch b = batches.Peek ();
1157 SetBatchedItems (b.perBatchItemsByName, b.commonItemsByName);
1159 SetBatchedItems (null, null);
1163 void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1165 this.perBatchItemsByName = perBatchItemsByName;
1166 this.commonItemsByName = commonItemsByName;
1170 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
1172 if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
1175 if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
1179 return EvaluatedItemsByName.TryGetValue (itemName, out group);
1182 internal string GetMetadataBatched (string itemName, string metadataName)
1184 BuildItemGroup group = null;
1185 if (itemName == null) {
1186 //unqualified, all items in a batch(bucket) have the
1187 //same metadata values
1188 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
1190 group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
1193 TryGetEvaluatedItemByNameBatched (itemName, out group);
1196 if (group != null) {
1197 foreach (BuildItem item in group) {
1198 if (item.HasMetadata (metadataName))
1199 return item.GetEvaluatedMetadata (metadataName);
1202 return String.Empty;
1205 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
1207 if (perBatchItemsByName == null && commonItemsByName == null)
1208 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
1211 if (perBatchItemsByName != null)
1212 foreach (BuildItemGroup group in perBatchItemsByName.Values)
1215 if (commonItemsByName != null)
1216 foreach (BuildItemGroup group in commonItemsByName.Values)
1220 T GetFirst<T> (ICollection<T> list)
1225 foreach (T t in list)
1231 internal void LogWarning (string filename, string message, params object[] messageArgs)
1233 BuildWarningEventArgs bwea = new BuildWarningEventArgs (
1234 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1236 ParentEngine.EventSource.FireWarningRaised (this, bwea);
1239 internal void LogError (string filename, string message,
1240 params object[] messageArgs)
1242 BuildErrorEventArgs beea = new BuildErrorEventArgs (
1243 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1245 ParentEngine.EventSource.FireErrorRaised (this, beea);
1248 static string ExtensionsPath {
1250 if (extensions_path == null) {
1251 // NOTE: code from mcs/tools/gacutil/driver.cs
1252 PropertyInfo gac = typeof (System.Environment).GetProperty (
1253 "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
1256 MethodInfo get_gac = gac.GetGetMethod (true);
1257 string gac_path = (string) get_gac.Invoke (null, null);
1258 extensions_path = Path.GetFullPath (Path.Combine (
1259 gac_path, Path.Combine ("..", "xbuild")));
1262 return extensions_path;
1266 public BuildPropertyGroup EvaluatedProperties {
1268 if (needToReevaluate) {
1269 needToReevaluate = false;
1272 return evaluatedProperties;
1276 internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
1278 foreach (BuildProperty bp in EvaluatedProperties)
1279 yield return new DictionaryEntry (bp.Name, bp.Value);
1283 public string FullFileName {
1284 get { return fullFileName; }
1285 set { fullFileName = value; }
1288 public BuildPropertyGroup GlobalProperties {
1289 get { return globalProperties; }
1292 throw new ArgumentNullException ("value");
1295 throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1297 globalProperties = value;
1301 public bool IsDirty {
1302 get { return isDirty; }
1305 public bool IsValidated {
1306 get { return isValidated; }
1307 set { isValidated = value; }
1310 public BuildItemGroupCollection ItemGroups {
1311 get { return itemGroups; }
1314 public ImportCollection Imports {
1315 get { return imports; }
1318 public string InitialTargets {
1320 return String.Join ("; ", initialTargets.ToArray ());
1323 initialTargets.Clear ();
1324 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1326 initialTargets.AddRange (value.Split (
1327 new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
1331 public Engine ParentEngine {
1332 get { return parentEngine; }
1335 public BuildPropertyGroupCollection PropertyGroups {
1336 get { return propertyGroups; }
1339 public string SchemaFile {
1340 get { return schemaFile; }
1341 set { schemaFile = value; }
1344 public TargetCollection Targets {
1345 get { return targets; }
1348 public DateTime TimeOfLastDirty {
1349 get { return timeOfLastDirty; }
1352 public UsingTaskCollection UsingTasks {
1353 get { return usingTasks; }
1358 get { return xmlDocument.InnerXml; }
1361 // corresponds to the xml attribute
1362 public string DefaultToolsVersion {
1364 if (xmlDocument != null)
1365 return xmlDocument.DocumentElement.GetAttribute ("ToolsVersion");
1369 if (xmlDocument != null)
1370 xmlDocument.DocumentElement.SetAttribute ("ToolsVersion", value);
1374 public bool HasToolsVersionAttribute {
1376 return xmlDocument != null && xmlDocument.DocumentElement.HasAttribute ("ToolsVersion");
1380 public string ToolsVersion {
1384 internal List<string> BuiltTargetKeys {
1385 get { return builtTargetKeys; }
1388 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1389 get { return last_item_group_containing; }
1392 internal static XmlNamespaceManager XmlNamespaceManager {
1394 if (manager == null) {
1395 manager = new XmlNamespaceManager (new NameTable ());
1396 manager.AddNamespace ("tns", ns);
1403 internal TaskDatabase TaskDatabase {
1404 get { return taskDatabase; }
1407 internal XmlDocument XmlDocument {
1408 get { return xmlDocument; }
1411 internal static string XmlNamespace {
1417 PlatformID pid = Environment.OSVersion.Platform;
1425 return "Windows_NT";