aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Prebuild/src/Core/Kernel.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Prebuild/src/Core/Kernel.cs832
1 files changed, 832 insertions, 0 deletions
diff --git a/Prebuild/src/Core/Kernel.cs b/Prebuild/src/Core/Kernel.cs
new file mode 100644
index 0000000..67051d5
--- /dev/null
+++ b/Prebuild/src/Core/Kernel.cs
@@ -0,0 +1,832 @@
1#region BSD License
2/*
3Copyright (c) 2004-2008
4Matthew Holmes (matthew@wildfiregames.com),
5Dan Moorehead (dan05a@gmail.com),
6Rob Loach (http://www.robloach.net),
7C.J. Adams-Collier (cjac@colliertech.org)
8
9Redistribution and use in source and binary forms, with or without
10modification, are permitted provided that the following conditions are
11met:
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
24THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34POSSIBILITY OF SUCH DAMAGE.
35
36*/
37#endregion
38
39#define NO_VALIDATE
40
41using System;
42using System.Collections.Generic;
43using System.IO;
44using System.Reflection;
45using System.Xml;
46using System.Xml.Schema;
47using Prebuild.Core.Attributes;
48using Prebuild.Core.Interfaces;
49using Prebuild.Core.Nodes;
50using Prebuild.Core.Utilities;
51
52namespace 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.9";
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}