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 parentEngine = engine;
83 xmlDocument = new XmlDocument ();
84 groupingCollection = new GroupingCollection (this);
85 imports = new ImportCollection (groupingCollection);
86 usingTasks = new UsingTaskCollection (this);
87 itemGroups = new BuildItemGroupCollection (groupingCollection);
88 propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
89 targets = new TargetCollection (this);
91 taskDatabase = new TaskDatabase ();
92 if (engine.DefaultTasksRegistered) {
93 taskDatabase.CopyTasks (engine.DefaultTasks);
96 globalProperties = new BuildPropertyGroup ();
97 fullFileName = String.Empty;
99 foreach (BuildProperty bp in parentEngine.GlobalProperties) {
100 GlobalProperties.AddProperty (bp.Clone (true));
103 // You can evaluate an empty project.
108 public void AddNewImport (string importLocation,
109 string importCondition)
111 throw new NotImplementedException ();
115 public BuildItem AddNewItem (string itemName,
118 return AddNewItem (itemName, itemInclude, false);
122 public BuildItem AddNewItem (string itemName,
124 bool treatItemIncludeAsLiteral)
126 throw new NotImplementedException ();
130 public BuildItemGroup AddNewItemGroup ()
132 throw new NotImplementedException ();
136 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
138 throw new NotImplementedException ();
142 public void AddNewUsingTaskFromAssemblyFile (string taskName,
145 throw new NotImplementedException ();
149 public void AddNewUsingTaskFromAssemblyName (string taskName,
152 throw new NotImplementedException ();
162 public bool Build (string targetName)
164 return Build (new string [1] { targetName });
168 public bool Build (string[] targetNames)
170 return Build (targetNames, null);
174 public bool Build (string[] targetNames,
175 IDictionary targetOutputs)
177 return Build (targetNames, targetOutputs, BuildSettings.None);
181 public bool Build (string[] targetNames,
182 IDictionary targetOutputs,
183 BuildSettings buildFlags)
188 if (targetNames.Length == 0) {
189 if (defaultTargets != null && defaultTargets.Length != 0)
190 targetNames = defaultTargets;
191 else if (firstTargetName != null)
192 targetNames = new string [1] { firstTargetName};
197 foreach (string target in targetNames) {
198 if (!targets.Exists (target))
199 // FIXME: test if it's logged
202 if (!targets [target].Build ())
210 public string[] GetConditionedPropertyValues (string propertyName)
212 if (conditionedProperties.ContainsKey (propertyName))
213 return conditionedProperties [propertyName].ToArray ();
215 return new string [0];
218 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
220 if (evaluatedItemsByName.ContainsKey (itemName))
221 return evaluatedItemsByName [itemName];
223 return new BuildItemGroup ();
226 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
228 if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
229 return evaluatedItemsByNameIgnoringCondition [itemName];
231 return new BuildItemGroup ();
234 public string GetEvaluatedProperty (string propertyName)
236 if (propertyName == null)
237 throw new ArgumentNullException ("propertyName");
239 BuildProperty bp = evaluatedProperties [propertyName];
241 return bp == null ? null : (string) bp;
244 public string GetProjectExtensions (string id)
246 if (id == null || id == String.Empty)
249 XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
254 return node.InnerXml;
258 public void Load (string projectFileName)
260 this.fullFileName = Path.GetFullPath (projectFileName);
262 DoLoad (new StreamReader (projectFileName));
264 Console.WriteLine ("Failure to load: {0}", projectFileName);
269 public void Load (TextReader textReader)
271 fullFileName = String.Empty;
275 public void LoadXml (string projectXml)
277 fullFileName = String.Empty;
278 DoLoad (new StringReader (projectXml));
282 public void MarkProjectAsDirty ()
288 public void RemoveAllItemGroups ()
290 throw new NotImplementedException ();
294 public void RemoveAllPropertyGroups ()
296 throw new NotImplementedException ();
300 public void RemoveItem (BuildItem itemToRemove)
302 throw new NotImplementedException ();
306 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
308 throw new NotImplementedException ();
312 // NOTE: does not modify imported projects
313 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
315 throw new NotImplementedException ();
319 public void RemoveItemsByName (string itemName)
321 throw new NotImplementedException ();
325 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
327 throw new NotImplementedException ();
331 // NOTE: does not modify imported projects
332 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
334 throw new NotImplementedException ();
338 public void ResetBuildStatus ()
340 throw new NotImplementedException ();
343 public void Save (string projectFileName)
345 Save (projectFileName, Encoding.Default);
348 public void Save (string projectFileName, Encoding encoding)
350 xmlDocument.Save (projectFileName);
353 public void Save (TextWriter outTextWriter)
355 xmlDocument.Save (outTextWriter);
358 public void SetImportedProperty (string propertyName,
359 string propertyValue,
361 Project importProject)
363 SetImportedProperty (propertyName, propertyValue, condition, importProject,
364 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
367 public void SetImportedProperty (string propertyName,
368 string propertyValue,
370 Project importedProject,
371 PropertyPosition position)
373 SetImportedProperty (propertyName, propertyValue, condition, importedProject,
374 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
378 public void SetImportedProperty (string propertyName,
379 string propertyValue,
381 Project importedProject,
382 PropertyPosition position,
383 bool treatPropertyValueAsLiteral)
385 throw new NotImplementedException ();
388 public void SetProjectExtensions (string id, string xmlText)
390 XmlNode projectExtensions, node;
392 if (id == null || id == String.Empty || xmlText == null)
395 projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
398 if (projectExtensions == null) {
399 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
400 xmlDocument.DocumentElement.AppendChild (projectExtensions);
402 node = xmlDocument.CreateElement (id, XmlNamespace);
403 node.InnerXml = xmlText;
404 projectExtensions.AppendChild (node);
406 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
409 node = xmlDocument.CreateElement (id, XmlNamespace);
410 projectExtensions.AppendChild (node);
413 node.InnerXml = xmlText;
419 public void SetProperty (string propertyName,
420 string propertyValue)
422 SetProperty (propertyName, propertyValue, "true",
423 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
427 public void SetProperty (string propertyName,
428 string propertyValue,
431 SetProperty (propertyName, propertyValue, condition,
432 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
436 public void SetProperty (string propertyName,
437 string propertyValue,
439 PropertyPosition position)
441 SetProperty (propertyName, propertyValue, condition,
442 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
446 public void SetProperty (string propertyName,
447 string propertyValue,
449 PropertyPosition position,
450 bool treatPropertyValueAsLiteral)
452 throw new NotImplementedException ();
455 internal void Unload ()
460 internal void CheckUnloaded ()
463 throw new InvalidOperationException ("This project object is no longer valid.");
466 // Does the actual loading.
467 private void DoLoad (TextReader textReader)
469 ParentEngine.RemoveLoadedProject (this);
471 XmlReaderSettings settings = new XmlReaderSettings ();
473 if (SchemaFile != null) {
474 settings.Schemas.Add (null, SchemaFile);
475 settings.ValidationType = ValidationType.Schema;
476 settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
479 XmlReader xmlReader = XmlReader.Create (textReader, settings);
480 xmlDocument.Load (xmlReader);
482 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
483 throw new InvalidProjectFileException (
484 @"The default XML namespace of the project must be the MSBuild XML namespace." +
485 " If the project is authored in the MSBuild 2003 format, please add " +
486 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
487 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
490 ParentEngine.AddLoadedProject (this);
493 private void ProcessXml ()
495 XmlElement xmlElement = xmlDocument.DocumentElement;
496 if (xmlElement.Name != "Project")
497 throw new InvalidProjectFileException ("Invalid root element.");
498 if (xmlElement.GetAttributeNode ("DefaultTargets") != null)
499 defaultTargets = xmlElement.GetAttribute ("DefaultTargets").Split (';');
501 defaultTargets = new string [0];
503 ProcessElements (xmlElement, null);
509 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
511 foreach (XmlNode xn in rootElement.ChildNodes) {
512 if (xn is XmlElement) {
513 XmlElement xe = (XmlElement) xn;
515 case "ProjectExtensions":
516 AddProjectExtensions (xe);
527 AddUsingTask (xe, ip);
535 case "PropertyGroup":
536 AddPropertyGroup (xe);
542 throw new InvalidProjectFileException ("Invalid element in project file.");
548 internal void Evaluate ()
550 evaluatedItems = new BuildItemGroup (null, this);
551 evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this);
552 evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
553 evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
554 evaluatedProperties = new BuildPropertyGroup ();
556 InitializeProperties ();
558 groupingCollection.Evaluate ();
560 //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
561 foreach (UsingTask usingTask in UsingTasks)
562 usingTask.Evaluate ();
565 private void InitializeProperties ()
569 foreach (BuildProperty gp in GlobalProperties) {
570 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
571 EvaluatedProperties.AddProperty (bp);
574 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
575 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
576 EvaluatedProperties.AddProperty (bp);
579 bp = new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved);
580 EvaluatedProperties.AddProperty (bp);
583 private void AddProjectExtensions (XmlElement xmlElement)
585 if (xmlElement == null)
586 throw new ArgumentNullException ("xmlElement");
589 private void AddMessage (XmlElement xmlElement)
591 if (xmlElement == null)
592 throw new ArgumentNullException ("xmlElement");
595 private void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
597 if (xmlElement == null)
598 throw new ArgumentNullException ("xmlElement");
599 Target target = new Target (xmlElement, this);
600 targets.AddTarget (target);
601 if (importedProject == null) {
602 target.IsImported = false;
603 if (firstTargetName == null)
604 firstTargetName = target.Name;
606 target.IsImported = true;
609 private void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
613 usingTask = new UsingTask (xmlElement, this, importedProject);
614 UsingTasks.Add (usingTask);
617 private void AddImport (XmlElement xmlElement, ImportedProject importingProject)
621 import = new Import (xmlElement, this, importingProject);
622 Imports.Add (import);
625 private void AddItemGroup (XmlElement xmlElement)
627 if (xmlElement == null)
628 throw new ArgumentNullException ("xmlElement");
629 BuildItemGroup big = new BuildItemGroup (xmlElement, this);
630 ItemGroups.Add (big);
633 private void AddPropertyGroup (XmlElement xmlElement)
635 if (xmlElement == null)
636 throw new ArgumentNullException ("xmlElement");
637 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this);
638 PropertyGroups.Add (bpg);
641 private void AddChoose (XmlElement xmlElement)
643 if (xmlElement == null)
644 throw new ArgumentNullException ("xmlElement");
646 BuildChoose bc = new BuildChoose (xmlElement, this);
647 groupingCollection.Add (bc);
651 private static void ValidationCallBack (object sender, ValidationEventArgs e)
653 Console.WriteLine ("Validation Error: {0}", e.Message);
656 public bool BuildEnabled {
661 buildEnabled = value;
665 public Encoding Encoding {
666 get { return encoding; }
669 public string DefaultTargets {
671 return xmlDocument.DocumentElement.GetAttribute ("DefaultTargets");
674 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
675 defaultTargets = value.Split (';');
679 public BuildItemGroup EvaluatedItems {
680 get { return evaluatedItems; }
683 public BuildItemGroup EvaluatedItemsIgnoringCondition {
684 get { return evaluatedItemsIgnoringCondition; }
687 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
688 get { return evaluatedItemsByName; }
691 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
692 get { return evaluatedItemsByNameIgnoringCondition; }
695 public BuildPropertyGroup EvaluatedProperties {
696 get { return evaluatedProperties; }
699 public string FullFileName {
700 get { return fullFileName; }
701 set { fullFileName = value; }
704 public BuildPropertyGroup GlobalProperties {
705 get { return globalProperties; }
708 throw new ArgumentNullException ("value");
711 throw new InvalidOperationException ("Can't do that.");
713 globalProperties = value;
717 public bool IsDirty {
718 get { return isDirty; }
721 public bool IsValidated {
722 get { return isValidated; }
723 set { isValidated = value; }
726 public BuildItemGroupCollection ItemGroups {
727 get { return itemGroups; }
730 public ImportCollection Imports {
731 get { return imports; }
734 public string InitialTargets {
735 get { return initialTargets; }
736 set { initialTargets = value; }
739 public Engine ParentEngine {
740 get { return parentEngine; }
743 public BuildPropertyGroupCollection PropertyGroups {
744 get { return propertyGroups; }
747 public string SchemaFile {
748 get { return schemaFile; }
749 set { schemaFile = value; }
752 public TargetCollection Targets {
753 get { return targets; }
756 public DateTime TimeOfLastDirty {
757 get { return timeOfLastDirty; }
760 public UsingTaskCollection UsingTasks {
761 get { return usingTasks; }
766 get { return xmlDocument.InnerXml; }
769 internal static XmlNamespaceManager XmlNamespaceManager {
771 if (manager == null) {
772 manager = new XmlNamespaceManager (new NameTable ());
773 manager.AddNamespace ("tns", ns);
780 internal TaskDatabase TaskDatabase {
781 get { return taskDatabase; }
784 internal XmlDocument XmlDocument {
785 get { return xmlDocument; }
788 internal static string XmlNamespace {