clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 #region BSD License
2 /*
3 Copyright (c) 2004-2008
4 Matthew Holmes (matthew@wildfiregames.com),
5 Dan Moorehead (dan05a@gmail.com),
6 Rob Loach (http://www.robloach.net),
7 C.J. Adams-Collier (cjac@colliertech.org)
8  
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are
11 met:
12  
13 * Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15  
16 * Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19  
20 * The name of the author may not be used to endorse or promote
21 products derived from this software without specific prior written
22 permission.
23  
24 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 POSSIBILITY OF SUCH DAMAGE.
35  
36 */
37 #endregion
38  
39 #define NO_VALIDATE
40  
41 using System;
42 using System.Collections.Generic;
43 using System.IO;
44 using System.Reflection;
45 using System.Xml;
46 using System.Xml.Schema;
47 using Prebuild.Core.Attributes;
48 using Prebuild.Core.Interfaces;
49 using Prebuild.Core.Nodes;
50 using Prebuild.Core.Utilities;
51  
52 namespace Prebuild.Core
53 {
54 /// <summary>
55 ///
56 /// </summary>
57 public class Kernel : IDisposable
58 {
59 #region Inner Classes
60  
61 private struct NodeEntry
62 {
63 public Type Type;
64 public DataNodeAttribute Attribute;
65 }
66  
67 #endregion
68  
69 #region Fields
70  
71 private static readonly Kernel m_Instance = new Kernel();
72  
73 /// <summary>
74 /// This must match the version of the schema that is embeeded
75 /// </summary>
76 private const string m_SchemaVersion = "1.10";
77 private const string m_Schema = "prebuild-" + m_SchemaVersion + ".xsd";
78 private const string m_SchemaURI = "http://dnpb.sourceforge.net/schemas/" + m_Schema;
79 bool disposed;
80 private Version m_Version;
81 private const string m_Revision = "";
82 private CommandLineCollection m_CommandLine;
83 private Log m_Log;
84 private CurrentDirectory m_CurrentWorkingDirectory;
85 private XmlSchemaCollection m_Schemas;
86  
87 private readonly Dictionary<string, ITarget> m_Targets = new Dictionary<string, ITarget>();
88 private readonly Dictionary<string, NodeEntry> m_Nodes = new Dictionary<string, NodeEntry>();
89  
90 readonly List<SolutionNode> m_Solutions = new List<SolutionNode>();
91 string m_Target;
92 string m_Clean;
93 string[] m_RemoveDirectories;
94 XmlDocument m_CurrentDoc;
95 bool m_PauseAfterFinish;
96 string[] m_ProjectGroups;
97  
98 #endregion
99  
100 #region Constructors
101  
102 private Kernel()
103 {
104 }
105  
106 #endregion
107  
108 #region Properties
109  
110 /// <summary>
111 /// Gets a value indicating whether [pause after finish].
112 /// </summary>
113 /// <value><c>true</c> if [pause after finish]; otherwise, <c>false</c>.</value>
114 public bool PauseAfterFinish
115 {
116 get
117 {
118 return m_PauseAfterFinish;
119 }
120 }
121  
122 /// <summary>
123 /// Gets the instance.
124 /// </summary>
125 /// <value>The instance.</value>
126 public static Kernel Instance
127 {
128 get
129 {
130 return m_Instance;
131 }
132 }
133  
134 /// <summary>
135 /// Gets the version.
136 /// </summary>
137 /// <value>The version.</value>
138 public string Version
139 {
140 get
141 {
142 return String.Format("{0}.{1}.{2}{3}", m_Version.Major, m_Version.Minor, m_Version.Build, m_Revision);
143 }
144 }
145  
146 /// <summary>
147 /// Gets the command line.
148 /// </summary>
149 /// <value>The command line.</value>
150 public CommandLineCollection CommandLine
151 {
152 get
153 {
154 return m_CommandLine;
155 }
156 }
157  
158 /// <summary>
159 /// Gets the targets.
160 /// </summary>
161 /// <value>The targets.</value>
162 public Dictionary<string, ITarget> Targets
163 {
164 get
165 {
166 return m_Targets;
167 }
168 }
169  
170 /// <summary>
171 /// Gets the log.
172 /// </summary>
173 /// <value>The log.</value>
174 public Log Log
175 {
176 get
177 {
178 return m_Log;
179 }
180 }
181  
182 /// <summary>
183 /// Gets the current working directory.
184 /// </summary>
185 /// <value>The current working directory.</value>
186 public CurrentDirectory CurrentWorkingDirectory
187 {
188 get
189 {
190 return m_CurrentWorkingDirectory;
191 }
192 }
193  
194 /// <summary>
195 /// Gets the solutions.
196 /// </summary>
197 /// <value>The solutions.</value>
198 public List<SolutionNode> Solutions
199 {
200 get
201 {
202 return m_Solutions;
203 }
204 }
205  
206 /// <summary>
207 /// Gets the XmlDocument object representing the prebuild.xml
208 /// being processed
209 /// </summary>
210 /// <value>The XmlDocument object</value>
211 public XmlDocument CurrentDoc
212 {
213 get
214 {
215 return m_CurrentDoc;
216 }
217 }
218  
219 #endregion
220  
221 #region Private Methods
222  
223 private static void RemoveDirectories(string rootDir, string[] dirNames)
224 {
225 foreach(string dir in Directory.GetDirectories(rootDir))
226 {
227 string simpleName = Path.GetFileName(dir);
228  
229 if(Array.IndexOf(dirNames, simpleName) != -1)
230 {
231 //delete if the name matches one of the directory names to delete
232 string fullDirPath = Path.GetFullPath(dir);
233 Directory.Delete(fullDirPath,true);
234 }
235 else//not a match, so check children
236 {
237 RemoveDirectories(dir,dirNames);
238 //recurse, checking children for them
239 }
240 }
241 }
242  
243 // private void RemoveDirectoryMatches(string rootDir, string dirPattern)
244 // {
245 // foreach(string dir in Directory.GetDirectories(rootDir))
246 // {
247 // foreach(string match in Directory.GetDirectories(dir))
248 // {//delete all child directories that match
249 // Directory.Delete(Path.GetFullPath(match),true);
250 // }
251 // //recure through the rest checking for nested matches to delete
252 // RemoveDirectoryMatches(dir,dirPattern);
253 // }
254 // }
255  
256 private void LoadSchema()
257 {
258 Assembly assembly = GetType().Assembly;
259 Stream stream = assembly.GetManifestResourceStream("Prebuild.data." + m_Schema);
260 if(stream == null)
261 {
262 //try without the default namespace prepending to it in case was compiled with SharpDevelop or MonoDevelop instead of Visual Studio .NET
263 stream = assembly.GetManifestResourceStream(m_Schema);
264 if(stream == null)
265 {
266 throw new System.Reflection.TargetException(string.Format("Could not find the scheme embedded resource file '{0}'.", m_Schema));
267 }
268 }
269 XmlReader schema = new XmlTextReader(stream);
270  
271 m_Schemas = new XmlSchemaCollection();
272 m_Schemas.Add(m_SchemaURI, schema);
273 }
274  
275 private void CacheVersion()
276 {
277 m_Version = Assembly.GetEntryAssembly().GetName().Version;
278 }
279  
280 private void CacheTargets(Assembly assm)
281 {
282 foreach(Type t in assm.GetTypes())
283 {
284 TargetAttribute ta = (TargetAttribute)Helper.CheckType(t, typeof(TargetAttribute), typeof(ITarget));
285  
286 if(ta == null)
287 continue;
288  
289 if (t.IsAbstract)
290 continue;
291  
292 ITarget target = (ITarget)assm.CreateInstance(t.FullName);
293 if (target == null)
294 {
295 throw new MissingMethodException("Could not create ITarget instance");
296 }
297  
298 m_Targets[ta.Name] = target;
299 }
300 }
301  
302 private void CacheNodeTypes(Assembly assm)
303 {
304 foreach(Type t in assm.GetTypes())
305 {
306 foreach (DataNodeAttribute dna in t.GetCustomAttributes(typeof(DataNodeAttribute), true))
307 {
308 NodeEntry ne = new NodeEntry();
309 ne.Type = t;
310 ne.Attribute = dna;
311 m_Nodes[dna.Name] = ne;
312 }
313 }
314 }
315  
316 private void LogBanner()
317 {
318 m_Log.Write("Prebuild v" + Version);
319 m_Log.Write("Copyright (c) 2004-2010");
320 m_Log.Write("Matthew Holmes (matthew@wildfiregames.com),");
321 m_Log.Write("Dan Moorehead (dan05a@gmail.com),");
322 m_Log.Write("David Hudson (jendave@yahoo.com),");
323 m_Log.Write("Rob Loach (http://www.robloach.net),");
324 m_Log.Write("C.J. Adams-Collier (cjac@colliertech.org),");
325 m_Log.Write("John Hurliman (john.hurliman@intel.com),");
326  
327 m_Log.Write("See 'prebuild /usage' for help");
328 m_Log.Write();
329 }
330  
331  
332  
333 private void ProcessFile(string file)
334 {
335 ProcessFile(file, m_Solutions);
336 }
337  
338 public void ProcessFile(ProcessNode node, SolutionNode parent)
339 {
340 if (node.IsValid)
341 {
342 List<SolutionNode> list = new List<SolutionNode>();
343 ProcessFile(node.Path, list);
344  
345 foreach (SolutionNode solution in list)
346 parent.SolutionsTable[solution.Name] = solution;
347 }
348 }
349  
350 /// <summary>
351 ///
352 /// </summary>
353 /// <param name="file"></param>
354 /// <param name="solutions"></param>
355 /// <returns></returns>
356 public void ProcessFile(string file, IList<SolutionNode> solutions)
357 {
358 m_CurrentWorkingDirectory.Push();
359  
360 string path = file;
361 try
362 {
363 try
364 {
365 path = Helper.ResolvePath(path);
366 }
367 catch(ArgumentException)
368 {
369 m_Log.Write("Could not open Prebuild file: " + path);
370 m_CurrentWorkingDirectory.Pop();
371 return;
372 }
373  
374 Helper.SetCurrentDir(Path.GetDirectoryName(path));
375  
376 XmlTextReader reader = new XmlTextReader(path);
377  
378 Core.Parse.Preprocessor pre = new Core.Parse.Preprocessor();
379  
380 //register command line arguments as XML variables
381 IEnumerator<KeyValuePair<string, string>> dict = m_CommandLine.GetEnumerator();
382 while (dict.MoveNext())
383 {
384 string name = dict.Current.Key.Trim();
385 if (name.Length > 0)
386 pre.RegisterVariable(name, dict.Current.Value);
387 }
388  
389 string xml = pre.Process(reader);//remove script and evaulate pre-proccessing to get schema-conforming XML
390  
391 // See if the user put into a pseudo target of "prebuild:preprocessed-input" to indicate they want to see the
392 // output before the system processes it.
393 if (m_CommandLine.WasPassed("ppi"))
394 {
395 // Get the filename if there is one, otherwise use a default.
396 string ppiFile = m_CommandLine["ppi"];
397 if (ppiFile == null || ppiFile.Trim().Length == 0)
398 {
399 ppiFile = "preprocessed-input.xml";
400 }
401  
402 // Write out the string to the given stream.
403 try
404 {
405 using (StreamWriter ppiWriter = new StreamWriter(ppiFile))
406 {
407 ppiWriter.WriteLine(xml);
408 }
409 }
410 catch(IOException ex)
411 {
412 Console.WriteLine("Could not write PPI file '{0}': {1}", ppiFile, ex.Message);
413 }
414  
415 // Finish processing this special tag.
416 return;
417 }
418  
419 m_CurrentDoc = new XmlDocument();
420 try
421 {
422 #if NO_VALIDATE
423 XmlReader validator = XmlReader.Create(new StringReader(xml));
424 m_CurrentDoc.Load(validator);
425 #else
426 XmlValidatingReader validator = new XmlValidatingReader(new XmlTextReader(new StringReader(xml)));
427  
428 //validate while reading from string into XmlDocument DOM structure in memory
429 foreach(XmlSchema schema in m_Schemas)
430 {
431 validator.Schemas.Add(schema);
432 }
433 m_CurrentDoc.Load(validator);
434 #endif
435 }
436 catch(XmlException e)
437 {
438 throw new XmlException(e.ToString());
439 }
440  
441 //is there a purpose to writing it? An syntax/schema problem would have been found during pre.Process() and reported with details
442 if(m_CommandLine.WasPassed("ppo"))
443 {
444 string ppoFile = m_CommandLine["ppo"];
445 if(ppoFile == null || ppoFile.Trim().Length < 1)
446 {
447 ppoFile = "preprocessed.xml";
448 }
449  
450 StreamWriter writer = null;
451 try
452 {
453 writer = new StreamWriter(ppoFile);
454 writer.Write(xml);
455 }
456 catch(IOException ex)
457 {
458 Console.WriteLine("Could not write PPO file '{0}': {1}", ppoFile, ex.Message);
459 }
460 finally
461 {
462 if(writer != null)
463 {
464 writer.Close();
465 }
466 }
467 return;
468 }
469 //start reading the xml config file
470 XmlElement rootNode = m_CurrentDoc.DocumentElement;
471 //string suggestedVersion = Helper.AttributeValue(rootNode,"version","1.0");
472 Helper.CheckForOSVariables = Helper.ParseBoolean(rootNode,"checkOsVars",false);
473  
474 foreach(XmlNode node in rootNode.ChildNodes)//solutions or if pre-proc instructions
475 {
476 IDataNode dataNode = ParseNode(node, null);
477 if(dataNode is ProcessNode)
478 {
479 ProcessNode proc = (ProcessNode)dataNode;
480 if(proc.IsValid)
481 {
482 ProcessFile(proc.Path);
483 }
484 }
485 else if(dataNode is SolutionNode)
486 {
487 solutions.Add((SolutionNode)dataNode);
488 }
489 }
490 }
491 catch(XmlSchemaException xse)
492 {
493 m_Log.Write("XML validation error at line {0} in {1}:\n\n{2}",
494 xse.LineNumber, path, xse.Message);
495 }
496 finally
497 {
498 m_CurrentWorkingDirectory.Pop();
499 }
500 }
501  
502 #endregion
503  
504 #region Public Methods
505  
506 /// <summary>
507 /// Allows the project.
508 /// </summary>
509 /// <param name="projectGroupsFlags">The project groups flags.</param>
510 /// <returns></returns>
511 public bool AllowProject(string projectGroupsFlags)
512 {
513 if(m_ProjectGroups != null && m_ProjectGroups.Length > 0)
514 {
515 if(projectGroupsFlags != null && projectGroupsFlags.Length == 0)
516 {
517 foreach(string group in projectGroupsFlags.Split('|'))
518 {
519 if(Array.IndexOf(m_ProjectGroups, group) != -1) //if included in the filter list
520 {
521 return true;
522 }
523 }
524 }
525 return false;//not included in the list or no groups specified for the project
526 }
527 return true;//no filter specified in the command line args
528 }
529  
530 /// <summary>
531 /// Gets the type of the node.
532 /// </summary>
533 /// <param name="node">The node.</param>
534 /// <returns></returns>
535 public Type GetNodeType(XmlNode node)
536 {
537 if( node == null )
538 {
539 throw new ArgumentNullException("node");
540 }
541 if(!m_Nodes.ContainsKey(node.Name))
542 {
543 return null;
544 }
545  
546 NodeEntry ne = m_Nodes[node.Name];
547 return ne.Type;
548 }
549  
550 /// <summary>
551 ///
552 /// </summary>
553 /// <param name="node"></param>
554 /// <param name="parent"></param>
555 /// <returns></returns>
556 public IDataNode ParseNode(XmlNode node, IDataNode parent)
557 {
558 return ParseNode(node, parent, null);
559 }
560  
561 //Create an instance of the data node type that is mapped to the name of the xml DOM node
562 /// <summary>
563 /// Parses the node.
564 /// </summary>
565 /// <param name="node">The node.</param>
566 /// <param name="parent">The parent.</param>
567 /// <param name="preNode">The pre node.</param>
568 /// <returns></returns>
569 public IDataNode ParseNode(XmlNode node, IDataNode parent, IDataNode preNode)
570 {
571 IDataNode dataNode;
572  
573 try
574 {
575 if( node == null )
576 {
577 throw new ArgumentNullException("node");
578 }
579 if(preNode == null)
580 {
581 if(!m_Nodes.ContainsKey(node.Name))
582 {
583 Console.WriteLine("WARNING: Unknown XML node: " + node.Name);
584 return null;
585 }
586  
587 NodeEntry ne = m_Nodes[node.Name];
588 Type type = ne.Type;
589 //DataNodeAttribute dna = ne.Attribute;
590  
591 dataNode = (IDataNode)type.Assembly.CreateInstance(type.FullName);
592 if(dataNode == null)
593 {
594 throw new System.Reflection.TargetException("Could not create new parser instance: " + type.FullName);
595 }
596 }
597 else
598 dataNode = preNode;
599  
600 dataNode.Parent = parent;
601 dataNode.Parse(node);
602 }
603 catch(WarningException wex)
604 {
605 m_Log.Write(LogType.Warning, wex.Message);
606 return null;
607 }
608 catch(FatalException fex)
609 {
610 m_Log.WriteException(LogType.Error, fex);
611 throw;
612 }
613 catch(Exception ex)
614 {
615 m_Log.WriteException(LogType.Error, ex);
616 throw;
617 }
618  
619 return dataNode;
620 }
621  
622 /// <summary>
623 /// Initializes the specified target.
624 /// </summary>
625 /// <param name="target">The target.</param>
626 /// <param name="args">The args.</param>
627 public void Initialize(LogTargets target, string[] args)
628 {
629 CacheTargets(GetType().Assembly);
630 CacheNodeTypes(GetType().Assembly);
631 CacheVersion();
632  
633 m_CommandLine = new CommandLineCollection(args);
634  
635 string logFile = null;
636 if(m_CommandLine.WasPassed("log"))
637 {
638 logFile = m_CommandLine["log"];
639  
640 if(logFile != null && logFile.Length == 0)
641 {
642 logFile = "Prebuild.log";
643 }
644 }
645 else
646 {
647 target = target & ~LogTargets.File; //dont output to a file
648 }
649  
650 m_Log = new Log(target, logFile);
651 LogBanner();
652  
653 m_CurrentWorkingDirectory = new CurrentDirectory();
654  
655 m_Target = m_CommandLine["target"];
656 m_Clean = m_CommandLine["clean"];
657 string removeDirs = m_CommandLine["removedir"];
658 if(removeDirs != null && removeDirs.Length == 0)
659 {
660 m_RemoveDirectories = removeDirs.Split('|');
661 }
662  
663 string flags = m_CommandLine["allowedgroups"];//allows filtering by specifying a pipe-delimited list of groups to include
664 if(flags != null && flags.Length == 0)
665 {
666 m_ProjectGroups = flags.Split('|');
667 }
668 m_PauseAfterFinish = m_CommandLine.WasPassed("pause");
669  
670 LoadSchema();
671 }
672  
673 /// <summary>
674 /// Processes this instance.
675 /// </summary>
676 public void Process()
677 {
678 bool perfomedOtherTask = false;
679 if(m_RemoveDirectories != null && m_RemoveDirectories.Length > 0)
680 {
681 try
682 {
683 RemoveDirectories(".",m_RemoveDirectories);
684 }
685 catch(IOException e)
686 {
687 m_Log.Write("Failed to remove directories named {0}",m_RemoveDirectories);
688 m_Log.WriteException(LogType.Error,e);
689 }
690 catch(UnauthorizedAccessException e)
691 {
692 m_Log.Write("Failed to remove directories named {0}",m_RemoveDirectories);
693 m_Log.WriteException(LogType.Error,e);
694 }
695 perfomedOtherTask = true;
696 }
697  
698 if(m_Target != null && m_Clean != null)
699 {
700 m_Log.Write(LogType.Error, "The options /target and /clean cannot be passed together");
701 return;
702 }
703  
704 if(m_Target == null && m_Clean == null)
705 {
706 if(perfomedOtherTask) //finished
707 {
708 return;
709 }
710 m_Log.Write(LogType.Error, "Must pass either /target or /clean to process a Prebuild file");
711 return;
712 }
713  
714 string file = "./prebuild.xml";
715 if(m_CommandLine.WasPassed("file"))
716 {
717 file = m_CommandLine["file"];
718 }
719  
720 ProcessFile(file);
721  
722 string target = (m_Target != null ? m_Target.ToLower() : m_Clean.ToLower());
723 bool clean = (m_Target == null);
724 if(clean && target != null && target.Length == 0)
725 {
726 target = "all";
727 }
728 if(clean && target == "all")//default to all if no target was specified for clean
729 {
730 //check if they passed yes
731 if (!m_CommandLine.WasPassed("yes"))
732 {
733 Console.WriteLine("WARNING: This operation will clean ALL project files for all targets, are you sure? (y/n):");
734 string ret = Console.ReadLine();
735 if(ret == null)
736 {
737 return;
738 }
739 ret = ret.Trim().ToLower();
740 if((ret.ToLower() != "y" && ret.ToLower() != "yes"))
741 {
742 return;
743 }
744 }
745 //clean all targets (just cleaning vs2002 target didn't clean nant)
746 foreach(ITarget targ in m_Targets.Values)
747 {
748 targ.Clean(this);
749 }
750 }
751 else
752 {
753 if (!m_Targets.ContainsKey(target)) {
754 m_Log.Write(LogType.Error, "Unknown Target \"{0}\"", target);
755 return;
756 }
757 ITarget targ = m_Targets[target];
758  
759 if(clean)
760 {
761 targ.Clean(this);
762 }
763 else
764 {
765 targ.Write(this);
766 }
767 }
768  
769 m_Log.Flush();
770 }
771  
772 #endregion
773  
774 #region IDisposable Members
775  
776 /// <summary>
777 ///
778 /// </summary>
779 public void Dispose()
780 {
781 Dispose(true);
782 GC.SuppressFinalize(this);
783 }
784  
785 /// <summary>
786 /// Dispose objects
787 /// </summary>
788 /// <param name="disposing">
789 /// If true, it will dispose close the handle
790 /// </param>
791 /// <remarks>
792 /// Will dispose managed and unmanaged resources.
793 /// </remarks>
794 protected virtual void Dispose(bool disposing)
795 {
796 if (!disposed)
797 {
798 if (disposing)
799 {
800 GC.SuppressFinalize(this);
801 if (m_Log != null)
802 {
803 m_Log.Close();
804 m_Log = null;
805 }
806 }
807 }
808 disposed = true;
809 }
810  
811 /// <summary>
812 ///
813 /// </summary>
814 ~Kernel()
815 {
816 Dispose(false);
817 }
818  
819 /// <summary>
820 /// Closes and destroys this object
821 /// </summary>
822 /// <remarks>
823 /// Same as Dispose(true)
824 /// </remarks>
825 public void Close()
826 {
827 Dispose();
828 }
829  
830 #endregion
831 }
832 }