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