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.
29 using System.Collections;
30 using System.Collections.Specialized;
34 using System.Xml.Schema;
35 using Microsoft.Build.Framework;
36 using Mono.XBuild.Shared;
38 namespace Microsoft.Build.BuildEngine {
39 public class Project : IProject {
40 static string separator = ";";
43 IDictionary conditionedProperties;
44 Encoding currentEncoding;
45 string[] defaultTargets;
46 IList directlyImportedProjects;
47 BuildPropertyGroup environmentProperties;
48 BuildItemGroup evaluatedItems;
49 BuildItemGroup evaluatedItemsIgnoringCondition;
50 IDictionary evaluatedItemsByName;
51 IDictionary evaluatedItemsByNameIgnoringCondition;
52 BuildPropertyGroup evaluatedProperties;
53 string firstTargetName;
55 BuildPropertyGroup globalProperties;
56 GroupingCollection groups;
60 BuildItemGroupCollection itemGroups;
61 IDictionary importedProjects;
63 BuildPropertyGroupCollection propertyGroups;
64 BuildPropertyGroup reservedProperties;
66 TaskDatabase taskDatabase;
67 TargetCollection targets;
68 DateTime timeOfLastDirty;
69 IList usingTaskElements;
70 XmlDocument xmlDocument;
71 XmlElement xmlElement;
78 public Project (Engine engine)
80 parentEngine = engine;
81 xmlDocument = new XmlDocument ();
82 evaluatedItems = new BuildItemGroup (this);
83 evaluatedItemsByName = new Hashtable (new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
84 evaluatedItemsByNameIgnoringCondition = new Hashtable (new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
85 evaluatedItemsIgnoringCondition = new BuildItemGroup (this);
86 evaluatedProperties = new BuildPropertyGroup (false, null);
87 groups = new GroupingCollection ();
88 itemGroups = new BuildItemGroupCollection (groups);
89 propertyGroups = new BuildPropertyGroupCollection (groups);
90 targets = new TargetCollection (this);
91 usingTaskElements = new ArrayList ();
92 taskDatabase = new TaskDatabase ();
95 public void AddNewImport (string importLocation,
96 string importCondition)
100 public BuildItem AddNewItem (string itemName,
103 // add it to rawitemgroups, maybe to evaluated items then?
107 public BuildItemGroup AddNewItemGroup ()
112 public BuildPropertyGroup AddNewPropertyGroup (bool insertAtEndOfProject)
117 public bool Build (string[] targetNamesToBuild,
118 IDictionary targetOutputs)
120 if (targetNamesToBuild.Length == 0) {
121 if (defaultTargets.Length != 0) {
122 targetNamesToBuild = defaultTargets;
124 else if (firstTargetName != null) {
125 targetNamesToBuild = new string [1] { firstTargetName};
130 foreach (string target in targetNamesToBuild) {
131 if (BuildTarget (target, targetOutputs) == false) {
138 public bool BuildTarget (string targetName,
139 IDictionary targetOutputs)
141 return BuildTargetWithFlags (targetName, targetOutputs, BuildSettings.None);
144 public bool BuildTargetWithFlags (string targetName,
145 IDictionary targetOutputs,
146 BuildSettings buildFlags)
148 if (targets.Exists (targetName) == false)
149 throw new Exception ("Target specified to build does not exist.");
151 this.targets [targetName].Build ();
155 public string[] GetConditionedPropertyValues (string propertyName)
157 StringCollection sc = (StringCollection) conditionedProperties [propertyName];
158 string[] propertyValues = new string [sc.Count];
160 foreach (string propertyValue in sc)
161 propertyValues [i++] = propertyValue;
162 return propertyValues;
165 public string[] GetDirectlyImportedProjects ()
167 string[] dip = new string [directlyImportedProjects.Count];
169 foreach (string importedProject in directlyImportedProjects)
170 dip [i++] = importedProject;
174 public BuildItemGroup GetEvaluatedItemsByName (string itemName)
176 return (BuildItemGroup) evaluatedItemsByName [itemName];
179 public BuildItemGroup GetEvaluatedItemsByNameIgnoringCondition (string itemName)
181 return (BuildItemGroup) evaluatedItemsByNameIgnoringCondition [itemName];
184 public string GetEvaluatedProperty (string propertyName)
186 return evaluatedProperties [propertyName];
189 public string[] GetNonImportedItemNames ()
194 public string[] GetNonImportedPropertyNames ()
199 public string[] GetNonImportedTargetNames ()
201 ArrayList temporaryNonImportedTargets = new ArrayList ();
202 foreach (Target target in targets)
203 if (target.IsImported == false)
204 temporaryNonImportedTargets.Add (target);
205 string[] nonImportedTargetNames = new string [temporaryNonImportedTargets.Count];
207 foreach (Target target in temporaryNonImportedTargets)
208 nonImportedTargetNames [i++] = target.Name;
209 return nonImportedTargetNames;
212 public string[] GetNonImportedUsingTasks ()
217 public string GetProjectExtensions (string id)
222 public void LoadFromFile (string projectFileName)
224 this.fullFileName = Path.GetFullPath (projectFileName);
225 XmlSchemaCollection xmlSchemaCollection = null;
226 XmlTextReader xmlTextReader = null;
227 XmlValidatingReader xmlValidatingReader = null;
229 if (this.schemaFile != null) {
230 xmlSchemaCollection = new XmlSchemaCollection ();
231 xmlSchemaCollection.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
232 xmlSchemaCollection.Add (null, this.schemaFile);
233 if (xmlSchemaCollection.Count > 0) {
234 xmlTextReader = new XmlTextReader (projectFileName);
235 xmlValidatingReader = new XmlValidatingReader (xmlTextReader);
236 xmlValidatingReader.ValidationType = ValidationType.Schema;
237 xmlValidatingReader.Schemas.Add (xmlSchemaCollection);
238 xmlValidatingReader.ValidationEventHandler += new ValidationEventHandler (ValidationCallBack);
241 xmlTextReader = new XmlTextReader (projectFileName);
243 if (xmlValidatingReader != null)
244 xmlDocument.Load (xmlValidatingReader);
245 else if (xmlTextReader != null)
246 xmlDocument.Load (xmlTextReader);
248 throw new Exception ();
249 xmlElement = xmlDocument.DocumentElement;
250 if (xmlElement.Name != "Project")
251 throw new InvalidProjectFileException ("Invalid root element.");
252 if (xmlElement.GetAttributeNode ("DefaultTargets") != null)
253 defaultTargets = xmlElement.GetAttribute ("DefaultTargets").Split (';');
255 defaultTargets = new string [0];
257 ProcessElements (xmlElement, null);
262 public void LoadFromXml (XmlDocument projectXml)
265 xmlDocument = projectXml;
266 xmlElement = xmlDocument.DocumentElement;
267 if (xmlElement.Name != "Project")
268 throw new InvalidProjectFileException ("Invalid root element.");
269 if (xmlElement.GetAttributeNode ("DefaultTargets") != null)
270 defaultTargets = xmlElement.GetAttribute ("DefaultTargets").Split (';');
272 defaultTargets = new string [0];
274 ProcessElements (xmlElement, null);
279 public void MarkProjectAsDirty ()
284 public void RemoveAllItemGroups ()
288 public void RemoveAllItemsGroupsByCondition (string condition)
292 public void RemoveAllPropertyGroups ()
296 public void RemoveAllPropertyGroupsByCondition (string condition)
300 public void RemoveItem (BuildItem itemToRemove)
304 public void RemoveItemGroup (BuildItemGroup itemGroupToRemove)
308 public void RemoveItemsByName (string itemName)
312 public void RemovePropertyGroup (BuildPropertyGroup propertyGroupToRemove)
317 public void ResetBuildStatus ()
321 public void SaveToFile (string projectFileName)
323 xmlDocument.Save (projectFileName);
326 public void SaveToFile (string projectFileName,
327 ProjectFileEncoding encoding)
329 SaveToFile (projectFileName);
332 public void SaveToTextWriter (TextWriter outTextWriter)
334 xmlDocument.Save (outTextWriter);
337 public void SetImportedProperty (string propertyName,
338 string propertyValue,
340 Project importProject)
344 public void SetImportedPropertyAt (string propertyName,
345 string propertyValue,
347 Project importedProject,
348 PropertyPosition position)
352 public void SetProjectExtensions (string id, string xmlText)
356 public void SetProperty (string propertyName,
357 string propertyValue,
362 public void SetPropertyAt (string propertyName,
363 string propertyValue,
365 PropertyPosition position)
369 public void Unload ()
373 private void ProcessElements (XmlElement rootElement, ImportedProject ip)
375 foreach (XmlNode xn in rootElement.ChildNodes) {
376 if (xn is XmlElement) {
377 XmlElement xe = (XmlElement) xn;
379 case "ProjectExtensions":
380 AddProjectExtensions (xe);
391 AddUsingTask (xe, ip);
399 case "PropertyGroup":
400 AddPropertyGroup (xe);
406 throw new InvalidProjectFileException ("Invalid element in project file.");
412 private void AddProjectExtensions (XmlElement xmlElement)
414 if (xmlElement == null)
415 throw new ArgumentNullException ("xmlElement");
418 private void AddMessage (XmlElement xmlElement)
420 if (xmlElement == null)
421 throw new ArgumentNullException ("xmlElement");
424 private void AddTarget (XmlElement xmlElement, ImportedProject importedProject)
426 if (xmlElement == null)
427 throw new ArgumentNullException ("xmlElement");
428 Target target = targets.AddNewTarget (xmlElement.GetAttribute ("Name"));
429 target.BindToXml (xmlElement);
430 if (importedProject == null) {
431 target.IsImported = false;
432 if (firstTargetName == null)
433 firstTargetName = target.Name;
435 target.IsImported = true;
438 private void AddUsingTask (XmlElement xmlElement, ImportedProject importedProject)
440 if (xmlElement == null)
441 throw new ArgumentNullException ("xmlElement");
443 if (xmlElement.GetAttribute ("TaskName") == String.Empty)
444 throw new InvalidProjectFileException ("TaskName attribute must be specified.");
446 usingTaskElements.Add (xmlElement);
448 AssemblyLoadInfo loadInfo = null;
449 string filename = null;
451 string taskName = xmlElement.GetAttribute ("TaskName");
453 if (xmlElement.GetAttribute ("AssemblyName") != String.Empty) {
454 name = xmlElement.GetAttribute ("AssemblyName");
455 loadInfo = new AssemblyLoadInfo (name, taskName);
456 taskDatabase.RegisterTask (taskName, loadInfo);
457 } else if (xmlElement.GetAttribute ("AssemblyFile") != String.Empty) {
458 filename = xmlElement.GetAttribute ("AssemblyFile");
459 if (Path.IsPathRooted (filename) == false) {
460 if (importedProject == null)
461 filename = Path.Combine (Path.GetDirectoryName (fullFileName), filename);
463 filename = Path.Combine (Path.GetDirectoryName (importedProject.FullFileName), filename);
465 loadInfo = new AssemblyLoadInfo (LoadInfoType.AssemblyFilename, filename, null, null, null, null, taskName);
466 taskDatabase.RegisterTask (taskName, loadInfo);
468 throw new InvalidProjectFileException ("AssemblyName or AssemblyFile attribute must be specified.");
471 private void AddImport (XmlElement xmlElement, ImportedProject importingProject)
473 if (xmlElement == null)
474 throw new ArgumentNullException ("xmlElement");
477 Expression importedFileExpr;
478 ImportedProject ImportedProject;
480 importedFileExpr = new Expression (this, xmlElement.GetAttribute ("Project"));
481 importedFile = (string) importedFileExpr.ToNonArray (typeof (string));
483 if (importedFile == String.Empty)
484 throw new InvalidProjectFileException ("Project attribute must be specified.");
486 if (Path.IsPathRooted (importedFile) == false) {
487 if (importingProject == null)
488 importedFile = Path.Combine (Path.GetDirectoryName (fullFileName), importedFile);
490 importedFile = Path.Combine (Path.GetDirectoryName (importingProject.FullFileName), importedFile);
493 ImportedProject importedProject = new ImportedProject ();
495 importedProject.Load (importedFile);
496 ProcessElements (importedProject.XmlDocument.DocumentElement, importedProject);
498 catch (Exception ex) {
499 Console.WriteLine (ex);
503 private void AddItemGroup (XmlElement xmlElement)
505 if (xmlElement == null)
506 throw new ArgumentNullException ("xmlElement");
507 BuildItemGroup big = new BuildItemGroup (this);
508 big.BindToXml (xmlElement);
509 itemGroups.Add (big);
512 private void AddPropertyGroup (XmlElement xmlElement)
514 if (xmlElement == null)
515 throw new ArgumentNullException ("xmlElement");
516 BuildPropertyGroup bpg = new BuildPropertyGroup (true, this);
517 bpg.BindToXml (xmlElement);
518 propertyGroups.Add (bpg);
521 private void AddChoose (XmlElement xmlElement)
523 if (xmlElement == null)
524 throw new ArgumentNullException ("xmlElement");
527 private static void ValidationCallBack (object sender, ValidationEventArgs e)
529 Console.WriteLine ("Validation Error: {0}", e.Message);
532 public bool BuildEnabled {
537 buildEnabled = value;
541 public ProjectFileEncoding CurrentProjectFileEncoding {
543 if (currentEncoding == Encoding.UTF8)
544 return ProjectFileEncoding.Utf8;
545 if (currentEncoding == Encoding.Unicode)
546 return ProjectFileEncoding.Unicode;
547 if (currentEncoding == Encoding.GetEncoding ("ANSI"))
548 return ProjectFileEncoding.Ansi;
549 throw new Exception ();
553 public string DefaultTargets {
554 get { return xmlElement.GetAttribute ("DefaultTargets"); }
556 xmlElement.SetAttribute ("DefaultTargets",value);
557 defaultTargets = value.Split (';');
561 public BuildItemGroup EvaluatedItems {
562 get { return evaluatedItems; }
565 public BuildItemGroup EvaluatedItemsIgnoringCondition {
566 get { return evaluatedItemsIgnoringCondition; }
569 internal IDictionary EvaluatedItemsByName {
570 get { return evaluatedItemsByName; }
573 internal IDictionary EvaluatedItemsByNameIgnoringCondition {
574 get { return evaluatedItemsByNameIgnoringCondition; }
577 public BuildPropertyGroup EvaluatedProperties {
578 get { return evaluatedProperties; }
581 public string FullFileName {
582 get { return fullFileName; }
583 set { fullFileName = value; }
586 public BuildPropertyGroup GlobalProperties {
587 get { return globalProperties; }
589 globalProperties = value;
590 foreach (BuildProperty bp in globalProperties)
591 evaluatedProperties.AddFromExistingProperty (bp);
595 public bool IsDirty {
596 get { return isDirty; }
599 public bool IsValidated {
600 get { return isValidated; }
601 set { isValidated = value; }
604 public BuildItemGroupCollection ItemGroups {
605 get { return itemGroups; }
608 public Engine ParentEngine {
609 get { return parentEngine; }
612 public BuildPropertyGroupCollection PropertyGroups {
613 get { return propertyGroups; }
616 public string SchemaFile {
617 get { return schemaFile; }
618 set { schemaFile = value; }
621 public TargetCollection Targets {
622 get { return targets; }
625 public DateTime TimeOfLastDirty {
626 get { return timeOfLastDirty; }
629 public XmlDocument Xml {
630 get { return xmlDocument; }
633 internal TaskDatabase TaskDatabase {
634 get { return taskDatabase; }
637 internal BuildPropertyGroup EnvironmentProperties {
639 environmentProperties = value;
640 foreach (BuildProperty bp in environmentProperties)
641 evaluatedProperties.AddFromExistingProperty (bp);
645 internal BuildPropertyGroup ReservedProperties {
647 reservedProperties = value;
648 foreach (BuildProperty bp in reservedProperties)
649 evaluatedProperties.AddFromExistingProperty (bp);