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;
84 buildEnabled = ParentEngine.BuildEnabled;
85 xmlDocument = new XmlDocument ();
86 xmlDocument.AppendChild (xmlDocument.CreateElement ("Project", XmlNamespace));
87 xmlDocument.DocumentElement.SetAttribute ("xmlns", ns);
89 fullFileName = String.Empty;
91 globalProperties = new BuildPropertyGroup (null, this, null, false);
92 foreach (BuildProperty bp in parentEngine.GlobalProperties)
93 GlobalProperties.AddProperty (bp.Clone (true));
99 public void AddNewImport (string importLocation,
100 string importCondition)
102 throw new NotImplementedException ();
106 public BuildItem AddNewItem (string itemName,
109 return AddNewItem (itemName, itemInclude, false);
113 public BuildItem AddNewItem (string itemName,
115 bool treatItemIncludeAsLiteral)
117 throw new NotImplementedException ();
121 public BuildItemGroup AddNewItemGroup ()
123 throw new NotImplementedException ();
127 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
129 throw new NotImplementedException ();
133 public void AddNewUsingTaskFromAssemblyFile (string taskName,
136 throw new NotImplementedException ();
140 public void AddNewUsingTaskFromAssemblyName (string taskName,
143 throw new NotImplementedException ();
153 public bool Build (string targetName)
155 return Build (new string [1] { targetName });
159 public bool Build (string[] targetNames)
161 return Build (targetNames, null);
165 public bool Build (string[] targetNames,
166 IDictionary targetOutputs)
168 return Build (targetNames, targetOutputs, BuildSettings.None);
172 public bool Build (string[] targetNames,
173 IDictionary targetOutputs,
174 BuildSettings buildFlags)
179 if (targetNames.Length == 0) {
180 if (defaultTargets != null && defaultTargets.Length != 0)
181 targetNames = defaultTargets;
182 else if (firstTargetName != null)
183 targetNames = new string [1] { firstTargetName};
188 foreach (string target in targetNames) {
189 if (!targets.Exists (target))
190 // FIXME: test if it's logged
193 if (!targets [target].Build ())
201 public string[] GetConditionedPropertyValues (string propertyName)
203 if (conditionedProperties.ContainsKey (propertyName))
204 return conditionedProperties [propertyName].ToArray ();
206 return new string [0];
209 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
211 if (evaluatedItemsByName.ContainsKey (itemName))
212 return evaluatedItemsByName [itemName];
214 return new BuildItemGroup ();
217 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
219 if (evaluatedItemsByNameIgnoringCondition.ContainsKey (itemName))
220 return evaluatedItemsByNameIgnoringCondition [itemName];
222 return new BuildItemGroup ();
225 public string GetEvaluatedProperty (string propertyName)
227 if (propertyName == null)
228 throw new ArgumentNullException ("propertyName");
230 BuildProperty bp = evaluatedProperties [propertyName];
232 return bp == null ? null : (string) bp;
235 public string GetProjectExtensions (string id)
237 if (id == null || id == String.Empty)
240 XmlNode node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
245 return node.InnerXml;
249 public void Load (string projectFileName)
251 this.fullFileName = Path.GetFullPath (projectFileName);
252 DoLoad (new StreamReader (projectFileName));
256 public void Load (TextReader textReader)
258 fullFileName = String.Empty;
262 public void LoadXml (string projectXml)
264 fullFileName = String.Empty;
265 DoLoad (new StringReader (projectXml));
266 MarkProjectAsDirty ();
270 public void MarkProjectAsDirty ()
273 timeOfLastDirty = DateTime.Now;
277 public void RemoveAllItemGroups ()
279 throw new NotImplementedException ();
283 public void RemoveAllPropertyGroups ()
285 throw new NotImplementedException ();
289 public void RemoveItem (BuildItem itemToRemove)
291 throw new NotImplementedException ();
295 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
297 throw new NotImplementedException ();
301 // NOTE: does not modify imported projects
302 public void RemoveItemGroupsWithMatchingCondition (string matchingCondition)
304 throw new NotImplementedException ();
308 public void RemoveItemsByName (string itemName)
310 throw new NotImplementedException ();
314 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
316 throw new NotImplementedException ();
320 // NOTE: does not modify imported projects
321 public void RemovePropertyGroupsWithMatchingCondition (string matchCondition)
323 throw new NotImplementedException ();
327 public void ResetBuildStatus ()
329 throw new NotImplementedException ();
332 public void Save (string projectFileName)
334 Save (projectFileName, Encoding.Default);
337 public void Save (string projectFileName, Encoding encoding)
339 xmlDocument.Save (projectFileName);
342 public void Save (TextWriter outTextWriter)
344 xmlDocument.Save (outTextWriter);
347 public void SetImportedProperty (string propertyName,
348 string propertyValue,
350 Project importProject)
352 SetImportedProperty (propertyName, propertyValue, condition, importProject,
353 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
356 public void SetImportedProperty (string propertyName,
357 string propertyValue,
359 Project importedProject,
360 PropertyPosition position)
362 SetImportedProperty (propertyName, propertyValue, condition, importedProject,
363 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
367 public void SetImportedProperty (string propertyName,
368 string propertyValue,
370 Project importedProject,
371 PropertyPosition position,
372 bool treatPropertyValueAsLiteral)
374 throw new NotImplementedException ();
377 public void SetProjectExtensions (string id, string xmlText)
379 XmlNode projectExtensions, node;
381 if (id == null || id == String.Empty || xmlText == null)
384 projectExtensions = xmlDocument.SelectSingleNode ("/tns:Project/tns:ProjectExtensions", XmlNamespaceManager);
387 if (projectExtensions == null) {
388 projectExtensions = xmlDocument.CreateElement ("ProjectExtensions", XmlNamespace);
389 xmlDocument.DocumentElement.AppendChild (projectExtensions);
391 node = xmlDocument.CreateElement (id, XmlNamespace);
392 node.InnerXml = xmlText;
393 projectExtensions.AppendChild (node);
395 node = xmlDocument.SelectSingleNode (String.Format ("/tns:Project/tns:ProjectExtensions/tns:{0}", id), XmlNamespaceManager);
398 node = xmlDocument.CreateElement (id, XmlNamespace);
399 projectExtensions.AppendChild (node);
402 node.InnerXml = xmlText;
406 MarkProjectAsDirty ();
410 public void SetProperty (string propertyName,
411 string propertyValue)
413 SetProperty (propertyName, propertyValue, "true",
414 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
418 public void SetProperty (string propertyName,
419 string propertyValue,
422 SetProperty (propertyName, propertyValue, condition,
423 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup);
427 public void SetProperty (string propertyName,
428 string propertyValue,
430 PropertyPosition position)
432 SetProperty (propertyName, propertyValue, condition,
433 PropertyPosition.UseExistingOrCreateAfterLastPropertyGroup, false);
437 public void SetProperty (string propertyName,
438 string propertyValue,
440 PropertyPosition position,
441 bool treatPropertyValueAsLiteral)
443 throw new NotImplementedException ();
446 internal void Unload ()
451 internal void CheckUnloaded ()
454 throw new InvalidOperationException ("This project object has been unloaded from the MSBuild engine and is no longer valid.");
457 // Does the actual loading.
458 void DoLoad (TextReader textReader)
461 ParentEngine.RemoveLoadedProject (this);
463 XmlReaderSettings settings = new XmlReaderSettings ();
465 if (SchemaFile != null) {
466 settings.Schemas.Add (null, SchemaFile);
467 settings.ValidationType = ValidationType.Schema;
468 settings.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
471 XmlReader xmlReader = XmlReader.Create (textReader, settings);
472 xmlDocument.Load (xmlReader);
474 if (xmlDocument.DocumentElement.Name != "Project") {
475 throw new InvalidProjectFileException (String.Format (
476 "The element <{0}> is unrecognized, or not supported in this context.", xmlDocument.DocumentElement.Name));
479 if (xmlDocument.DocumentElement.GetAttribute ("xmlns") != ns) {
480 throw new InvalidProjectFileException (
481 @"The default XML namespace of the project must be the MSBuild XML namespace." +
482 " If the project is authored in the MSBuild 2003 format, please add " +
483 "xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\" to the <Project> element. " +
484 "If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. ");
487 ParentEngine.AddLoadedProject (this);
488 } catch (Exception e) {
489 throw new InvalidProjectFileException (e.Message, e);
493 internal void ProcessXml ()
495 groupingCollection = new GroupingCollection (this);
496 imports = new ImportCollection (groupingCollection);
497 usingTasks = new UsingTaskCollection (this);
498 itemGroups = new BuildItemGroupCollection (groupingCollection);
499 propertyGroups = new BuildPropertyGroupCollection (groupingCollection);
500 targets = new TargetCollection (this);
502 taskDatabase = new TaskDatabase ();
503 if (ParentEngine.DefaultTasksRegistered)
504 taskDatabase.CopyTasks (ParentEngine.DefaultTasks);
506 if (xmlDocument.DocumentElement.GetAttributeNode ("DefaultTargets") != null)
507 defaultTargets = xmlDocument.DocumentElement.GetAttribute ("DefaultTargets").Split (';');
509 defaultTargets = new string [0];
511 ProcessElements (xmlDocument.DocumentElement, null);
517 internal void ProcessElements (XmlElement rootElement, ImportedProject ip)
519 foreach (XmlNode xn in rootElement.ChildNodes) {
520 if (xn is XmlElement) {
521 XmlElement xe = (XmlElement) xn;
523 case "ProjectExtensions":
524 AddProjectExtensions (xe);
535 AddUsingTask (xe, ip);
541 AddItemGroup (xe, ip);
543 case "PropertyGroup":
544 AddPropertyGroup (xe, ip);
550 throw new InvalidProjectFileException ("Invalid element in project file.");
556 internal void Evaluate ()
558 evaluatedItems = new BuildItemGroup (null, this, null, true);
559 evaluatedItemsIgnoringCondition = new BuildItemGroup (null, this, null, true);
560 evaluatedItemsByName = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
561 evaluatedItemsByNameIgnoringCondition = new Dictionary <string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
562 evaluatedProperties = new BuildPropertyGroup (null, null, null, true);
564 InitializeProperties ();
566 groupingCollection.Evaluate ();
568 //FIXME: UsingTasks aren't really evaluated. (shouldn't use expressions or anything)
569 foreach (UsingTask usingTask in UsingTasks)
570 usingTask.Evaluate ();
573 void InitializeProperties ()
577 foreach (BuildProperty gp in GlobalProperties) {
578 bp = new BuildProperty (gp.Name, gp.Value, PropertyType.Global);
579 EvaluatedProperties.AddProperty (bp);
582 foreach (DictionaryEntry de in Environment.GetEnvironmentVariables ()) {
583 bp = new BuildProperty ((string) de.Key, (string) de.Value, PropertyType.Environment);
584 EvaluatedProperties.AddProperty (bp);
587 bp = new BuildProperty ("MSBuildBinPath", parentEngine.BinPath, PropertyType.Reserved);
588 EvaluatedProperties.AddProperty (bp);
591 void AddProjectExtensions (XmlElement xmlElement)
595 void AddMessage (XmlElement xmlElement)
599 void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
601 Target target = new Target (xmlElement, this, importedProject);
602 targets.AddTarget (target);
604 if (firstTargetName == null)
605 firstTargetName = target.Name;
608 void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
612 usingTask = new UsingTask (xmlElement, this, importedProject);
613 UsingTasks.Add (usingTask);
616 void AddImport (XmlElement xmlElement, ImportedProject importingProject)
620 import = new Import (xmlElement, this, importingProject);
621 Imports.Add (import);
624 void AddItemGroup (XmlElement xmlElement, ImportedProject importedProject)
626 BuildItemGroup big = new BuildItemGroup (xmlElement, this, importedProject, false);
627 ItemGroups.Add (big);
630 void AddPropertyGroup (XmlElement xmlElement, ImportedProject importedProject)
632 BuildPropertyGroup bpg = new BuildPropertyGroup (xmlElement, this, importedProject, false);
633 PropertyGroups.Add (bpg);
636 void AddChoose (XmlElement xmlElement)
638 BuildChoose bc = new BuildChoose (xmlElement, this);
639 groupingCollection.Add (bc);
643 static void ValidationCallBack (object sender, ValidationEventArgs e)
645 Console.WriteLine ("Validation Error: {0}", e.Message);
648 public bool BuildEnabled {
653 buildEnabled = value;
657 public Encoding Encoding {
658 get { return encoding; }
661 public string DefaultTargets {
663 return xmlDocument.DocumentElement.GetAttribute ("DefaultTargets");
666 xmlDocument.DocumentElement.SetAttribute ("DefaultTargets", value);
667 defaultTargets = value.Split (';');
671 public BuildItemGroup EvaluatedItems {
672 get { return evaluatedItems; }
675 public BuildItemGroup EvaluatedItemsIgnoringCondition {
676 get { return evaluatedItemsIgnoringCondition; }
679 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByName {
680 get { return evaluatedItemsByName; }
683 internal IDictionary <string, BuildItemGroup> EvaluatedItemsByNameIgnoringCondition {
684 get { return evaluatedItemsByNameIgnoringCondition; }
687 public BuildPropertyGroup EvaluatedProperties {
688 get { return evaluatedProperties; }
691 public string FullFileName {
692 get { return fullFileName; }
693 set { fullFileName = value; }
696 public BuildPropertyGroup GlobalProperties {
697 get { return globalProperties; }
700 throw new ArgumentNullException ("value");
703 throw new InvalidOperationException ("Can't do that.");
705 globalProperties = value;
709 public bool IsDirty {
710 get { return isDirty; }
713 public bool IsValidated {
714 get { return isValidated; }
715 set { isValidated = value; }
718 public BuildItemGroupCollection ItemGroups {
719 get { return itemGroups; }
722 public ImportCollection Imports {
723 get { return imports; }
726 public string InitialTargets {
727 get { return initialTargets; }
728 set { initialTargets = value; }
731 public Engine ParentEngine {
732 get { return parentEngine; }
735 public BuildPropertyGroupCollection PropertyGroups {
736 get { return propertyGroups; }
739 public string SchemaFile {
740 get { return schemaFile; }
741 set { schemaFile = value; }
744 public TargetCollection Targets {
745 get { return targets; }
748 public DateTime TimeOfLastDirty {
749 get { return timeOfLastDirty; }
752 public UsingTaskCollection UsingTasks {
753 get { return usingTasks; }
758 get { return xmlDocument.InnerXml; }
761 internal static XmlNamespaceManager XmlNamespaceManager {
763 if (manager == null) {
764 manager = new XmlNamespaceManager (new NameTable ());
765 manager.AddNamespace ("tns", ns);
772 internal TaskDatabase TaskDatabase {
773 get { return taskDatabase; }
776 internal XmlDocument XmlDocument {
777 get { return xmlDocument; }
780 internal static string XmlNamespace {