/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: nib.cs // // Note: Native image building tool. // using System; using System.Collections; using System.Diagnostics; using System.Runtime.InteropServices; using System.IO; using System.Text; using System.Threading; #if !SINGULARITY using System.Xml; using Windows; #else using Microsoft.Singularity.Xml; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Io; using Microsoft.Singularity.Directory; using Microsoft.Singularity; using Microsoft.Singularity.Applications; #endif namespace Microsoft.Singularity.Applications { public class NativeImageBuilder { // // Global flags. // #if !SINGULARITY public static string Bartok = "bartok.exe"; #else public static string Bartok = "bartok"; #endif public static bool DoClean = false; public static bool DoCodegen = true; public static bool DoLinker = true; public static bool DoManifest = true; public static bool DoLogBartok = false; public static bool ForceCodegen = false; public static bool ForceLinker = false; public static bool ForceManifest = false; public static bool Verbose = false; // // Fields in the native image building class // private Options options; // private string currentFile; // For warning and error messages. private static string machine; private string cacheDirectory; private string libCacheDirectory; private string nativeDirectory; private string tempDirectory; private string optionsFile; #if !SINGULARITY private XmlDocument optManifest; private XmlDocument appManifest; #else private XmlNode optManifest; private XmlNode appManifest; #endif // set up an object that can build native images based on the system manifest. public NativeImageBuilder(string cacheDirectory, string libCacheDirectory, string nativeDirectory, string tempDirectory) { this.options = null; this.cacheDirectory = cacheDirectory; this.libCacheDirectory = libCacheDirectory; this.nativeDirectory = nativeDirectory; this.tempDirectory = tempDirectory; } public bool LoadOptionsManifest(string optionsFile) { this.optionsFile = optionsFile; this.options = ParseOptionsManifest(optionsFile, out optManifest); return (options != null); } //////////////////////////////////////////////// Methods to help with XML. // private XmlNode AddElement(XmlNode parent, string name) { #if !SINGULARITY XmlNode element = appManifest.CreateNode(XmlNodeType.Element, name, ""); if (parent != null) { parent.AppendChild(element); } return element; #else XmlNode element = appManifest.CreateNode(name); if (parent != null) { parent.AddChild(element); } return element; #endif } private void AddAttribute(XmlNode node, string name, string value) { #if !SINGULARITY XmlAttribute attr = appManifest.CreateAttribute(name); attr.Value = value; node.Attributes.Append(attr); #else node.AddAttribute(name,value); #endif } private void InsertAttribute(XmlNode node, string name, string value) { #if !SINGULARITY XmlAttribute attr = appManifest.CreateAttribute(name); attr.Value = value; node.Attributes.Prepend(attr); #else node.PrependAttribute(name,value); #endif } // Return an array with every child of the parent whose name matches // private static XmlNode[] GetChildren(XmlNode parent, string name) { ArrayList result = new ArrayList(); foreach (XmlNode child in parent.ChildNodes) { if (child.Name == name) { result.Add(child); } } if (result.Count == 0) { return new XmlNode[0]; } XmlNode[] children = new XmlNode [result.Count]; for (int i = 0; i < result.Count; i++) { children[i] = (XmlNode)result[i]; } return children; } // Get the first child named `name' private static XmlNode GetChild(XmlNode parent, string name) { #if !SINGULARITY return parent[name]; #else return parent.GetChild(name); #endif } // Get end node along a path of first matches private static XmlNode GetDescendant(XmlNode root, string [] path) { XmlNode parent = root; foreach (string pathelement in path) { parent = GetChild(parent, pathelement); if (parent == null) { return null; } } return parent; } // Get the named attribute if it exists. private static string GetAttribute(XmlNode node, string attrib) { #if !SINGULARITY XmlAttribute xa = node.Attributes[attrib]; return xa != null ? xa.Value : null; #else return node.GetAttribute(attrib, null); #endif } private static string GetAttribute(XmlNode node, string attrib, string value) { #if !SINGULARITY XmlAttribute xa = node.Attributes[attrib]; return xa != null ? xa.Value : value; #else string val = node.GetAttribute(attrib,null); return val != null ? val : value; #endif } private static int GetAttribute(XmlNode node, string attrib, int value) { #if !SINGULARITY XmlAttribute xa = node.Attributes[attrib]; return xa != null ? Int32.Parse(xa.Value) : value; #else string val = node.GetAttribute(attrib,null); return val != null ? Int32.Parse(val) : value; #endif } #if SINGULARITY public bool RunProgram(string appname, string program, string arguments, StreamWriter log, PipeMultiplexer! outputMux) { string commandName; string[]! args; Process p = null; UnicodePipeContract.Imp! childStdInImp; UnicodePipeContract.Exp! childStdInExp; UnicodePipeContract.Imp! childStdOutImp; UnicodePipeContract.Exp! childStdOutExp; NibHelper.BreakCommandLine(program + " " + arguments, new char[] {' '}, out commandName, out args); DirectoryServiceContract.Imp:Ready! ds = NibHelper.GetDirectoryServiceContract(); WriteLine("\t\t[{0,-30}] {1}", appname, program); try { if (log != null) { UnicodePipeContract.NewChannel(out childStdInImp, out childStdInExp); UnicodePipeContract.NewChannel(out childStdOutImp, out childStdOutExp); p = Binder.CreateProcess(ds,args,(!)childStdInExp,(!)childStdOutImp); if (p == null) return false; p.Start(); bool done = false; assert childStdOutExp != null; char[] buffer = null; while (!done) { switch receive { case childStdOutExp.Write(i_buffer,i_offset, i_count): //copy buffer; buffer = new char[i_count]; int i=0; int j = i_offset; for (; i < i_count; ) { buffer[i++] = i_buffer[j++]; } childStdOutExp.SendAckWrite(i_buffer); Console.WriteLine(buffer); log.Write(buffer, 0, i_count); if (i_count < buffer.Length) { break; } buffer = null; continue; case childStdOutExp.ChannelClosed(): done = true; continue; } } try { delete childStdInExp; // kill our end of the input pipe; } catch(Exception ex) { Console.WriteLine("error deleting endpoint"); ShowException(ex); } } else { p = Binder.CreateProcess(ds,args,outputMux); if (p == null) return false; p.Start(); } if (p != null) { p.Join(); } } finally { NibHelper.ReleaseDirectoryServiceContract(ds); } return true; } #else public bool RunProgram(string appname, string program, string arguments, StreamWriter log) { Process p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = (log != null); p.StartInfo.FileName = program; p.StartInfo.Arguments = arguments; int exit = -1; long peakPagedMemorySize = 0; long peakVirtualMemorySize = 0; long peakWorkingSet = 0; try { p.Start(); p.PriorityClass = ProcessPriorityClass.BelowNormal; // In Win32, the usual sequence is to create a process with the initial thread // suspended, then add the process to a job object. But the .Net framework // doesn't provide any way to do this; the process is not created until // Process.Create(), and there is no flag in ProcessStartInfo to indicate that // the initial thread should be created suspended. So, we just start the // process and immediately try to set the relevant parameters. if (_BuildingParallel) { if (_JobHandle != IntPtr.Zero) { if (!Kernel32.AssignProcessToJobObject(_JobHandle, p.Handle)) Console.WriteLine("FAILED to assign process to job: " + Kernel32.GetLastErrorText()); } try { p.ProcessorAffinity = _ChildProcessorAffinityMask; } catch (Exception ex) { Console.WriteLine("FAILED to set processor affinity of child process!"); ShowException(ex); } } WriteLine("\t\t[{0,-30}] {1}", appname, program); if (Verbose) { WriteLine("\t{0}", arguments); } if (log != null) { StreamReader reader = p.StandardOutput; char[] buffer = new char [4096]; for (;;) { int read = reader.ReadBlock(buffer, 0, buffer.Length); if (_ShowDots) Console.Write("."); log.Write(buffer, 0, read); if (read < buffer.Length) { break; } try { peakPagedMemorySize = Math.Max(p.PeakPagedMemorySize64, peakPagedMemorySize); peakVirtualMemorySize = Math.Max(p.PeakVirtualMemorySize64, peakVirtualMemorySize); peakWorkingSet = Math.Max(p.PeakWorkingSet64, peakWorkingSet); } catch (Exception) { } } } if (_ShowDots) Console.WriteLine(); p.WaitForExit(); exit = p.ExitCode; } catch (Exception e) { Console.WriteLine("nib: Exception: {0}", e); } if (log != null) { log.WriteLine("# peak memory: paged={0}KB, virtual={1}KB, working={2}KB", peakPagedMemorySize / 1024, peakWorkingSet / 1024, peakVirtualMemorySize / 1024); } if (exit != 0) { if (true) { WriteLine("nib: Error: [{0}] {1} failed with exit code: {2}", appname, program, exit); } return false; } else { return true; } } #endif ////////////////////////////////////////////////////////////////////////// // class OAssembly { public readonly string Name; public readonly int MajorVersion; // -1 if null. public readonly int MinorVersion; // -1 if null. public readonly int Build; // -1 if null. public readonly int Revision; // -1 if null. public string Locale; public string PublicKey; public string Cache; // products: public string Final; public OAssembly(string name, string version) { this.Name = name; this.MajorVersion = -1; this.MinorVersion = -1; this.Build = -1; this.Revision = -1; if (version != null) { int offset = 0; this.MajorVersion = ParseInt(version, ref offset); this.MinorVersion = ParseInt(version, ref offset); this.Build = ParseInt(version, ref offset); this.Revision = ParseInt(version, ref offset); } } private static int ParseInt(string s, ref int offset) { int num = -1; while (offset < s.Length && s[offset] >= '0' && s[offset] <= '9') { if (num == -1) { num = (s[offset++] - '0'); } else { num = num * 10 + (s[offset++] - '0'); } } if (offset < s.Length && s[offset] == '.') { offset++; } return num; } #if xxx public override String ToString() { return String.Format("{0}.{1}.{2}.{3}.{4}.{5}.{6}", Name, MajorVersion, MinorVersion, Build, Revision, Locale != null ? Locale : "", PublicKey != null ? PublicKey.Substring(0,8) : ""); } #endif // Only non-null fields in the target should be compared. // So that a 1.0.3000.3 in "this" matches 1.0.3000 in target. public bool IsMatch(OAssembly target) { if (String.Compare(Name, target.Name, true) != 0) { return false; } if (target.Locale != null) { if (Locale == null || String.Compare(Locale, target.Locale, true) != 0) { return false; } } if (target.PublicKey != null) { if (PublicKey == null || String.Compare(PublicKey, target.PublicKey, true) != 0) { return false; } } if (target.MajorVersion != -1 && MajorVersion != target.MajorVersion) { return false; } if (target.MinorVersion != -1 && MinorVersion != target.MinorVersion) { return false; } if (target.Build != -1 && Build != target.Build) { return false; } if (target.Revision != -1 && Revision != target.Revision) { return false; } return true; } } class OReplacement : OAssembly { public OReplacement(string name, string version) : base(name, version) { } public OAssembly[] replacements; } class OCodegen { public string[] parameters; } class OLibrary { public readonly string Name; public string Cache; // products: public string Final; public OLibrary(string name) { this.Name = name; } } class OLinker { public string[] parameters; public OLibrary[] libraries; } class Options { public OReplacement[] assemblies; public OCodegen codegen; public OLinker linker; } ////////////////////////////////////////////////////////////////////////// // #if !SINGULARITY private Options ParseOptionsManifest(string optionsFile, out XmlDocument optManifest) #else private Options ParseOptionsManifest(string optionsFile, out XmlNode optManifest) #endif { // // // // // // // // // // // // #if SINGULARITY // read the file optManifest = NibHelper.OpenXmlDocument(optionsFile); if (optManifest != null) Console.WriteLine("parse of {0} successful", optionsFile); else Console.WriteLine("parse of {0} failed", optionsFile); #else optManifest = new XmlDocument(); optManifest.Load(optionsFile); #endif XmlNode options = GetChild(optManifest, "options"); if (options == null) { WriteLine("nib: Error: missing top-level tag."); return null; } Options opts = new Options(); XmlNode[] assemblies = GetChildren(options, "assembly"); if (assemblies.Length != 0) { opts.assemblies = new OReplacement[assemblies.Length]; for (int a = 0; a < assemblies.Length; a++) { XmlNode anode = assemblies[a]; string name = GetAttribute(anode, "name"); string version = GetAttribute(anode, "version"); if (name == null) { WriteLine("nib: Error: tag missing 'name' attribute."); return null; } OReplacement assembly = new OReplacement(name, version); assembly.PublicKey = GetAttribute(anode, "publickey"); assembly.Locale = GetAttribute(anode, "locale"); XmlNode[] replacements = GetChildren(anode, "replacement"); if (replacements.Length == 0) { WriteLine("nib: Error: tag missing child."); return null; } assembly.replacements = new OAssembly[replacements.Length]; for (int r = 0; r < replacements.Length; r++) { XmlNode rnode = replacements[r]; name = GetAttribute(rnode, "name"); version = GetAttribute(rnode, "version"); if (name == null) { WriteLine("nib: Error: tag missing 'name' attribute."); return null; } OAssembly replacement = new OAssembly(name, version); replacement.PublicKey = GetAttribute(rnode, "publickey"); replacement.Locale = GetAttribute(rnode, "locale"); replacement.Cache = GetAttribute(rnode, "cache"); assembly.replacements[r] = replacement; } opts.assemblies[a] = assembly; } } // Parse the codegen section. XmlNode codegen = GetChild(options, "codegen"); if (codegen == null) { WriteLine("nib: Error: tag missing child."); } opts.codegen = new OCodegen(); XmlNode[] parameters = GetChildren(codegen, "parameter"); if (parameters.Length != 0) { string[] pars = new string [parameters.Length]; for (int p = 0; p < parameters.Length; p++) { pars[p] = GetAttribute(parameters[p], "value"); if (pars[p] == null) { WriteLine("nib: Error: tag missing 'value' attribute."); return null; } } opts.codegen.parameters = pars; } // Parse the linker section. XmlNode linker = GetChild(options, "linker"); if (linker == null) { WriteLine("nib: Error: tag missing child."); } opts.linker = new OLinker(); parameters = GetChildren(linker, "parameter"); if (parameters.Length != 0) { string[] pars = new string [parameters.Length]; for (int p = 0; p < parameters.Length; p++) { pars[p] = GetAttribute(parameters[p], "value"); if (pars[p] == null) { WriteLine("nib: Error: tag missing 'value' attribute."); return null; } } opts.linker.parameters = pars; } XmlNode[] libraries = GetChildren(linker, "library"); if (libraries.Length != 0) { opts.linker.libraries = new OLibrary [libraries.Length]; for (int l = 0; l < libraries.Length; l++) { XmlNode lnode = libraries[l]; string name = GetAttribute(lnode, "name"); if (name == null) { WriteLine("nib: Error: tag missing 'name' attribute."); return null; } OLibrary library = new OLibrary(name); library.Cache = GetAttribute(lnode, "cache"); opts.linker.libraries[l] = library; } } return opts; } class Application { public readonly string Name; public string Path; public string Cache; public OAssembly[] assemblies; // products: public string obj; public string info; public string super; public string pdb; public string x86; public string xman; // Output manifest public string map; public string log; public OCodegen codegen; public OLinker linker; public Application(string name) { this.Name = name; } } ///////////////////////////////////////////////////// Manifest Processing. // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // #if !SINGULARITY private Application ParseApplicationManifest(string applicationFile, out XmlDocument appManifest) { appManifest = new XmlDocument(); appManifest.Load(applicationFile); #else private Application ParseApplicationManifest(string applicationFile, out XmlNode appManifest) { appManifest = NibHelper.CreateXmlDocument(applicationFile); #endif XmlNode application = GetChild(appManifest, "application"); if (application == null) { WriteLine("nib: Error: missing top-level tag."); return null; } string name = GetAttribute(application, "name"); if (name == null) { WriteLine("nib: Error: tag missing 'name' attribute."); return null; } XmlNode process = GetChild(application, "process"); if (process == null) { WriteLine("nib: Error: tag missing child tag."); return null; } Application app = new Application(name); app.Path = GetAttribute(process, "path"); app.Cache = GetAttribute(process, "cache"); XmlNode assemblyHolder = GetChild(process, "assemblies"); if (assemblyHolder == null) { WriteLine("nib: Error: tag missing child tag."); return null; } XmlNode[] assemblies = GetChildren(assemblyHolder, "assembly"); if (assemblies.Length != 0) { app.assemblies = new OAssembly[assemblies.Length]; for (int a = 0; a < assemblies.Length; a++) { XmlNode anode = assemblies[a]; name = GetAttribute(anode, "name"); string version = GetAttribute(anode, "version"); if (name == null) { WriteLine("nib: Error: tag missing 'name' attribute."); return null; } OAssembly assembly = new OAssembly(name, version); assembly.Cache = GetAttribute(anode, "cache"); assembly.Locale = GetAttribute(anode, "locale"); assembly.PublicKey = GetAttribute(anode, "publickey"); app.assemblies[a] = assembly; } } // Parse the optional codegen section. XmlNode codegen = GetChild(process, "codegen"); if (codegen != null) { app.codegen = new OCodegen(); XmlNode[] parameters = GetChildren(codegen, "parameter"); if (parameters.Length != 0) { string[] pars = new string [parameters.Length]; for (int p = 0; p < parameters.Length; p++) { pars[p] = GetAttribute(parameters[p], "value"); if (pars[p] == null) { WriteLine("nib: Error: tag missing 'value' attribute."); return null; } } app.codegen.parameters = pars; } } // Parse the optional linker section. XmlNode linker = GetChild(process, "linker"); if (linker != null) { app.linker = new OLinker(); XmlNode[] parameters = GetChildren(linker, "parameter"); if (parameters.Length != 0) { string[] pars = new string [parameters.Length]; for (int p = 0; p < parameters.Length; p++) { pars[p] = GetAttribute(parameters[p], "value"); if (pars[p] == null) { WriteLine("nib: Error: tag missing 'value' attribute."); return null; } } app.linker.parameters = pars; } #if false // For now, we don't allow libraries in the application linker section. XmlNode[] libraries = GetChildren(linker, "library"); if (libraries.Length != 0) { app.linker.libraries = new OLibrary [libraries.Length]; for (int l = 0; l < libraries.Length; l++) { XmlNode lnode = libraries[l]; string lname = GetAttribute(lnode, "name"); if (lname == null) { WriteLine("nib: Error: tag missing 'name' attribute."); return null; } OLibrary library = new OLibrary(lname); library.Cache = GetAttribute(lnode, "cache"); app.linker.libraries[l] = library; } } #endif } return app; } private bool SetMaxChildId(XmlNode root, string xmlGroup) { XmlNode group = GetChild(root, xmlGroup); if (group != null) { int maxId = -1; foreach (XmlNode node in group.ChildNodes) { int id = GetAttribute(node, "id", -1); if (id == -1) { id = maxId + 1; InsertAttribute(node, "id", id.ToString()); } maxId = Math.Max(maxId, id); } InsertAttribute(group, "length", (maxId + 1).ToString()); return true; } else { group = AddElement(root, xmlGroup); AddAttribute(group, "length", "0"); } return false; } private bool InstallApplicationManifest() { XmlNode application = GetChild(appManifest, "application"); if (application == null) { WriteLine("nib: Error: missing top-level tag."); return false; } int maxProcessId = -1; foreach (XmlNode process in GetChildren(application, "process")) { int maxCategoryId = -1; maxProcessId = Math.Max(maxProcessId, GetAttribute(process, "id", -1)); // look for categories node foreach (XmlNode categories in GetChildren(process, "categories")) { foreach (XmlNode category in GetChildren(categories, "category")) { maxCategoryId++; SetMaxChildId(category, "endpoints"); string name = GetAttribute(category, "name", ""); if (name == "driver") { SetMaxChildId(category, "fixedHardware"); SetMaxChildId(category, "dynamicHardware"); SetMaxChildId(category, "configs"); } // set these for all categories SetMaxChildId(category, "StringParameters"); SetMaxChildId(category, "StringArrayParameters"); SetMaxChildId(category, "LongParameters"); SetMaxChildId(category, "BoolParameters"); } } AddAttribute(process, "categories", (maxCategoryId + 1).ToString()); } AddAttribute(application, "processes", (maxProcessId + 1).ToString()); return true; } // ////////////////////////////////////////////////////////////////////////// private OAssembly[] FindReplacements(OAssembly assembly) { if (options == null) { return null; } OAssembly[] assemblies = options.assemblies; for (int a = 0; a < assemblies.Length; a++) { if (assembly.IsMatch(assemblies[a])) { return ((OReplacement)assemblies[a]).replacements; } } return null; } private OAssembly[] FindReplacements(OAssembly[] assemblies) { // Short circuit search if there are no replacement assemblies. if (options == null || options.assemblies == null) { return assemblies; } ArrayList list = new ArrayList(); for (int a = 0; a < assemblies.Length; a++) { OAssembly[] replacements = FindReplacements(assemblies[a]); if (replacements != null) { for (int r = 0; r < replacements.Length; r++) { if (Verbose) { WriteLine("Replacing {0} with {1}", assemblies[a], replacements[r]); } list.Add(replacements[r]); } } else { list.Add(assemblies[a]); } } OAssembly[] final = new OAssembly[list.Count]; for (int a = 0; a < list.Count; a++) { final[a] = (OAssembly)list[a]; } return final; } public void DeleteIfExists(string file) { if (File.Exists(file)) { File.Delete(file); if (Verbose) { WriteLine("deleted {0}", file); } } } private static string ReplaceSuffix(string value, string old, string suffix) { if (value.EndsWith(old)) { return value.Substring(0, value.Length - old.Length) + suffix; } return value + suffix; } // Returns true of the application was built or is up to date. #if !SINGULARITY private bool BuildApplication(string applicationFile) { #else private bool BuildApplication(string applicationFile, PipeMultiplexer! outputMux) { #endif StreamWriter log = null; DateTime logBegin = DateTime.Now; bool doCodegen = DoCodegen; bool doLinker = DoLinker; bool doManifest = DoManifest; bool doForceCodegen = ForceCodegen; bool doForceLinker = ForceLinker; bool doForceManifest = ForceManifest; try { Application app = ParseApplicationManifest(applicationFile, out appManifest); if (app == null) { // parse failed. return false; } if (app.Path == null || app.Path == "") { WriteLine( "nib: Warning: No executable in manifest: {0}.", applicationFile); doCodegen = false; doLinker = false; doForceCodegen = false; doForceLinker = false; // This is hack just for the sake of the kernel. app.Path = app.Name + ".x86"; } WriteLine("\t{0}", applicationFile); app.obj = tempDirectory + app.Name + ".obj"; app.info = tempDirectory + app.Name + "_info.obj"; app.super = tempDirectory + app.Name + "_superObj.obj"; app.map = tempDirectory + app.Name + ".map"; app.log = tempDirectory + app.Name + ".log"; if (nativeDirectory != null) { app.x86 = nativeDirectory + app.Path; } else { app.x86 = cacheDirectory + app.Cache.Replace('/', '\\'); } app.xman = ReplaceSuffix(app.x86, ".x86", ".manifest"); app.pdb = tempDirectory + app.Path + ".pdb"; DateTime written; DateTime update = optionsFile == null ? DateTime.MinValue : File.GetLastWriteTime(optionsFile); DateTime newest = File.GetLastWriteTime(applicationFile); if (update < newest) { update = newest; } if (Verbose) { WriteLine("Name = [{0}]", app.Name); WriteLine("Path = [{0}]", app.Path); WriteLine("Cache= [{0}]", app.Cache); WriteLine("obj = [{0}]", app.obj); WriteLine("info = [{0}]", app.info); WriteLine("super= [{0}]", app.super); WriteLine("pdb = [{0}]", app.pdb); WriteLine("x86 = [{0}]", app.x86); WriteLine("xman = [{0}]", app.xman); WriteLine("map = [{0}]", app.map); WriteLine("log = [{0}]", app.log); } if (DoClean) { DeleteIfExists(app.obj); DeleteIfExists(app.info); DeleteIfExists(app.super); DeleteIfExists(app.pdb); DeleteIfExists(app.x86); DeleteIfExists(app.xman); DeleteIfExists(app.map); DeleteIfExists(app.log); return true; } #if VERY_VERBOSE WriteLine(" >>> Pre replace ***"); for (int a = 0; a < app.assemblies.Length; a++) { if (Verbose) { WriteLine(" {0}", app.assemblies[a]); WriteLine(" {0}", app.assemblies[a].Cache); WriteLine(" {0}", app.assemblies[a].Final); } } #endif // Now that we've parsed the manifest, we replace any mapped assemblies. OAssembly[] assemblies = FindReplacements(app.assemblies); #if VERY_VERBOSE WriteLine(" <<< Post replace ***"); #endif // Now, find the final code (if it isn't already cached). for (int a = 0; a < assemblies.Length; a++) { string test = cacheDirectory + assemblies[a].Name; if (File.Exists(test)) { assemblies[a].Final = test; } else { if (assemblies[a].Cache == null) { WriteLine("nib: Error: Couldn't find assembly: {0}", assemblies[a]); return false; } string assemblyCachePath = assemblies[a].Cache.Replace('/', '\\'); string finalAssemblyPath = Path.Combine(cacheDirectory, assemblyCachePath); assemblies[a].Final = finalAssemblyPath; } #if VERY_VERBOSE if (Verbose) { WriteLine(" {0}", assemblies[a]); WriteLine(" {0}", assemblies[a].Cache); WriteLine(" {0}", assemblies[a].Final); } #endif } if (doCodegen) { // Verify that all of the assemblies exist. bool errorFound = false; for (int a = 0; a < assemblies.Length; a++) { if (!File.Exists(assemblies[a].Final)) { WriteLine("nib: Error: Couldn't assembly file: {0}", assemblies[a].Final); errorFound = true; } } if (errorFound) return false; // Find the newest assembly. newest = update; for (int a = 0; a < assemblies.Length; a++) { written = File.GetLastWriteTime(assemblies[a].Final); if (newest < written) { newest = written; } } DateTime target = DateTime.MinValue; if (File.Exists(app.obj)) { target = File.GetLastWriteTime(app.obj); } if (newest >= target || doForceCodegen) { // Codegen is necessary. //