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);
933 foreach (BuildProperty gp in GlobalProperties) {
934 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
935 evaluatedProperties.AddProperty (bp);
938 foreach (BuildProperty gp in GlobalProperties)
939 ParentEngine.GlobalProperties.AddProperty (gp);
941 // add properties that we dont have from parent engine's
943 foreach (BuildProperty gp in ParentEngine.GlobalProperties) {
944 if (evaluatedProperties [gp.Name] == null) {
945 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
946 evaluatedProperties.AddProperty (bp);
950 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
951 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
952 evaluatedProperties.AddProperty (bp);
955 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFile", Path.GetFileName (fullFileName),
956 PropertyType.Reserved));
957 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectFullPath", fullFileName, PropertyType.Reserved));
958 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectName",
959 Path.GetFileNameWithoutExtension (fullFileName),
960 PropertyType.Reserved));
961 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectExtension",
962 Path.GetExtension (fullFileName),
963 PropertyType.Reserved));
965 string toolsPath = parentEngine.Toolsets [effective_tools_version].ToolsPath;
966 if (toolsPath == null)
967 throw new Exception (String.Format ("Invalid tools version '{0}', no tools path set for this.", effective_tools_version));
968 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildBinPath", toolsPath, PropertyType.Reserved));
969 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsPath", toolsPath, PropertyType.Reserved));
970 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildToolsVersion", effective_tools_version, PropertyType.Reserved));
971 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath", ExtensionsPath, PropertyType.Reserved));
972 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath32", ExtensionsPath, PropertyType.Reserved));
973 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildExtensionsPath64", ExtensionsPath, PropertyType.Reserved));
974 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDefaultTargets", DefaultTargets, PropertyType.Reserved));
975 evaluatedProperties.AddProperty (new BuildProperty ("OS", OS, PropertyType.Environment));
977 // FIXME: make some internal method that will work like GetDirectoryName but output String.Empty on null/String.Empty
979 if (FullFileName == String.Empty)
980 projectDir = Environment.CurrentDirectory;
982 projectDir = Path.GetDirectoryName (FullFileName);
984 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildProjectDirectory", projectDir, PropertyType.Reserved));
986 if (this_file_property_stack.Count > 0)
987 // Just re-inited the properties, but according to the stack,
988 // we should have a MSBuild*This* property set
989 SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
993 // ToolsVersion property
994 // ToolsVersion attribute on the project
995 // parentEngine's DefaultToolsVersion
996 string GetToolsVersionToUse (bool emitWarning)
998 if (!String.IsNullOrEmpty (ToolsVersion))
1001 if (!HasToolsVersionAttribute)
1002 return parentEngine.DefaultToolsVersion;
1004 if (parentEngine.Toolsets [DefaultToolsVersion] == null) {
1006 LogWarning (FullFileName, "Project has unknown ToolsVersion '{0}'. Using the default tools version '{1}' instead.",
1007 DefaultToolsVersion, parentEngine.DefaultToolsVersion);
1008 return parentEngine.DefaultToolsVersion;
1011 return DefaultToolsVersion;
1014 void AddProjectExtensions (XmlElement xmlElement)
1018 void AddMessage (XmlElement xmlElement)
1022 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
1024 Target target = new Target (xmlElement, this, importedProject);
1025 targets.AddTarget (target);
1027 if (firstTargetName == null)
1028 firstTargetName = target.Name;
1031 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
1033 UsingTask usingTask;
1035 usingTask = new UsingTask (xmlElement, this, importedProject);
1036 UsingTasks.Add (usingTask);
1039 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
1041 // eval all the properties etc till the import
1042 groupingCollection.Evaluate (EvaluationType.Property);
1044 Import import = new Import (xmlElement, this, importingProject);
1045 if (!ConditionParser.ParseAndEvaluate (import.Condition, this))
1048 if (Imports.Contains (import)) {
1049 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1050 "A circular reference was found involving the import of {0}. Only" +
1051 " the first import of this file will be used, ignoring others.",
1052 import.ProjectPath);
1057 if (String.Compare (fullFileName, import.EvaluatedProjectPath) == 0) {
1058 LogWarning (importingProject != null ? importingProject.FullFileName : fullFileName,
1059 "The main project file was imported here, which creates a circular " +
1060 "reference. Ignoring this import.");
1065 Imports.Add (import);
1066 import.Evaluate (project_load_settings == ProjectLoadSettings.IgnoreMissingImports);
1069 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
1071 BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
1072 ItemGroups.Add (big);
1075 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
1077 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
1078 PropertyGroups.Add (bpg);
1081 void AddChoose (XmlElement xmlElement, ImportedProject importedProject)
1083 BuildChoose bc = new BuildChoose (xmlElement, this, importedProject);
1084 groupingCollection.Add (bc);
1087 static void ValidationCallBack (object sender, ValidationEventArgs e)
1089 Console.WriteLine ("Validation Error: {0}", e.Message);
1092 public bool BuildEnabled {
1094 return buildEnabled;
1097 buildEnabled = value;
1102 public Encoding Encoding {
1103 get { return encoding; }
1106 public string DefaultTargets {
1108 return String.Join ("; ", defaultTargets);
1111 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
1113 defaultTargets = value.Split (new char [] {';', ' '},
1114 StringSplitOptions.RemoveEmptyEntries);
1118 public BuildItemGroup EvaluatedItems {
1120 if (needToReevaluate) {
1121 needToReevaluate = false;
1124 return evaluatedItems;
1128 public BuildItemGroup EvaluatedItemsIgnoringCondition {
1130 if (needToReevaluate) {
1131 needToReevaluate = false;
1134 return evaluatedItemsIgnoringCondition;
1138 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
1140 // FIXME: do we need to do this here?
1141 if (needToReevaluate) {
1142 needToReevaluate = false;
1145 return evaluatedItemsByName;
1149 internal IEnumerable EvaluatedItemsByNameAsDictionaryEntries {
1151 if (EvaluatedItemsByName.Count == 0)
1154 foreach (KeyValuePair<string, BuildItemGroup> pair in EvaluatedItemsByName) {
1155 foreach (BuildItem bi in pair.Value)
1156 yield return new DictionaryEntry (pair.Key, bi.ConvertToITaskItem (null, ExpressionOptions.ExpandItemRefs));
1161 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
1163 // FIXME: do we need to do this here?
1164 if (needToReevaluate) {
1165 needToReevaluate = false;
1168 return evaluatedItemsByNameIgnoringCondition;
1172 // For batching implementation
1173 Dictionary<string, BuildItemGroup> perBatchItemsByName;
1174 Dictionary<string, BuildItemGroup> commonItemsByName;
1177 public Dictionary<string, BuildItemGroup> perBatchItemsByName;
1178 public Dictionary<string, BuildItemGroup> commonItemsByName;
1180 public Batch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1182 this.perBatchItemsByName = perBatchItemsByName;
1183 this.commonItemsByName = commonItemsByName;
1187 Stack<Batch> Batches {
1188 get { return batches; }
1191 internal void PushBatch (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1193 batches.Push (new Batch (perBatchItemsByName, commonItemsByName));
1194 SetBatchedItems (perBatchItemsByName, commonItemsByName);
1197 internal void PopBatch ()
1200 if (batches.Count > 0) {
1201 Batch b = batches.Peek ();
1202 SetBatchedItems (b.perBatchItemsByName, b.commonItemsByName);
1204 SetBatchedItems (null, null);
1208 void SetBatchedItems (Dictionary<string, BuildItemGroup> perBatchItemsByName, Dictionary<string, BuildItemGroup> commonItemsByName)
1210 this.perBatchItemsByName = perBatchItemsByName;
1211 this.commonItemsByName = commonItemsByName;
1215 internal bool TryGetEvaluatedItemByNameBatched (string itemName, out BuildItemGroup group)
1217 if (perBatchItemsByName != null && perBatchItemsByName.TryGetValue (itemName, out group))
1220 if (commonItemsByName != null && commonItemsByName.TryGetValue (itemName, out group))
1224 return EvaluatedItemsByName.TryGetValue (itemName, out group);
1227 internal string GetMetadataBatched (string itemName, string metadataName)
1229 BuildItemGroup group = null;
1230 if (itemName == null) {
1231 //unqualified, all items in a batch(bucket) have the
1232 //same metadata values
1233 group = GetFirst<BuildItemGroup> (perBatchItemsByName.Values);
1235 group = GetFirst<BuildItemGroup> (commonItemsByName.Values);
1238 TryGetEvaluatedItemByNameBatched (itemName, out group);
1241 if (group != null) {
1242 foreach (BuildItem item in group) {
1243 if (item.HasMetadata (metadataName))
1244 return item.GetEvaluatedMetadata (metadataName);
1247 return String.Empty;
1250 internal IEnumerable<BuildItemGroup> GetAllItemGroups ()
1252 if (perBatchItemsByName == null && commonItemsByName == null)
1253 foreach (BuildItemGroup group in EvaluatedItemsByName.Values)
1256 if (perBatchItemsByName != null)
1257 foreach (BuildItemGroup group in perBatchItemsByName.Values)
1260 if (commonItemsByName != null)
1261 foreach (BuildItemGroup group in commonItemsByName.Values)
1265 T GetFirst<T> (ICollection<T> list)
1270 foreach (T t in list)
1276 // Used for MSBuild*This* set of properties
1277 internal void PushThisFileProperty (string full_filename)
1279 string last_file = this_file_property_stack.Count == 0 ? String.Empty : this_file_property_stack.Peek ();
1280 this_file_property_stack.Push (full_filename);
1281 if (last_file != full_filename)
1282 // first time, or different from previous one
1283 SetMSBuildThisFileProperties (full_filename);
1286 internal void PopThisFileProperty ()
1288 string last_file = this_file_property_stack.Pop ();
1289 if (this_file_property_stack.Count > 0 && last_file != this_file_property_stack.Peek ())
1290 SetMSBuildThisFileProperties (this_file_property_stack.Peek ());
1293 void SetMSBuildThisFileProperties (string full_filename)
1295 if (String.IsNullOrEmpty (full_filename))
1298 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFile", Path.GetFileName (full_filename), PropertyType.Reserved));
1299 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileFullPath", full_filename, PropertyType.Reserved));
1300 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileName", Path.GetFileNameWithoutExtension (full_filename), PropertyType.Reserved));
1301 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileExtension", Path.GetExtension (full_filename), PropertyType.Reserved));
1303 string project_dir = Path.GetDirectoryName (full_filename) + Path.DirectorySeparatorChar;
1304 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectory", project_dir, PropertyType.Reserved));
1305 evaluatedProperties.AddProperty (new BuildProperty ("MSBuildThisFileDirectoryNoRoot",
1306 project_dir.Substring (Path.GetPathRoot (project_dir).Length),
1307 PropertyType.Reserved));
1311 internal void LogWarning (string filename, string message, params object[] messageArgs)
1313 BuildWarningEventArgs bwea = new BuildWarningEventArgs (
1314 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1316 ParentEngine.EventSource.FireWarningRaised (this, bwea);
1319 internal void LogError (string filename, string message,
1320 params object[] messageArgs)
1322 BuildErrorEventArgs beea = new BuildErrorEventArgs (
1323 null, null, filename, 0, 0, 0, 0, String.Format (message, messageArgs),
1325 ParentEngine.EventSource.FireErrorRaised (this, beea);
1328 static string ExtensionsPath {
1330 if (extensions_path == null) {
1331 // NOTE: code from mcs/tools/gacutil/driver.cs
1332 PropertyInfo gac = typeof (System.Environment).GetProperty (
1333 "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
1336 MethodInfo get_gac = gac.GetGetMethod (true);
1337 string gac_path = (string) get_gac.Invoke (null, null);
1338 extensions_path = Path.GetFullPath (Path.Combine (
1339 gac_path, Path.Combine ("..", "xbuild")));
1342 return extensions_path;
1346 public BuildPropertyGroup EvaluatedProperties {
1348 if (needToReevaluate) {
1349 needToReevaluate = false;
1352 return evaluatedProperties;
1356 internal IEnumerable EvaluatedPropertiesAsDictionaryEntries {
1358 foreach (BuildProperty bp in EvaluatedProperties)
1359 yield return new DictionaryEntry (bp.Name, bp.Value);
1363 public string FullFileName {
1364 get { return fullFileName; }
1365 set { fullFileName = value; }
1368 public BuildPropertyGroup GlobalProperties {
1369 get { return globalProperties; }
1372 throw new ArgumentNullException ("value");
1375 throw new InvalidOperationException ("GlobalProperties can not be set to persisted property group.");
1377 globalProperties = value;
1381 public bool IsDirty {
1382 get { return isDirty; }
1385 public bool IsValidated {
1386 get { return isValidated; }
1387 set { isValidated = value; }
1390 public BuildItemGroupCollection ItemGroups {
1391 get { return itemGroups; }
1394 public ImportCollection Imports {
1395 get { return imports; }
1398 public string InitialTargets {
1400 return String.Join ("; ", initialTargets.ToArray ());
1403 initialTargets.Clear ();
1404 xmlDocument.DocumentElement.SetAttribute ("InitialTargets", value);
1406 initialTargets.AddRange (value.Split (
1407 new char [] {';', ' '}, StringSplitOptions.RemoveEmptyEntries));
1411 public Engine ParentEngine {
1412 get { return parentEngine; }
1415 public BuildPropertyGroupCollection PropertyGroups {
1416 get { return propertyGroups; }
1419 public string SchemaFile {
1420 get { return schemaFile; }
1421 set { schemaFile = value; }
1424 public TargetCollection Targets {
1425 get { return targets; }
1428 public DateTime TimeOfLastDirty {
1429 get { return timeOfLastDirty; }
1432 public UsingTaskCollection UsingTasks {
1433 get { return usingTasks; }
1438 get { return xmlDocument.InnerXml; }
1441 // corresponds to the xml attribute
1442 public string DefaultToolsVersion {
1444 if (xmlDocument != null)
1445 return xmlDocument.DocumentElement.GetAttribute ("ToolsVersion");
1449 if (xmlDocument != null)
1450 xmlDocument.DocumentElement.SetAttribute ("ToolsVersion", value);
1454 public bool HasToolsVersionAttribute {
1456 return xmlDocument != null && xmlDocument.DocumentElement.HasAttribute ("ToolsVersion");
1460 public string ToolsVersion {
1464 internal Dictionary <string, BuildItemGroup> LastItemGroupContaining {
1465 get { return last_item_group_containing; }
1468 internal static XmlNamespaceManager XmlNamespaceManager {
1470 if (manager == null) {
1471 manager = new XmlNamespaceManager (new NameTable ());
1472 manager.AddNamespace ("tns", ns);
1479 internal TaskDatabase TaskDatabase {
1480 get { return taskDatabase; }
1483 internal XmlDocument XmlDocument {
1484 get { return xmlDocument; }
1487 internal static string XmlNamespace {
1493 PlatformID pid = Environment.OSVersion.Platform;
1501 return "Windows_NT";