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;
80 // This is used to keep track of "current" file,
81 // which is then used to set the reserved properties
82 // $(MSBuildThisFile*)
83 Stack<string> this_file_property_stack;
84 ProjectLoadSettings project_load_settings;
87 static string extensions_path;
88 static XmlNamespaceManager manager;
89 static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
92 : this (Engine.GlobalEngine)
96 public Project (Engine engine) : this (engine, null)
100 public Project (Engine engine, string toolsVersion)
102 parentEngine = engine;
103 ToolsVersion = toolsVersion;
105 buildEnabled = ParentEngine.BuildEnabled;
106 xmlDocument = new XmlDocument ();
107 xmlDocument.PreserveWhitespace = false;
108 xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
109 xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
111 fullFileName = String.Empty;
112 timeOfLastDirty = DateTime.Now;
113 current_settings = BuildSettings.None;
114 project_load_settings = ProjectLoadSettings.None;
118 initialTargets = new List<string> ();
119 defaultTargets = new string [0];
120 batches = new Stack<Batch> ();
121 this_file_property_stack = new Stack<string> ();
123 globalProperties = new BuildPropertyGroup (null, this, null, false);
124 foreach (BuildProperty bp in parentEngine.GlobalProperties)
125 GlobalProperties.AddProperty (bp.Clone (true));
131 [MonoTODO ("Not tested")]
132 public void AddNewImport (string importLocation,
133 string importCondition)
135 if (importLocation == null)
136 throw new ArgumentNullException ("importLocation");
138 XmlElement importElement = xmlDocument.CreateElement ("Import", XmlNamespace);
139 xmlDocument.DocumentElement.AppendChild (importElement);
140 importElement.SetAttribute ("Project", importLocation);
141 if (!String.IsNullOrEmpty (importCondition))
142 importElement.SetAttribute ("Condition", importCondition);
144 Import import = new Import (importElement, this, null);
145 imports.Add (import);
146 MarkProjectAsDirty ();
150 public BuildItem AddNewItem (string itemName,
153 return AddNewItem (itemName, itemInclude, false);
156 [MonoTODO ("Adds item not in the same place as MS")]
157 public BuildItem AddNewItem (string itemName,
159 bool treatItemIncludeAsLiteral)
163 if (itemGroups.Count == 0)
164 big = AddNewItemGroup ();
166 if (last_item_group_containing.ContainsKey (itemName)) {
167 big = last_item_group_containing [itemName];
170 BuildItemGroup [] groups = new BuildItemGroup [itemGroups.Count];
171 itemGroups.CopyTo (groups, 0);
176 BuildItem item = big.AddNewItem (itemName, itemInclude, treatItemIncludeAsLiteral);
178 MarkProjectAsDirty ();
184 [MonoTODO ("Not tested")]
185 public BuildItemGroup AddNewItemGroup ()
187 XmlElement element = xmlDocument.CreateElement ("ItemGroup", XmlNamespace);
188 xmlDocument.DocumentElement.AppendChild (element);
190 BuildItemGroup big = new BuildItemGroup (element, this, null, false);
191 itemGroups.Add (big);
192 MarkProjectAsDirty ();
198 [MonoTODO ("Ignores insertAtEndOfProject")]
199 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
201 XmlElement element = xmlDocument.CreateElement ("PropertyGroup", XmlNamespace);
202 xmlDocument.DocumentElement.AppendChild (element);
204 BuildPropertyGroup bpg = new BuildPropertyGroup (element, this, null, false);
205 propertyGroups.Add (bpg);
206 MarkProjectAsDirty ();
212 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
213 public void AddNewUsingTaskFromAssemblyFile (string taskName,
216 if (taskName == null)
217 throw new ArgumentNullException ("taskName");
218 if (assemblyFile == null)
219 throw new ArgumentNullException ("assemblyFile");
221 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
222 xmlDocument.DocumentElement.AppendChild (element);
223 element.SetAttribute ("TaskName", taskName);
224 element.SetAttribute ("AssemblyFile", assemblyFile);
226 UsingTask ut = new UsingTask (element, this, null);
228 MarkProjectAsDirty ();
231 [MonoTODO ("Not tested, isn't added to TaskDatabase (no reevaluation)")]
232 public void AddNewUsingTaskFromAssemblyName (string taskName,
235 if (taskName == null)
236 throw new ArgumentNullException ("taskName");
237 if (assemblyName == null)
238 throw new ArgumentNullException ("assemblyName");
240 XmlElement element = xmlDocument.CreateElement ("UsingTask", XmlNamespace);
241 xmlDocument.DocumentElement.AppendChild (element);
242 element.SetAttribute ("TaskName", taskName);
243 element.SetAttribute ("AssemblyName", assemblyName);
245 UsingTask ut = new UsingTask (element, this, null);
247 MarkProjectAsDirty ();
250 [MonoTODO ("Not tested")]
253 return Build (new string [0]);
256 [MonoTODO ("Not tested")]
257 public bool Build (string targetName)
259 if (targetName == null)
260 return Build ((string[]) null);
262 return Build (new string [1] { targetName });
265 [MonoTODO ("Not tested")]
266 public bool Build (string [] targetNames)
268 return Build (targetNames, null);
271 [MonoTODO ("Not tested")]
272 public bool Build (string [] targetNames,
273 IDictionary targetOutputs)
275 return Build (targetNames, targetOutputs, BuildSettings.None);
278 [MonoTODO ("Not tested")]
279 public bool Build (string [] targetNames,
280 IDictionary targetOutputs,
281 BuildSettings buildFlags)
285 ParentEngine.StartProjectBuild (this, targetNames);
287 // Invoking this to emit a warning in case of unsupported
289 GetToolsVersionToUse (true);
291 string current_directory = Environment.CurrentDirectory;
293 current_settings = buildFlags;
294 if (!String.IsNullOrEmpty (fullFileName))
295 Directory.SetCurrentDirectory (Path.GetDirectoryName (fullFileName));
297 result = BuildInternal (targetNames, targetOutputs, buildFlags);
299 ParentEngine.EndProjectBuild (this, result);
300 current_settings = BuildSettings.None;
301 Directory.SetCurrentDirectory (current_directory);
308 bool BuildInternal (string [] targetNames,
309 IDictionary targetOutputs,
310 BuildSettings buildFlags)
313 if (buildFlags == BuildSettings.None) {
314 needToReevaluate = false;
318 if (targetNames == null || targetNames.Length == 0) {
319 if (defaultTargets != null && defaultTargets.Length != 0) {
320 targetNames = defaultTargets;
321 } else if (firstTargetName != null) {
322 targetNames = new string [1] { firstTargetName};
324 if (targets == null || targets.Count == 0) {
325 LogError (fullFileName, "No target found in the project");
333 if (!initialTargetsBuilt) {
334 foreach (string target in initialTargets) {
335 if (!BuildTarget (target.Trim (), targetOutputs))
338 initialTargetsBuilt = true;
341 foreach (string target in targetNames)
342 if (!BuildTarget (target.Trim (), targetOutputs))
348 bool BuildTarget (string target_name, IDictionary targetOutputs)
350 if (target_name == null)
351 throw new ArgumentException ("targetNames cannot contain null strings");
353 if (!targets.Exists (target_name)) {
354 LogError (fullFileName, "Target named '{0}' not found in the project.", target_name);
358 string key = GetKeyForTarget (target_name);
359 if (!targets [target_name].Build (key))
363 if (ParentEngine.BuiltTargetsOutputByName.TryGetValue (key, out outputs)) {
364 if (targetOutputs != null)
365 targetOutputs.Add (target_name, outputs);
370 internal string GetKeyForTarget (string target_name)
372 return GetKeyForTarget (target_name, true);
375 internal string GetKeyForTarget (string target_name, bool include_global_properties)
377 // target name is case insensitive
378 return fullFileName + ":" + target_name.ToLower () +
379 (include_global_properties ? (":" + GlobalPropertiesToString (GlobalProperties))
383 string GlobalPropertiesToString (BuildPropertyGroup bgp)
385 StringBuilder sb = new StringBuilder ();
386 foreach (BuildProperty bp in bgp)
387 sb.AppendFormat (" {0}:{1}", bp.Name, bp.FinalValue);
388 return sb.ToString ();
392 public string [] GetConditionedPropertyValues (string propertyName)
394 if (conditionedProperties.ContainsKey (propertyName))
395 return conditionedProperties [propertyName].ToArray ();
397 return new string [0];
400 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
402 if (needToReevaluate) {
403 needToReevaluate = false;
407 if (evaluatedItemsByName.ContainsKey (itemName))
408 return evaluatedItemsByName [itemName];
410 return new BuildItemGroup (this);
413 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
415 if (needToReevaluate) {
416 needToReevaluate = false;
420 if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
421 return evaluatedItemsByNameIgnoringCondition [itemName];
423 return new BuildItemGroup (this);
426 public string GetEvaluatedProperty (string propertyName)
428 if (needToReevaluate) {
429 needToReevaluate = false;
433 if (propertyName == null)
434 throw new ArgumentNullException ("propertyName");
436 BuildProperty bp = evaluatedProperties [propertyName];
438 return bp == null ? null : (string) bp;
441 [MonoTODO ("We should remember that node and not use XPath to get it")]
442 public string GetProjectExtensions (string id)
444 if (id == null || id == String.Empty)
447 XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
452 return node.InnerXml;
456 public void Load (string projectFileName)
458 Load (projectFileName, ProjectLoadSettings.None);
461 public void Load (string projectFileName, ProjectLoadSettings settings)
463 project_load_settings = settings;
464 if (String.IsNullOrEmpty (projectFileName))
465 throw new ArgumentNullException ("projectFileName");
467 if (!File.Exists (projectFileName))
468 throw new ArgumentException (String.Format ("Project file {0} not found", projectFileName),
471 this.fullFileName = Utilities.FromMSBuildPath (Path.GetFullPath (projectFileName));
472 PushThisFileProperty (fullFileName);
474 string filename = fullFileName;
475 if (String.Compare (Path.GetExtension (fullFileName), ".sln", true) == 0) {
476 Project tmp_project = ParentEngine.CreateNewProject ();
477 SolutionParser sln_parser = new SolutionParser ();
478 sln_parser.ParseSolution (fullFileName, tmp_project, delegate (int errorNumber, string message) {
479 LogWarning (filename, message);
481 filename = fullFileName + ".proj";
483 tmp_project.Save (filename);
484 ParentEngine.RemoveLoadedProject (tmp_project);
485 DoLoad (new StreamReader (filename));
487 if (Environment.GetEnvironmentVariable ("XBUILD_EMIT_SOLUTION") == null)
488 File.Delete (filename);
491 DoLoad (new StreamReader (filename));
495 [MonoTODO ("Not tested")]
496 public void Load (TextReader textReader)
498 Load (textReader, ProjectLoadSettings.None);
501 public void Load (TextReader textReader, ProjectLoadSettings projectLoadSettings)
503 project_load_settings = projectLoadSettings;
504 fullFileName = String.Empty;
508 public void LoadXml (string projectXml)
510 LoadXml (projectXml, ProjectLoadSettings.None);
513 public void LoadXml (string projectXml, ProjectLoadSettings projectLoadSettings)
515 project_load_settings = projectLoadSettings;
516 fullFileName = String.Empty;
517 DoLoad (new StringReader (projectXml));
518 MarkProjectAsDirty ();
522 public void MarkProjectAsDirty ()
525 timeOfLastDirty = DateTime.Now;
528 [MonoTODO ("Not tested")]
529 public void RemoveAllItemGroups ()
531 int length = ItemGroups.Count;
532 BuildItemGroup [] groups = new BuildItemGroup [length];
533 ItemGroups.CopyTo (groups, 0);
535 for (int i = 0; i < length; i++)
536 RemoveItemGroup (groups [i]);
538 MarkProjectAsDirty ();
542 [MonoTODO ("Not tested")]
543 public void RemoveAllPropertyGroups ()
545 int length = PropertyGroups.Count;
546 BuildPropertyGroup [] groups = new BuildPropertyGroup [length];
547 PropertyGroups.CopyTo (groups, 0);
549 for (int i = 0; i < length; i++)
550 RemovePropertyGroup (groups [i]);
552 MarkProjectAsDirty ();
557 public void RemoveItem (BuildItem itemToRemove)
559 if (itemToRemove == null)
560 throw new ArgumentNullException ("itemToRemove");
562 if (!itemToRemove.FromXml && !itemToRemove.HasParentItem)
563 throw new InvalidOperationException ("The object passed in is not part of the project.");
565 BuildItemGroup big = itemToRemove.ParentItemGroup;
567 if (big.Count == 1) {
568 // ParentItemGroup for items from xml and that have parent is the same
569 groupingCollection.Remove (big);
571 if (big.ParentProject != this)
572 throw new InvalidOperationException ("The object passed in is not part of the project.");
574 if (itemToRemove.FromXml)
575 big.RemoveItem (itemToRemove);
577 big.RemoveItem (itemToRemove.ParentItem);
580 MarkProjectAsDirty ();
584 [MonoTODO ("Not tested")]
585 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
587 if (itemGroupToRemove == null)
588 throw new ArgumentNullException ("itemGroupToRemove");
590 groupingCollection.Remove (itemGroupToRemove);
591 MarkProjectAsDirty ();
595 // NOTE: does not modify imported projects
596 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
598 throw new NotImplementedException ();
602 public void RemoveItemsByName (string itemName)
604 if (itemName == null)
605 throw new ArgumentNullException ("itemName");
607 throw new NotImplementedException ();
610 [MonoTODO ("Not tested")]
611 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
613 if (propertyGroupToRemove == null)
614 throw new ArgumentNullException ("propertyGroupToRemove");
616 groupingCollection.Remove (propertyGroupToRemove);
617 MarkProjectAsDirty ();
621 // NOTE: does not modify imported projects
622 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
624 throw new NotImplementedException ();
628 public void ResetBuildStatus ()
630 // hack to allow built targets to be removed
636 public void Save (string projectFileName)
638 Save (projectFileName, Encoding.Default);
642 [MonoTODO ("Ignores encoding")]
643 public void Save (string projectFileName, Encoding encoding)
645 xmlDocument.Save (projectFileName);
649 public void Save (TextWriter outTextWriter)
651 xmlDocument.Save (outTextWriter);
655 public void SetImportedProperty (string propertyName,
656 string propertyValue,
658 Project importProject)
660 SetImportedProperty (propertyName, propertyValue, condition, importProject,
661 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
664 public void SetImportedProperty (string propertyName,
665 string propertyValue,
667 Project importedProject,
668 PropertyPosition position)
670 SetImportedProperty (propertyName, propertyValue, condition, importedProject,
671 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
675 public void SetImportedProperty (string propertyName,
676 string propertyValue,
678 Project importedProject,
679 PropertyPosition position,
680 bool treatPropertyValueAsLiteral)
682 throw new NotImplementedException ();
685 public void SetProjectExtensions (string id, string xmlText)
688 throw new ArgumentNullException ("id");
690 throw new ArgumentNullException ("xmlText");
692 XmlNode projectExtensions, node;
694 projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
696 if (projectExtensions == null) {
697 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
698 xmlDocument.DocumentElement.AppendChild (projectExtensions);
700 node = xmlDocument.CreateElement (id, XmlNamespace);
701 node.InnerXml = xmlText;
702 projectExtensions.AppendChild (node);
704 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
707 node = xmlDocument.CreateElement (id, XmlNamespace);
708 projectExtensions.AppendChild (node);
711 node.InnerXml = xmlText;
715 MarkProjectAsDirty ();
718 public void SetProperty (string propertyName,
719 string propertyValue)
721 SetProperty (propertyName, propertyValue, "true",
722 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
725 public void SetProperty (string propertyName,
726 string propertyValue,
729 SetProperty (propertyName, propertyValue, condition,
730 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
733 public void SetProperty (string propertyName,
734 string propertyValue,
736 PropertyPosition position)
738 SetProperty (propertyName, propertyValue, condition,
739 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
743 public void SetProperty (string propertyName,
744 string propertyValue,
746 PropertyPosition position,
747 bool treatPropertyValueAsLiteral)
749 throw new NotImplementedException ();
752 internal void Unload ()
757 internal void CheckUnloaded ()
760 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
763 internal void NeedToReevaluate ()
765 needToReevaluate = true;
768 // Does the actual loading.
769 void DoLoad (TextReader textReader)
772 ParentEngine.RemoveLoadedProject (this);
774 xmlDocument.Load (textReader);
776 if (xmlDocument.DocumentElement.Name == "VisualStudioProject")
777 throw new InvalidProjectFileException (String.Format (
778 "Project file '{0}' is a VS2003 project, which is not " +
779 "supported by xbuild. You need to convert it to msbuild " +
780 "format to build with xbuild.", fullFileName));
782 if (SchemaFile != null) {
783 xmlDocument.Schemas.Add (XmlSchema.Read (
784 new StreamReader (SchemaFile), ValidationCallBack));
785 xmlDocument.Validate (ValidationCallBack);
788 if (xmlDocument.DocumentElement.Name != "Project") {
789 throw new InvalidProjectFileException (String.Format (
790 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
793 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
794 throw new InvalidProjectFileException (
795 @"The default XML namespace of the project must be the MSBuild XML namespace." +
796 " If the project is authored in the MSBuild 2003 format, please add " +
797 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
798 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
801 ParentEngine.AddLoadedProject (this);
802 } catch (Exception e) {
803 throw new InvalidProjectFileException (String.Format ("{0}: {1}",
804 fullFileName, e.Message), e);
806 if (textReader != null)
818 groupingCollection = new GroupingCollection (this);
819 imports = new ImportCollection (groupingCollection);
820 usingTasks = new UsingTaskCollection (this);
821 itemGroups = new BuildItemGroupCollection (groupingCollection);
822 propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
823 targets = new TargetCollection (this);
824 last_item_group_containing = new Dictionary <string, BuildItemGroup> ();
826 string effective_tools_version = GetToolsVersionToUse (false);
827 taskDatabase = new TaskDatabase ();
828 taskDatabase.CopyTasks (ParentEngine.GetDefaultTasks (effective_tools_version));
830 initialTargets = new List<string> ();
831 defaultTargets = new string [0];
832 PrepareForEvaluate (effective_tools_version);
833 ProcessElements (xmlDocument.DocumentElement, null);
839 void ProcessProjectAttributes (XmlAttributeCollection attributes)
841 foreach (XmlAttribute attr in attributes) {
843 case "InitialTargets":
844 initialTargets.AddRange (attr.Value.Split (
845 new char [] {';', ' '},
846 StringSplitOptions.RemoveEmptyEntries));
848 case "DefaultTargets":
849 // first non-empty DefaultTargets found is used
850 if (defaultTargets == null || defaultTargets.Length == 0)
851 defaultTargets = attr.Value.Split (new char [] {';', ' '},
852 StringSplitOptions.RemoveEmptyEntries);
853 EvaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets",
854 DefaultTargets, PropertyType.Reserved));
860 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
862 ProcessProjectAttributes (rootElement.Attributes);
863 foreach (XmlNode xn in rootElement.ChildNodes) {
864 if (xn is XmlElement) {
865 XmlElement xe = (XmlElement) xn;
867 case "ProjectExtensions":
868 AddProjectExtensions (xe);
879 AddUsingTask (xe, ip);
885 AddItemGroup (xe, ip);
887 case "PropertyGroup":
888 AddPropertyGroup (xe, ip);
894 throw new InvalidProjectFileException (String.Format ("Invalid element '{0}' in project file.", xe.Name));
900 void PrepareForEvaluate (string effective_tools_version)
902 evaluatedItems = new BuildItemGroup (null, this, null, true);
903 evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
904 evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
905 evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
906 if (building && current_settings == BuildSettings.None)
907 RemoveBuiltTargets ();
909 InitializeProperties (effective_tools_version);
914 groupingCollection.Evaluate ();
916 //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
917 foreach (UsingTask usingTask in UsingTasks)
918 usingTask.Evaluate ();
921 // Removes entries of all earlier built targets for this project
922 void RemoveBuiltTargets ()
924 ParentEngine.ClearBuiltTargetsForProject (this);
927 void InitializeProperties (string effective_tools_version)
931 evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
932 conditionedProperties = new Dictionary<string, List<string>> ();
934 foreach (BuildProperty gp in GlobalProperties) {
935 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
936 evaluatedProperties.AddProperty (bp);
939 foreach (BuildProperty gp in GlobalProperties)
940 ParentEngine.GlobalProperties.AddProperty (gp);
942 // add properties that we dont have from parent engine's
944 foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
945 if (evaluatedProperties [gp.Name] == null) {
946 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
947 evaluatedProperties.AddProperty (bp);
951 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
952 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
953 evaluatedProperties.AddProperty (bp);
956 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
957 PropertyType.Reserved));
958 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFullPath", fullFileName, PropertyType.Reserved));
959 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
960 Path.GetFileNameWithoutExtension (fullFileName),
961 PropertyType.Reserved));
962 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectExtension",
963 Path.GetExtension (fullFileName),
964 PropertyType.Reserved));
966 string toolsPath = parentEngine.Toolsets [effective_tools_version].ToolsPath;
967 if (toolsPath == null)
968 throw new Exception (String.Format ("Invalid tools version '{0}', no tools path set for this.", effective_tools_version));
969 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", toolsPath, PropertyType.Reserved));
970 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
971 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsRoot", Path.GetDirectoryName (toolsPath), PropertyType.Reserved));
972 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", effective_tools_version, PropertyType.Reserved));
973 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
974 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", ExtensionsPath, PropertyType.Reserved));
975 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath64", ExtensionsPath, PropertyType.Reserved));
976 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
977 evaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
979 // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
981 if (FullFileName == String.Empty)
982 projectDir = Environment.CurrentDirectory;
984 projectDir = Path.GetDirectoryName (FullFileName);
986 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
988 if (this_file_property_stack.Count > 0)
989 // Just re-inited the properties, but according to the stack,
990 // we should have a MSBuild*This* property set
991 SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
995 // ToolsVersion property
996 // ToolsVersion attribute on the project
997 // parentEngine's DefaultToolsVersion
998 string GetToolsVersionToUse (bool emitWarning)
1000 if (!String.IsNullOrEmpty (ToolsVersion))
1001 return ToolsVersion;
1003 if (!HasToolsVersionAttribute)
1004 return parentEngine.DefaultToolsVersion;
1006 if (parentEngine.Toolsets [DefaultToolsVersion] == null) {
1008 LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
1009 DefaultToolsVersion, parentEngine.DefaultToolsVersion);
1010 return parentEngine.DefaultToolsVersion;
1013 return DefaultToolsVersion;
1016 void AddProjectExtensions (XmlElement xmlElement)
1020 void AddMessage (XmlElement xmlElement)
1024 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
1026 Target target = new Target (xmlElement, this, importedProject);
1027 targets.AddTarget (target);
1029 if (firstTargetName == null)
1030 firstTargetName = target.Name;
1033 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
1035 UsingTask usingTask;
1037 usingTask = new UsingTask (xmlElement, this, importedProject);
1038 UsingTasks.Add (usingTask);
1041 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
1043 // eval all the properties etc till the import
1044 groupingCollection.Evaluate (EvaluationType.Property);
1046 Import import = new Import (xmlElement, this, importingProject);
1047 if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
1050 if (Imports.Contains (import)) {
1051 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1052 "A circular reference was found involving the import of {0}. Only" +
1053 " the first import of this file will be used, ignoring others.",
1054 import.ProjectPath);
1059 if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
1060 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1061 "The main project file was imported here, which creates a circular " +
1062 "reference. Ignoring this import.");
1067 Imports.Add (import);
1068 import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
1071 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
1073 BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
1074 ItemGroups.Add (big);
1077 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
1079 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
1080 PropertyGroups.Add (bpg);
1083 void AddChoose (XmlElement xmlElement, ImportedProject importedProject)
1085 BuildChoose bc = new BuildChoose (xmlElement, this, importedProject);
1086 groupingCollection.Add (bc);
1089 static void ValidationCallBack (object sender, ValidationEventArgs e)
1091 Console.WriteLine ("Validation Error: {0}", e.Message);
1094 public bool BuildEnabled {
1096 return buildEnabled;
1099 buildEnabled = value;
1104 public Encoding Encoding {
1105 get { return encoding; }
1108 public string DefaultTargets {
1110 return String.Join ("; ", defaultTargets);
1113 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
1115 defaultTargets = value.Split (new char [] {';', ' '},
1116 StringSplitOptions.RemoveEmptyEntries);
1120 public BuildItemGroup EvaluatedItems {
1122 if (needToReevaluate) {
1123 needToReevaluate = false;
1126 return evaluatedItems;
1130 public BuildItemGroup EvaluatedItemsIgnoringCondition {
1132 if (needToReevaluate) {
1133 needToReevaluate = false;
1136 return evaluatedItemsIgnoringCondition;
1140 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
1142 // FIXME: do we need to do this here?
1143 if (needToReevaluate) {
1144 needToReevaluate = false;
1147 return evaluatedItemsByName;
1151 internal IEnumerable EvaluatedItemsByNameAsDictionaryEntries {
1153 if (EvaluatedItemsByName.Count == 0)
1156 foreach (KeyValuePair<string, BuildItemGroup> pair in EvaluatedItemsByName) {
1157 foreach (BuildItem bi in pair.Value)
1158 yield return new DictionaryEntry (pair.Key, bi.ConvertToITaskItem (null, ExpressionOptions.ExpandItemRefs));
1163 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
1165 // FIXME: do we need to do this here?
1166 if (needToReevaluate) {
1167 needToReevaluate = false;
1170 return evaluatedItemsByNameIgnoringCondition;
1174 // For batching implementation
1175 Dictionary<string, BuildItemGroup> perBatchItemsByName;
1176 Dictionary<string, BuildItemGroup> commonItemsByName;
1179 public Dictionary<string, BuildItemGroup> perBatchItemsByName;
1180 public Dictionary<string, BuildItemGroup> commonItemsByName;
1182 public Batch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1184 this.perBatchItemsByName = perBatchItemsByName;
1185 this.commonItemsByName = commonItemsByName;
1189 Stack<Batch> Batches {
1190 get { return batches; }
1193 internal void PushBatch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1195 batches.Push (new Batch (perBatchItemsByName, commonItemsByName));
1196 SetBatchedItems (perBatchItemsByName, commonItemsByName);
1199 internal void PopBatch ()
1202 if (batches.Count > 0) {
1203 Batch b = batches.Peek ();
1204 SetBatchedItems (b.perBatchItemsByName, b.commonItemsByName);
1206 SetBatchedItems (null, null);
1210 void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1212 this.perBatchItemsByName = perBatchItemsByName;
1213 this.commonItemsByName = commonItemsByName;
1217 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
1219 if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
1222 if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
1226 return EvaluatedItemsByName.TryGetValue (itemName, out group);
1229 internal string GetMetadataBatched (string itemName, string metadataName)
1231 BuildItemGroup group = null;
1232 if (itemName == null) {
1233 //unqualified, all items in a batch(bucket) have the
1234 //same metadata values
1235 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
1237 group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
1240 TryGetEvaluatedItemByNameBatched (itemName, out group);
1243 if (group != null) {
1244 foreach (BuildItem item in group) {
1245 if (item.HasMetadata (metadataName))
1246 return item.GetEvaluatedMetadata (metadataName);
1249 return String.Empty;
1252 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
1254 if (perBatchItemsByName == null && commonItemsByName == null)
1255 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
1258 if (perBatchItemsByName != null)
1259 foreach (BuildItemGroup group in perBatchItemsByName.Values)
1262 if (commonItemsByName != null)
1263 foreach (BuildItemGroup group in commonItemsByName.Values)
1267 T GetFirst<T> (ICollection<T> list)
1272 foreach (T t in list)
1278 // Used for MSBuild*This* set of properties
1279 internal void PushThisFileProperty (string full_filename)
1281 string last_file = this_file_property_stack.Count == 0 ? String.Empty : this_file_property_stack.Peek ();
1282 this_file_property_stack.Push (full_filename);
1283 if (last_file != full_filename)
1284 // first time, or different from previous one
1285 SetMSBuildThisFileProperties (full_filename);
1288 internal void PopThisFileProperty ()
1290 string last_file = this_file_property_stack.Pop ();
1291 if (this_file_property_stack.Count > 0 && last_file != this_file_property_stack.Peek ())
1292 SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
1295 void SetMSBuildThisFileProperties (string full_filename)
1297 if (String.IsNullOrEmpty (full_filename))
1300 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFile", Path.GetFileName (full_filename), PropertyType.Reserved));
1301 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileFullPath", full_filename, PropertyType.Reserved));
1302 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileName", Path.GetFileNameWithoutExtension (full_filename), PropertyType.Reserved));
1303 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileExtension", Path.GetExtension (full_filename), PropertyType.Reserved));
1305 string project_dir = Path.GetDirectoryName (full_filename) + Path.DirectorySeparatorChar;
1306 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectory", project_dir, PropertyType.Reserved));
1307 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectoryNoRoot",
1308 project_dir.Substring (Path.GetPathRoot (project_dir).Length),
1309 PropertyType.Reserved));
1313 internal void LogWarning (string filename, string message, params object[] messageArgs)
1315 BuildWarningEventArgs bwea = new BuildWarningEventArgs (
1316 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1318 ParentEngine.EventSource.FireWarningRaised (this, bwea);
1321 internal void LogError (string filename, string message,
1322 params object[] messageArgs)
1324 BuildErrorEventArgs beea = new BuildErrorEventArgs (
1325 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1327 ParentEngine.EventSource.FireErrorRaised (this, beea);
1330 static string ExtensionsPath {
1332 if (extensions_path == null) {
1333 // NOTE: code from mcs/tools/gacutil/driver.cs
1334 PropertyInfo gac = typeof (System.Environment).GetProperty (
1335 "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
1338 MethodInfo get_gac = gac.GetGetMethod (true);
1339 string gac_path = (string) get_gac.Invoke (null, null);
1340 extensions_path = Path.GetFullPath (Path.Combine (
1341 gac_path, Path.Combine ("..", "xbuild")));
1344 return extensions_path;
1348 public BuildPropertyGroup EvaluatedProperties {
1350 if (needToReevaluate) {
1351 needToReevaluate = false;
1354 return evaluatedProperties;
1358 internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
1360 foreach (BuildProperty bp in EvaluatedProperties)
1361 yield return new DictionaryEntry (bp.Name, bp.Value);
1365 public string FullFileName {
1366 get { return fullFileName; }
1367 set { fullFileName = value; }
1370 public BuildPropertyGroup GlobalProperties {
1371 get { return globalProperties; }
1374 throw new ArgumentNullException ("value");
1377 throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1379 globalProperties = value;
1383 public bool IsDirty {
1384 get { return isDirty; }
1387 public bool IsValidated {
1388 get { return isValidated; }
1389 set { isValidated = value; }
1392 public BuildItemGroupCollection ItemGroups {
1393 get { return itemGroups; }
1396 public ImportCollection Imports {
1397 get { return imports; }
1400 public string InitialTargets {
1402 return String.Join ("; ", initialTargets.ToArray ());
1405 initialTargets.Clear ();
1406 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1408 initialTargets.AddRange (value.Split (
1409 new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
1413 public Engine ParentEngine {
1414 get { return parentEngine; }
1417 public BuildPropertyGroupCollection PropertyGroups {
1418 get { return propertyGroups; }
1421 public string SchemaFile {
1422 get { return schemaFile; }
1423 set { schemaFile = value; }
1426 public TargetCollection Targets {
1427 get { return targets; }
1430 public DateTime TimeOfLastDirty {
1431 get { return timeOfLastDirty; }
1434 public UsingTaskCollection UsingTasks {
1435 get { return usingTasks; }
1440 get { return xmlDocument.InnerXml; }
1443 // corresponds to the xml attribute
1444 public string DefaultToolsVersion {
1446 if (xmlDocument != null)
1447 return xmlDocument.DocumentElement.GetAttribute ("ToolsVersion");
1451 if (xmlDocument != null)
1452 xmlDocument.DocumentElement.SetAttribute ("ToolsVersion", value);
1456 public bool HasToolsVersionAttribute {
1458 return xmlDocument != null && xmlDocument.DocumentElement.HasAttribute ("ToolsVersion");
1462 public string ToolsVersion {
1466 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1467 get { return last_item_group_containing; }
1470 internal static XmlNamespaceManager XmlNamespaceManager {
1472 if (manager == null) {
1473 manager = new XmlNamespaceManager (new NameTable ());
1474 manager.AddNamespace ("tns", ns);
1481 internal TaskDatabase TaskDatabase {
1482 get { return taskDatabase; }
1485 internal XmlDocument XmlDocument {
1486 get { return xmlDocument; }
1489 internal static string XmlNamespace {
1495 PlatformID pid = Environment.OSVersion.Platform;
1503 return "Windows_NT";