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;
37 using System.Xml.Schema;
38 using Microsoft.Build.Framework;
39 using Mono.XBuild.Framework;
41 namespace Microsoft.Build.BuildEngine {
42 public class Project {
45 Dictionary <string, List <string>> conditionedProperties;
46 string[] defaultTargets;
48 BuildItemGroup evaluatedItems;
49 BuildItemGroup evaluatedItemsIgnoringCondition;
50 Dictionary <string, BuildItemGroup> evaluatedItemsByName;
51 Dictionary <string, BuildItemGroup> evaluatedItemsByNameIgnoringCondition;
52 BuildPropertyGroup evaluatedProperties;
53 string firstTargetName;
55 BuildPropertyGroup globalProperties;
56 GroupingCollection groupingCollection;
59 BuildItemGroupCollection itemGroups;
60 ImportCollection imports;
61 string initialTargets;
63 BuildPropertyGroupCollection propertyGroups;
65 TaskDatabase taskDatabase;
66 TargetCollection targets;
67 DateTime timeOfLastDirty;
68 UsingTaskCollection usingTasks;
69 XmlDocument xmlDocument;
72 static XmlNamespaceManager manager;
73 static string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
76 : this (Engine.GlobalEngine)
80 public Project (Engine engine)
82 buildEnabled = engine.BuildEnabled;
83 parentEngine = engine;
84 xmlDocument = new XmlDocument ();
85 groupingCollection = new GroupingCollection (this);
86 imports = new ImportCollection (groupingCollection);
87 usingTasks = new UsingTaskCollection (this);
88 itemGroups = new BuildItemGroupCollection (groupingCollection);
89 propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
90 targets = new TargetCollection (this);
92 taskDatabase = new TaskDatabase ();
93 if (engine.DefaultTasksRegistered) {
94 taskDatabase.CopyTasks (engine.DefaultTasks);
97 globalProperties = new BuildPropertyGroup ();
98 fullFileName = String.Empty;
100 foreach (BuildProperty bp in parentEngine.GlobalProperties) {
101 GlobalProperties.AddProperty (bp.Clone (true));
104 // You can evaluate an empty project.
109 public void AddNewImport (string importLocation,
110 string importCondition)
112 throw new NotImplementedException ();
116 public BuildItem AddNewItem (string itemName,
119 return AddNewItem (itemName, itemInclude, false);
123 public BuildItem AddNewItem (string itemName,
125 bool treatItemIncludeAsLiteral)
127 throw new NotImplementedException ();
131 public BuildItemGroup AddNewItemGroup ()
133 throw new NotImplementedException ();
137 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
139 throw new NotImplementedException ();
143 public void AddNewUsingTaskFromAssemblyFile (string taskName,
146 throw new NotImplementedException ();
150 public void AddNewUsingTaskFromAssemblyName (string taskName,
153 throw new NotImplementedException ();
163 public bool Build (string targetName)
165 return Build (new string [1] { targetName });
169 public bool Build (string[] targetNames)
171 return Build (targetNames, null);
175 public bool Build (string[] targetNames,
176 IDictionary targetOutputs)
178 return Build (targetNames, targetOutputs, BuildSettings.None);
182 public bool Build (string[] targetNames,
183 IDictionary targetOutputs,
184 BuildSettings buildFlags)
189 if (targetNames.Length == 0) {
190 if (defaultTargets != null && defaultTargets.Length != 0)
191 targetNames = defaultTargets;
192 else if (firstTargetName != null)
193 targetNames = new string [1] { firstTargetName};
198 foreach (string target in targetNames) {
199 if (!targets.Exists (target))
200 // FIXME: test if it's logged
203 if (!targets [target].Build ())
211 public string[] GetConditionedPropertyValues (string propertyName)
213 if (conditionedProperties.ContainsKey (propertyName))
214 return conditionedProperties [propertyName].ToArray ();
216 return new string [0];
219 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
221 if (evaluatedItemsByName.ContainsKey (itemName))
222 return evaluatedItemsByName [itemName];
224 return new BuildItemGroup ();
227 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
229 if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
230 return evaluatedItemsByNameIgnoringCondition [itemName];
232 return new BuildItemGroup ();
235 public string GetEvaluatedProperty (string propertyName)
237 if (propertyName == null)
238 throw new ArgumentNullException ("propertyName");
240 BuildProperty bp = evaluatedProperties [propertyName];
242 return bp == null ? null : (string) bp;
245 public string GetProjectExtensions (string id)
247 if (id == null || id == String.Empty)
250 XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
255 return node.InnerXml;
259 public void Load (string projectFileName)
261 this.fullFileName = Path.GetFullPath (projectFileName);
262 DoLoad (new StreamReader (projectFileName));
266 public void Load (TextReader textReader)
268 fullFileName = String.Empty;
272 public void LoadXml (string projectXml)
274 fullFileName = String.Empty;
275 DoLoad (new StringReader (projectXml));
276 MarkProjectAsDirty ();
280 public void MarkProjectAsDirty ()
283 timeOfLastDirty = DateTime.Now;
287 public void RemoveAllItemGroups ()
289 throw new NotImplementedException ();
293 public void RemoveAllPropertyGroups ()
295 throw new NotImplementedException ();
299 public void RemoveItem (BuildItem itemToRemove)
301 throw new NotImplementedException ();
305 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
307 throw new NotImplementedException ();
311 // NOTE: does not modify imported projects
312 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
314 throw new NotImplementedException ();
318 public void RemoveItemsByName (string itemName)
320 throw new NotImplementedException ();
324 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
326 throw new NotImplementedException ();
330 // NOTE: does not modify imported projects
331 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
333 throw new NotImplementedException ();
337 public void ResetBuildStatus ()
339 throw new NotImplementedException ();
342 public void Save (string projectFileName)
344 Save (projectFileName, Encoding.Default);
347 public void Save (string projectFileName, Encoding encoding)
349 xmlDocument.Save (projectFileName);
352 public void Save (TextWriter outTextWriter)
354 xmlDocument.Save (outTextWriter);
357 public void SetImportedProperty (string propertyName,
358 string propertyValue,
360 Project importProject)
362 SetImportedProperty (propertyName, propertyValue, condition, importProject,
363 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
366 public void SetImportedProperty (string propertyName,
367 string propertyValue,
369 Project importedProject,
370 PropertyPosition position)
372 SetImportedProperty (propertyName, propertyValue, condition, importedProject,
373 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
377 public void SetImportedProperty (string propertyName,
378 string propertyValue,
380 Project importedProject,
381 PropertyPosition position,
382 bool treatPropertyValueAsLiteral)
384 throw new NotImplementedException ();
387 public void SetProjectExtensions (string id, string xmlText)
389 XmlNode projectExtensions, node;
391 if (id == null || id == String.Empty || xmlText == null)
394 projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
397 if (projectExtensions == null) {
398 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
399 xmlDocument.DocumentElement.AppendChild (projectExtensions);
401 node = xmlDocument.CreateElement (id, XmlNamespace);
402 node.InnerXml = xmlText;
403 projectExtensions.AppendChild (node);
405 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
408 node = xmlDocument.CreateElement (id, XmlNamespace);
409 projectExtensions.AppendChild (node);
412 node.InnerXml = xmlText;
416 MarkProjectAsDirty ();
420 public void SetProperty (string propertyName,
421 string propertyValue)
423 SetProperty (propertyName, propertyValue, "true",
424 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
428 public void SetProperty (string propertyName,
429 string propertyValue,
432 SetProperty (propertyName, propertyValue, condition,
433 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
437 public void SetProperty (string propertyName,
438 string propertyValue,
440 PropertyPosition position)
442 SetProperty (propertyName, propertyValue, condition,
443 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
447 public void SetProperty (string propertyName,
448 string propertyValue,
450 PropertyPosition position,
451 bool treatPropertyValueAsLiteral)
453 throw new NotImplementedException ();
456 internal void Unload ()
461 internal void CheckUnloaded ()
464 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
467 // Does the actual loading.
468 void DoLoad (TextReader textReader)
471 ParentEngine.RemoveLoadedProject (this);
473 XmlReaderSettings settings = new XmlReaderSettings ();
475 if (SchemaFile != null) {
476 settings.Schemas.Add (null, SchemaFile);
477 settings.ValidationType = ValidationType.Schema;
478 settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
481 XmlReader xmlReader = XmlReader.Create (textReader, settings);
482 xmlDocument.Load (xmlReader);
484 if (xmlDocument.DocumentElement.Name != "Project") {
485 throw new InvalidProjectFileException (String.Format (
486 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
489 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
490 throw new InvalidProjectFileException (
491 @"The default XML namespace of the project must be the MSBuild XML namespace." +
492 " If the project is authored in the MSBuild 2003 format, please add " +
493 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
494 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
497 ParentEngine.AddLoadedProject (this);
498 } catch (Exception e) {
499 throw new InvalidProjectFileException (e.Message, e);
505 XmlElement xmlElement = xmlDocument.DocumentElement;
506 if (xmlElement.Name != "Project")
507 throw new InvalidProjectFileException ("Invalid root element.");
508 if (xmlElement.GetAttributeNode ("DefaultTargets") != null)
509 defaultTargets = xmlElement.GetAttribute ("DefaultTargets").Split (';');
511 defaultTargets = new string [0];
513 ProcessElements (xmlElement, null);
519 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
521 foreach (XmlNode xn in rootElement.ChildNodes) {
522 if (xn is XmlElement) {
523 XmlElement xe = (XmlElement) xn;
525 case "ProjectExtensions":
526 AddProjectExtensions (xe);
537 AddUsingTask (xe, ip);
543 AddItemGroup (xe, ip);
545 case "PropertyGroup":
546 AddPropertyGroup (xe, ip);
552 throw new InvalidProjectFileException ("Invalid element in project file.");
558 internal void Evaluate ()
560 evaluatedItems = new BuildItemGroup (null, this, null);
561 evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null);
562 evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
563 evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
564 evaluatedProperties = new BuildPropertyGroup ();
566 InitializeProperties ();
568 groupingCollection.Evaluate ();
570 //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
571 foreach (UsingTask usingTask in UsingTasks)
572 usingTask.Evaluate ();
575 private void InitializeProperties ()
579 foreach (BuildProperty gp in GlobalProperties) {
580 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
581 EvaluatedProperties.AddProperty (bp);
584 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
585 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
586 EvaluatedProperties.AddProperty (bp);
589 bp = new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved);
590 EvaluatedProperties.AddProperty (bp);
593 void AddProjectExtensions (XmlElement xmlElement)
597 void AddMessage (XmlElement xmlElement)
601 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
603 Target target = new Target (xmlElement, this, importedProject);
604 targets.AddTarget (target);
606 if (firstTargetName == null)
607 firstTargetName = target.Name;
610 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
614 usingTask = new UsingTask (xmlElement, this, importedProject);
615 UsingTasks.Add (usingTask);
618 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
622 import = new Import (xmlElement, this, importingProject);
623 Imports.Add (import);
626 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
628 BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject);
629 ItemGroups.Add (big);
632 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
634 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject);
635 PropertyGroups.Add (bpg);
638 void AddChoose (XmlElement xmlElement)
640 BuildChoose bc = new BuildChoose (xmlElement, this);
641 groupingCollection.Add (bc);
645 static void ValidationCallBack (object sender, ValidationEventArgs e)
647 Console.WriteLine ("Validation Error: {0}", e.Message);
650 public bool BuildEnabled {
655 buildEnabled = value;
659 public Encoding Encoding {
660 get { return encoding; }
663 public string DefaultTargets {
665 return xmlDocument.DocumentElement.GetAttribute ("DefaultTargets");
668 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
669 defaultTargets = value.Split (';');
673 public BuildItemGroup EvaluatedItems {
674 get { return evaluatedItems; }
677 public BuildItemGroup EvaluatedItemsIgnoringCondition {
678 get { return evaluatedItemsIgnoringCondition; }
681 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
682 get { return evaluatedItemsByName; }
685 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
686 get { return evaluatedItemsByNameIgnoringCondition; }
689 public BuildPropertyGroup EvaluatedProperties {
690 get { return evaluatedProperties; }
693 public string FullFileName {
694 get { return fullFileName; }
695 set { fullFileName = value; }
698 public BuildPropertyGroup GlobalProperties {
699 get { return globalProperties; }
702 throw new ArgumentNullException ("value");
705 throw new InvalidOperationException ("Can't do that.");
707 globalProperties = value;
711 public bool IsDirty {
712 get { return isDirty; }
715 public bool IsValidated {
716 get { return isValidated; }
717 set { isValidated = value; }
720 public BuildItemGroupCollection ItemGroups {
721 get { return itemGroups; }
724 public ImportCollection Imports {
725 get { return imports; }
728 public string InitialTargets {
729 get { return initialTargets; }
730 set { initialTargets = value; }
733 public Engine ParentEngine {
734 get { return parentEngine; }
737 public BuildPropertyGroupCollection PropertyGroups {
738 get { return propertyGroups; }
741 public string SchemaFile {
742 get { return schemaFile; }
743 set { schemaFile = value; }
746 public TargetCollection Targets {
747 get { return targets; }
750 public DateTime TimeOfLastDirty {
751 get { return timeOfLastDirty; }
754 public UsingTaskCollection UsingTasks {
755 get { return usingTasks; }
760 get { return xmlDocument.InnerXml; }
763 internal static XmlNamespaceManager XmlNamespaceManager {
765 if (manager == null) {
766 manager = new XmlNamespaceManager (new NameTable ());
767 manager.AddNamespace ("tns", ns);
774 internal TaskDatabase TaskDatabase {
775 get { return taskDatabase; }
778 internal XmlDocument XmlDocument {
779 get { return xmlDocument; }
782 internal static string XmlNamespace {