2110 lines
81 KiB
C#
2110 lines
81 KiB
C#
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 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
|
||
|
// <name>
|
||
|
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
|
||
|
{
|
||
|
// <options>
|
||
|
// <assembly name= version= publickey= locale= />
|
||
|
// <replacement name= cache= version= publickey= locale= />
|
||
|
// </assembly>
|
||
|
// <codegen>
|
||
|
// <parameter value= />
|
||
|
// </codegen>
|
||
|
// <linker>
|
||
|
// <parameter value= />
|
||
|
// <library name= cache= />
|
||
|
// </linker>
|
||
|
// <options>
|
||
|
|
||
|
#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 <options> 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: <assembly> 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: <assembly> tag missing <replacement> 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: <replacement> 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: <options> tag missing <codegen> 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: <parameter> 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: <options> tag missing <linker> 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: <parameter> 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: <library> 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.
|
||
|
//
|
||
|
// <application name=>
|
||
|
// <process id= main= path= cache=>
|
||
|
// <assemblies>
|
||
|
// <assembly name= version= publickey= cache= />
|
||
|
// </assemblies>
|
||
|
// <categories>
|
||
|
// <category id= name= class=>
|
||
|
// <device signature= />
|
||
|
// <ioPortRange id= baseAddress= rangeLength= />
|
||
|
// <ioIrqRange id= baseAddress= rangeLength= />
|
||
|
// <ioDmaRange id= baseAddress= rangeLength= />
|
||
|
// <ioMemoryRange addressBits= alignment= rangeLength= fixed= />
|
||
|
// <extension id= startStateId= contractName= endpointEnd= assembly=...>
|
||
|
// <imp>
|
||
|
// <inherit name= />
|
||
|
// </imp>
|
||
|
// <exp>
|
||
|
// <inherit name= />
|
||
|
// </exp>
|
||
|
// </extension>
|
||
|
// <serviceProvider id= startStateId= contractName= endpointEnd= assembly=...>
|
||
|
// <imp>
|
||
|
// <inherit name= />
|
||
|
// </imp>
|
||
|
// <exp>
|
||
|
// <inherit name= />
|
||
|
// </exp>
|
||
|
// </serviceProvider>
|
||
|
// </category>
|
||
|
// </categories>
|
||
|
// <codegen>
|
||
|
// <parameter value= />
|
||
|
// </codegen>
|
||
|
// <linker>
|
||
|
// <parameter value= />
|
||
|
// </linker>
|
||
|
// </process>
|
||
|
// </application>
|
||
|
//
|
||
|
#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 <application> tag.");
|
||
|
return null;
|
||
|
}
|
||
|
string name = GetAttribute(application, "name");
|
||
|
if (name == null) {
|
||
|
WriteLine("nib: Error: <application> tag missing 'name' attribute.");
|
||
|
return null;
|
||
|
}
|
||
|
XmlNode process = GetChild(application, "process");
|
||
|
if (process == null) {
|
||
|
WriteLine("nib: Error: <application> tag missing <process> 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: <process> tag missing <assemblies> 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: <assembly> 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: <parameter> 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: <parameter> 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: <library> 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 <application> 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.
|
||
|
|
||
|
// <option>
|
||
|
// /out: ..\obj\Prototype.MarkSweep\Shell.obj
|
||
|
// /outdir: ..\obj\Prototype.MarkSweep
|
||
|
// <assembly>
|
||
|
|
||
|
StringBuilder sb = new StringBuilder(4096);
|
||
|
if (machine == "x64") sb.Append(" /x64 ");
|
||
|
if (options != null) {
|
||
|
foreach (string arg in options.codegen.parameters) {
|
||
|
sb.Append(" ");
|
||
|
sb.Append(arg);
|
||
|
}
|
||
|
}
|
||
|
if (app.codegen != null) {
|
||
|
foreach (string arg in app.codegen.parameters) {
|
||
|
sb.Append(" ");
|
||
|
sb.Append(arg);
|
||
|
}
|
||
|
}
|
||
|
sb.Append(" /out: \"");
|
||
|
sb.Append(app.obj);
|
||
|
sb.Append("\"");
|
||
|
sb.Append(" /outdir: \"");
|
||
|
sb.Append(tempDirectory.Trim('\\'));
|
||
|
sb.Append("\"");
|
||
|
for (int a = 0; a < assemblies.Length; a++) {
|
||
|
sb.Append(" \"");
|
||
|
sb.Append(assemblies[a].Final);
|
||
|
sb.Append("\"");
|
||
|
}
|
||
|
|
||
|
log = new StreamWriter(app.log);
|
||
|
log.WriteLine("# Name = [{0}]", app.Name);
|
||
|
log.WriteLine("# Path = [{0}]", app.Path);
|
||
|
log.WriteLine("# Cache= [{0}]", app.Cache);
|
||
|
log.WriteLine("# obj = [{0}]", app.obj);
|
||
|
log.WriteLine("# info = [{0}]", app.info);
|
||
|
log.WriteLine("# super= [{0}]", app.super);
|
||
|
log.WriteLine("# pdb = [{0}]", app.pdb);
|
||
|
log.WriteLine("# x86 = [{0}]", app.x86);
|
||
|
log.WriteLine("# map = [{0}]", app.map);
|
||
|
log.WriteLine("# log = [{0}]", app.log);
|
||
|
log.WriteLine("# target = {0}", target);
|
||
|
log.WriteLine("#newest = {0}", newest);
|
||
|
|
||
|
log.WriteLine("#{0} {1}", Bartok, sb.ToString());
|
||
|
if (Verbose) {
|
||
|
WriteLine(sb.ToString());
|
||
|
}
|
||
|
#if !SINGULARITY
|
||
|
if (!RunProgram(app.Name, Bartok, sb.ToString(), log)) {
|
||
|
#else
|
||
|
if (!RunProgram(app.Name, Bartok, sb.ToString(), log, outputMux)) {
|
||
|
#endif
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (Verbose) {
|
||
|
WriteLine("nib: Codegen unneeded: {0}", app.obj);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now that we've parsed the manifest, we replace any mapped libraries.
|
||
|
OLibrary[] libraries =
|
||
|
options != null ? options.linker.libraries : new OLibrary[0];
|
||
|
|
||
|
// Now, find the final code (if it isn't already cached).
|
||
|
for (int l = 0; l < libraries.Length; l++) {
|
||
|
string test = libCacheDirectory + libraries[l].Name;
|
||
|
//WriteLine(" looking for final code @ {0}", test);
|
||
|
if (File.Exists(test)) {
|
||
|
libraries[l].Final = test;
|
||
|
if (libraries[l].Cache == null) {
|
||
|
libraries[l].Cache = libraries[l].Name;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (libraries[l].Cache == null) {
|
||
|
WriteLine("nib: Error: Couldn't find library: {0}",
|
||
|
libraries[l]);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
libraries[l].Final = Path.Combine(cacheDirectory,
|
||
|
libraries[l].Cache.Replace('/', '\\'));
|
||
|
}
|
||
|
|
||
|
if (Verbose) {
|
||
|
WriteLine(" {0}", libraries[l].Cache);
|
||
|
WriteLine(" {0}", libraries[l].Final);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (doLinker) {
|
||
|
// Verify that all of the libraries exist.
|
||
|
for (int l = 0; l < libraries.Length; l++) {
|
||
|
if (!File.Exists(libraries[l].Final)) {
|
||
|
WriteLine("nib: Error: Couldn't find library: {0}", libraries[l].Final);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!File.Exists(app.obj)) {
|
||
|
WriteLine("nib: Error: Couldn't find file: {0}", app.obj);
|
||
|
return false;
|
||
|
}
|
||
|
if (!File.Exists(app.super)) {
|
||
|
WriteLine("nib: Error: Couldn't find file: {0}", app.super);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Find the newest library.
|
||
|
newest = update;
|
||
|
for (int l = 0; l < libraries.Length; l++) {
|
||
|
written = File.GetLastWriteTime(libraries[l].Final);
|
||
|
if (newest < written) {
|
||
|
newest = written;
|
||
|
}
|
||
|
}
|
||
|
written = File.GetLastWriteTime(app.obj);
|
||
|
if (newest < written) {
|
||
|
newest = written;
|
||
|
}
|
||
|
written = File.GetLastWriteTime(app.super);
|
||
|
if (newest < written) {
|
||
|
newest = written;
|
||
|
}
|
||
|
|
||
|
DateTime target = DateTime.MinValue;
|
||
|
if (File.Exists(app.x86)) {
|
||
|
target = File.GetLastWriteTime(app.x86);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (newest >= target || doForceLinker) {
|
||
|
// Linking is necessary
|
||
|
|
||
|
// <option>
|
||
|
// /out:..\obj\Prototype.MarkSweep\Shell.x86
|
||
|
// /pdb:..\obj\Prototype.MarkSweep\Shell.x86.pdb
|
||
|
// ..\obj\Prototype.MarkSweep\Shell.obj
|
||
|
// ..\obj\Prototype.MarkSweep\Shell_superObj.obj
|
||
|
// <library>
|
||
|
StringBuilder sb = new StringBuilder(4096);
|
||
|
if (options != null) {
|
||
|
foreach (string arg in options.linker.parameters) {
|
||
|
sb.Append(" ");
|
||
|
sb.Append(arg);
|
||
|
}
|
||
|
}
|
||
|
if (app.linker != null) {
|
||
|
foreach (string arg in app.linker.parameters) {
|
||
|
sb.Append(" ");
|
||
|
sb.Append(arg);
|
||
|
}
|
||
|
}
|
||
|
sb.Append(" /machine:");
|
||
|
sb.Append(machine);
|
||
|
sb.Append(" /out:");
|
||
|
sb.Append(app.x86);
|
||
|
sb.Append(" /pdb:");
|
||
|
sb.Append(app.pdb);
|
||
|
sb.Append(" /map:");
|
||
|
sb.Append(app.map);
|
||
|
sb.Append(" ");
|
||
|
sb.Append(app.obj);
|
||
|
sb.Append(" ");
|
||
|
sb.Append(app.super);
|
||
|
for (int l = 0; l < libraries.Length; l++) {
|
||
|
sb.Append(" ");
|
||
|
sb.Append(libraries[l].Final);
|
||
|
}
|
||
|
String args = sb.ToString();
|
||
|
|
||
|
#if !SINGULARITY
|
||
|
if (log != null) {
|
||
|
log.WriteLine("#{0} {1}", "link.exe", args);
|
||
|
}
|
||
|
if (!RunProgram(app.Name, "link.exe", args, null)) {
|
||
|
return false;
|
||
|
}
|
||
|
#else
|
||
|
if (log != null) {
|
||
|
log.WriteLine("#{0} {1}", "link", args);
|
||
|
}
|
||
|
if (!RunProgram(app.Name, "link", args, null, outputMux)) {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if CALL_MKNAME
|
||
|
args = "/u " + app.x86;
|
||
|
if (log != null) {
|
||
|
log.WriteLine("#{0} {1}", "mkname.exe", args);
|
||
|
}
|
||
|
#if !SINGULARITY
|
||
|
if (!RunProgram(app.Name, "mkname.exe", args, null)) {
|
||
|
#else
|
||
|
if (!RunProgram(app.Name, "mkname.exe", args, null, PipeMultiplexer! outputMux)) {
|
||
|
#endif
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
if (Verbose) {
|
||
|
WriteLine("nib: Linker unneeded: {0}", app.x86);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (doManifest) {
|
||
|
// Find the newest library.
|
||
|
newest = File.GetLastWriteTime(applicationFile);
|
||
|
if (newest < update) {
|
||
|
newest = update;
|
||
|
}
|
||
|
DateTime target = DateTime.MinValue;
|
||
|
if (File.Exists(app.xman)) {
|
||
|
target = File.GetLastWriteTime(app.xman);
|
||
|
}
|
||
|
|
||
|
if (newest >= target || doForceManifest) {
|
||
|
//////////////////////////////////////////////////////////
|
||
|
// Update the installed manifest and write it out.
|
||
|
//
|
||
|
if (!InstallApplicationManifest()) {
|
||
|
return false;
|
||
|
}
|
||
|
#if SINGULARITY
|
||
|
Console.WriteLine(" manifest ->{0}",app.xman);
|
||
|
FileStream fs =new FileStream("/fs/temp/outfile", FileMode.Create,
|
||
|
FileAccess.Write, FileShare.None);
|
||
|
XmlWriter writer = new XmlWriter(fs, System.Text.Encoding.UTF8);
|
||
|
//writer.Formatting = Formatting.Indented;
|
||
|
NibHelper.SaveXmlDocument(writer, appManifest);
|
||
|
writer.Close();
|
||
|
#else
|
||
|
XmlTextWriter writer = new XmlTextWriter(app.xman,
|
||
|
System.Text.Encoding.UTF8);
|
||
|
writer.Formatting = Formatting.Indented;
|
||
|
appManifest.Save(writer);
|
||
|
writer.Close();
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
if (Verbose) {
|
||
|
WriteLine("nib: Manifest generation unneeded: {0}", app.xman);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
finally {
|
||
|
if (log != null) {
|
||
|
TimeSpan elapsed = DateTime.Now - logBegin;
|
||
|
log.WriteLine("# {0} seconds elapsed.", elapsed.TotalSeconds);
|
||
|
log.Close();
|
||
|
}
|
||
|
appManifest = null;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
#region Parallel Build Support
|
||
|
|
||
|
// Per-worker state
|
||
|
string _PrintPrefix = "";
|
||
|
IntPtr _ChildProcessorAffinityMask;
|
||
|
|
||
|
// Global state
|
||
|
static bool _ShowDots = true;
|
||
|
readonly static Object _PrintLock = new Object();
|
||
|
static IntPtr _JobHandle;
|
||
|
|
||
|
void WriteLine(string line)
|
||
|
{
|
||
|
if (_BuildingParallel)
|
||
|
Monitor.Enter(_PrintLock);
|
||
|
|
||
|
if (_PrintPrefix != null && _PrintPrefix.Length != 0)
|
||
|
Console.WriteLine(_PrintPrefix + line);
|
||
|
else {
|
||
|
#if SINGULARITY
|
||
|
DebugStub.WriteLine(line);
|
||
|
#endif
|
||
|
Console.WriteLine(line);
|
||
|
}
|
||
|
if (_BuildingParallel)
|
||
|
Monitor.Exit(_PrintLock);
|
||
|
}
|
||
|
|
||
|
void WriteLine(string format, params object[] args)
|
||
|
{
|
||
|
WriteLine(String.Format(format, args));
|
||
|
}
|
||
|
|
||
|
|
||
|
class QueueJob
|
||
|
{
|
||
|
public string Filename;
|
||
|
public bool Result;
|
||
|
}
|
||
|
|
||
|
// Contains
|
||
|
static readonly Object _JobQueueMonitor = new Object();
|
||
|
static readonly Queue _WaitingJobQueue = new Queue();
|
||
|
static readonly ArrayList _FinishedJobList = new ArrayList();
|
||
|
static bool _BuildingParallel;
|
||
|
|
||
|
class WorkerThread
|
||
|
{
|
||
|
public string DebugPrefix = "";
|
||
|
|
||
|
// zero-based processor index
|
||
|
public int Id;
|
||
|
public Thread Thread;
|
||
|
|
||
|
public TimeSpan KernelTime;
|
||
|
public TimeSpan UserTime;
|
||
|
|
||
|
|
||
|
public WorkerThread(NativeImageBuilder nib, int id)
|
||
|
{
|
||
|
this.Id = id;
|
||
|
this.nib = nib;
|
||
|
this.DebugPrefix = String.Format("{0}> ", id);
|
||
|
this._printPrefix = String.Format("{0}> ", id);
|
||
|
}
|
||
|
|
||
|
string _printPrefix;
|
||
|
|
||
|
NativeImageBuilder nib;
|
||
|
|
||
|
void DebugLine(string line)
|
||
|
{
|
||
|
#if !SINGULARITY
|
||
|
Debug.WriteLine(DebugPrefix + line);
|
||
|
#else
|
||
|
DebugStub.WriteLine(DebugPrefix + line);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void DebugLine(string format, params object[] args)
|
||
|
{
|
||
|
DebugLine(String.Format(format, args));
|
||
|
}
|
||
|
|
||
|
void WriteLine(string line)
|
||
|
{
|
||
|
Console.WriteLine(DebugPrefix + line);
|
||
|
}
|
||
|
|
||
|
void WriteLine(string format, params object[] args)
|
||
|
{
|
||
|
WriteLine(String.Format(format, args));
|
||
|
}
|
||
|
|
||
|
public void ThreadMain()
|
||
|
{
|
||
|
try {
|
||
|
RunQueuedJobs();
|
||
|
}
|
||
|
catch (Exception e) {
|
||
|
ShowException(e);
|
||
|
Environment.Exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void RunQueuedJobs()
|
||
|
{
|
||
|
DebugLine("worker thread started");
|
||
|
#if SINGULARITY
|
||
|
// build a pipe mux
|
||
|
// Use a mux to splice our own output together with the child
|
||
|
// processes we will run.
|
||
|
// Make ourselves a new output pipe
|
||
|
UnicodePipeContract.Exp! newOutputExp;
|
||
|
UnicodePipeContract.Imp! newOutputImp;
|
||
|
UnicodePipeContract.NewChannel(out newOutputImp, out newOutputExp);
|
||
|
|
||
|
// Retrieve our real stdOut and start using the one we just created
|
||
|
// instead
|
||
|
UnicodePipeContract.Imp stdOut = ConsoleOutput.Swap(newOutputImp);
|
||
|
PipeMultiplexer! outputMux = PipeMultiplexer.Start(stdOut, newOutputExp);
|
||
|
#endif
|
||
|
for (;;) {
|
||
|
QueueJob job;
|
||
|
|
||
|
lock (_JobQueueMonitor)
|
||
|
{
|
||
|
if (_WaitingJobQueue.Count == 0) {
|
||
|
DebugLine("no more jobs in queue -- exiting");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
job = (QueueJob)_WaitingJobQueue.Dequeue();
|
||
|
|
||
|
}
|
||
|
|
||
|
// Execute the job.
|
||
|
|
||
|
DebugLine("starting job - " + job.Filename);
|
||
|
job.Result = false;
|
||
|
try {
|
||
|
#if !SINGULARITY
|
||
|
job.Result = nib.BuildApplication(job.Filename);
|
||
|
#else
|
||
|
job.Result = nib.BuildApplication(job.Filename, outputMux);
|
||
|
#endif
|
||
|
}
|
||
|
catch (Exception e) {
|
||
|
Console.WriteLine("Failed building {0}\n", job.Filename);
|
||
|
throw e;
|
||
|
}
|
||
|
|
||
|
DebugLine("job finished - " + job.Filename);
|
||
|
|
||
|
if (!job.Result)
|
||
|
DebugLine("job FAILED - " + job.Filename);
|
||
|
|
||
|
lock (_JobQueueMonitor)
|
||
|
{
|
||
|
_FinishedJobList.Add(job);
|
||
|
}
|
||
|
}
|
||
|
#if !SINGULARITY
|
||
|
long creationTime;
|
||
|
long exitTime;
|
||
|
long kernelTime;
|
||
|
long userTime;
|
||
|
if (Kernel32.GetThreadTimes(Kernel32.GetCurrentThread(), out creationTime, out exitTime, out kernelTime, out userTime)) {
|
||
|
this.KernelTime = new TimeSpan(kernelTime);
|
||
|
this.UserTime = new TimeSpan(userTime);
|
||
|
}
|
||
|
else {
|
||
|
WriteLine("Failed to get thread times! error = " + Marshal.GetLastWin32Error());
|
||
|
}
|
||
|
#else
|
||
|
WriteLine("need to fix up thread times");
|
||
|
outputMux.Dispose();
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void ShowException(Exception chain)
|
||
|
{
|
||
|
for (Exception ex = chain; ex != null; ex = ex.InnerException) {
|
||
|
Console.WriteLine("{0}: {1}", ex.GetType().FullName, ex.Message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
|
||
|
// Print the correct command line args for the program
|
||
|
static void Usage()
|
||
|
{
|
||
|
Console.WriteLine(
|
||
|
"Usage:\n" +
|
||
|
" nib /cache:<path> [options] app.manifest [app1.manifest ...]\n" +
|
||
|
"Options:\n" +
|
||
|
" /apps:<file> - File containing list of app.manifests.\n" +
|
||
|
" /bartok:<file> - Specify Bartok to use.\n" +
|
||
|
" /c - Code generation only, don't link.\n" +
|
||
|
" /clean - Clean up output only.\n" +
|
||
|
" /cache:<path> - Root of file cache.\n" +
|
||
|
" /force - Force generation even if output files are newer.\n" +
|
||
|
" /link - Link only, don't compile.\n" +
|
||
|
" /libcache:<path> - Root of Architecture-dependent cache.\n" +
|
||
|
" /machine:<arch> - Binary target architecture.\n" +
|
||
|
" /manifest - Generate reified manifest only.\n" +
|
||
|
" /native:<path> - Root of native files.\n" +
|
||
|
" /options:<file> - Options manifest file.\n" +
|
||
|
" /temp:<path> - Temporary directory.\n" +
|
||
|
" /v - Verbose logging output.\n" +
|
||
|
" /par - Disable parallel builds.\n" +
|
||
|
" /par:<nn> - Set to use <nn> parallel processors.\n" +
|
||
|
"Summary:\n" +
|
||
|
" Create a native image for each application manifest.\n" +
|
||
|
"");
|
||
|
}
|
||
|
|
||
|
static bool ReadInfilesFromFile(string filePath, ArrayList infiles)
|
||
|
{
|
||
|
try {
|
||
|
using (StreamReader sr = new StreamReader(filePath)) {
|
||
|
string line;
|
||
|
while ((line = sr.ReadLine()) != null) {
|
||
|
infiles.Add(line);
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
catch (FileNotFoundException) {
|
||
|
Console.WriteLine("File not found {0}\n", filePath);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int Main(string[] args)
|
||
|
{
|
||
|
ArrayList infiles = new ArrayList();
|
||
|
string optionsFile = null;
|
||
|
string cacheDirectory = null;
|
||
|
string libCacheDirectory = null;
|
||
|
string tempDirectory = null;
|
||
|
string nativeDirectory = null;
|
||
|
DateTime beg = DateTime.Now;
|
||
|
|
||
|
#if !SINGULARITY
|
||
|
string bartok = Environment.GetEnvironmentVariable("BARTOK");
|
||
|
if (bartok != null) {
|
||
|
Bartok = bartok;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if !SINGULARITY
|
||
|
int maxWorkerThreads = Kernel32.GetProcessorCount();
|
||
|
#else
|
||
|
int maxWorkerThreads = 1;
|
||
|
#endif
|
||
|
int numWorkerThreads = maxWorkerThreads;
|
||
|
|
||
|
// Temporaries for command-line parsing
|
||
|
bool needHelp = (args.Length == 0);
|
||
|
|
||
|
#if !SINGULARITY
|
||
|
for (int i = 0; i < args.Length && !needHelp; i++) {
|
||
|
#else
|
||
|
for (int i = 1; i < args.Length && !needHelp; i++) {
|
||
|
#endif
|
||
|
string arg = (string) args[i];
|
||
|
|
||
|
if (arg.Length >= 2 && (arg[0] == '-' || arg[0] == '/')) {
|
||
|
string name = null;
|
||
|
string value = null;
|
||
|
int n = arg.IndexOf(':');
|
||
|
if (n > -1) {
|
||
|
name = arg.Substring(1, n - 1).ToLower();
|
||
|
if (n < arg.Length + 1) {
|
||
|
value = arg.Substring(n + 1);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
name = arg.Substring(1).ToLower();
|
||
|
}
|
||
|
bool badArg = false;
|
||
|
switch (name) {
|
||
|
case "h":
|
||
|
case "help":
|
||
|
case "?":
|
||
|
needHelp = true;
|
||
|
break;
|
||
|
|
||
|
#if SINGULARITY
|
||
|
case "app":
|
||
|
if (value != null) {
|
||
|
infiles.Add(value);
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
#endif
|
||
|
case "apps":
|
||
|
if (value != null) {
|
||
|
#if !SINGULARITY
|
||
|
badArg = !ReadInfilesFromFile(value, infiles);
|
||
|
#else
|
||
|
Console.WriteLine("'apps' not supported");
|
||
|
needHelp = true;
|
||
|
break;
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "bartok":
|
||
|
if (value != null) {
|
||
|
Bartok = value.Trim(new char[] {'"'});
|
||
|
if (!File.Exists(Bartok)) {
|
||
|
Console.WriteLine("Bartok not found: {0}",
|
||
|
Bartok);
|
||
|
badArg = true;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "c":
|
||
|
DoCodegen = true;
|
||
|
DoLinker = false;
|
||
|
DoManifest = false;
|
||
|
break;
|
||
|
|
||
|
case "clean":
|
||
|
DoClean = true;
|
||
|
break;
|
||
|
|
||
|
case "cache":
|
||
|
if (value != null) {
|
||
|
cacheDirectory = value.TrimEnd('/', '\\') + "\\";
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "force":
|
||
|
ForceCodegen = true;
|
||
|
ForceLinker = true;
|
||
|
ForceManifest = true;
|
||
|
break;
|
||
|
|
||
|
case "libcache":
|
||
|
if (value != null) {
|
||
|
#if !SINGULARITY
|
||
|
libCacheDirectory = value.TrimEnd('/', '\\') + "\\";
|
||
|
#else
|
||
|
libCacheDirectory = value + "/";
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "link":
|
||
|
DoCodegen = false;
|
||
|
DoLinker = true;
|
||
|
DoManifest = true;
|
||
|
break;
|
||
|
|
||
|
case "manifest":
|
||
|
DoCodegen = false;
|
||
|
DoLinker = false;
|
||
|
DoManifest = true;
|
||
|
break;
|
||
|
|
||
|
case "log":
|
||
|
#if !SINGULARITY
|
||
|
cacheDirectory = value.TrimEnd('/', '\\') + "\\";
|
||
|
#else
|
||
|
cacheDirectory = value + "/";
|
||
|
#endif
|
||
|
break;
|
||
|
|
||
|
case "native":
|
||
|
if (value != null) {
|
||
|
#if true //!SINGULARITY
|
||
|
nativeDirectory = value.TrimEnd('/', '\\') + "\\";
|
||
|
#else
|
||
|
nativeDirectory = value + "/";
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "options":
|
||
|
if (value != null) {
|
||
|
optionsFile = value;
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "temp":
|
||
|
if (value != null) {
|
||
|
#if true //!SINGULARITY
|
||
|
tempDirectory = value.TrimEnd('/', '\\') + "\\";
|
||
|
#else
|
||
|
tempDirectory = value + "/";
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "v":
|
||
|
Verbose = true;
|
||
|
break;
|
||
|
|
||
|
case "par":
|
||
|
if (value != null) {
|
||
|
bool force = false;
|
||
|
if (value.EndsWith("!")) {
|
||
|
value = value.Substring(0, value.Length - 1);
|
||
|
force = true;
|
||
|
}
|
||
|
int count;
|
||
|
try {
|
||
|
#if !SINGULARITY
|
||
|
count = Convert.ToInt32(value);
|
||
|
#else
|
||
|
count = Int32.Parse(value);
|
||
|
#endif
|
||
|
}
|
||
|
catch {
|
||
|
badArg = true;
|
||
|
break;
|
||
|
}
|
||
|
if (count < 1) {
|
||
|
badArg = true;
|
||
|
break;
|
||
|
}
|
||
|
if (count > maxWorkerThreads && !force) {
|
||
|
Console.WriteLine("nib: Warning: Cannot use {0} worker threads; this machine only has {1} processors.", count, maxWorkerThreads);
|
||
|
Console.WriteLine("nib: Using {0} worker threads.", maxWorkerThreads);
|
||
|
count = maxWorkerThreads;
|
||
|
}
|
||
|
else {
|
||
|
numWorkerThreads = count;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Parallel builds disabled.
|
||
|
numWorkerThreads = 1;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "machine":
|
||
|
if (value != null) {
|
||
|
machine = value;
|
||
|
}
|
||
|
else {
|
||
|
badArg = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Console.WriteLine("nib: Unknown command line argument: {0}", arg);
|
||
|
needHelp = true;
|
||
|
break;
|
||
|
}
|
||
|
if (badArg) {
|
||
|
Console.WriteLine("nib: Invalid command line argument: {0}", arg);
|
||
|
needHelp = true;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
infiles.Add(arg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (machine == null) {
|
||
|
Console.WriteLine("No machine architecture specified. Terminating.");
|
||
|
Usage();
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (infiles.Count == 0 ||
|
||
|
cacheDirectory == null || tempDirectory == null) {
|
||
|
|
||
|
Console.WriteLine("nib: Arguments missing from command line.");
|
||
|
needHelp = true;
|
||
|
}
|
||
|
|
||
|
if (needHelp) {
|
||
|
Usage();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// check all input files
|
||
|
foreach (string filename in infiles) {
|
||
|
if (!File.Exists(filename)) {
|
||
|
Console.WriteLine("nib: Error: Application manifest '{0}' not found.",
|
||
|
filename);
|
||
|
return 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check all input files
|
||
|
if (optionsFile != null && !File.Exists(optionsFile)) {
|
||
|
Console.WriteLine("nib: Error: Options manifest '{0}' not found.",
|
||
|
optionsFile);
|
||
|
return 2;
|
||
|
}
|
||
|
if (!Directory.Exists(cacheDirectory)) {
|
||
|
Console.WriteLine("nib: Error: Cache directory '{0}' not found.",
|
||
|
cacheDirectory);
|
||
|
return 2;
|
||
|
}
|
||
|
if (!Directory.Exists(tempDirectory)) {
|
||
|
Console.WriteLine("nib: Error: Temp directory '{0}' not found.",
|
||
|
tempDirectory);
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
foreach (string filename in infiles) {
|
||
|
QueueJob job = new QueueJob();
|
||
|
job.Filename = filename;
|
||
|
_WaitingJobQueue.Enqueue(job);
|
||
|
}
|
||
|
|
||
|
#if !SINGULARITY
|
||
|
if (Kernel32.IsCurrentProcessInJob()) {
|
||
|
Console.WriteLine("nib: Current process is already in a job. Accounting disabled.");
|
||
|
_JobHandle = IntPtr.Zero;
|
||
|
}
|
||
|
else {
|
||
|
try {
|
||
|
IntPtr job = Kernel32.CreateJobObject();
|
||
|
_JobHandle = job;
|
||
|
}
|
||
|
catch (Exception ex)
|
||
|
{
|
||
|
ShowException(ex);
|
||
|
_JobHandle = IntPtr.Zero;
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
Console.WriteLine(" call to IsCurrentProcessJob");
|
||
|
#endif
|
||
|
int worker_count = numWorkerThreads;
|
||
|
|
||
|
if (worker_count > 1) {
|
||
|
Console.WriteLine("nib: Using {0} processors.", worker_count);
|
||
|
|
||
|
_BuildingParallel = true;
|
||
|
_ShowDots = false;
|
||
|
}
|
||
|
else {
|
||
|
Console.WriteLine("nib: Using 1 processor.");
|
||
|
_BuildingParallel = false;
|
||
|
_ShowDots = true;
|
||
|
}
|
||
|
|
||
|
WorkerThread[] workers = new WorkerThread[worker_count];
|
||
|
for (int i = 0; i < worker_count; i++) {
|
||
|
NativeImageBuilder nib = new NativeImageBuilder(cacheDirectory,
|
||
|
libCacheDirectory,
|
||
|
nativeDirectory,
|
||
|
tempDirectory);
|
||
|
|
||
|
if (optionsFile != null && !nib.LoadOptionsManifest(optionsFile)) {
|
||
|
return 3;
|
||
|
}
|
||
|
|
||
|
if (_BuildingParallel) {
|
||
|
nib._PrintPrefix = String.Format("{0}> ", i);
|
||
|
nib._ChildProcessorAffinityMask = (IntPtr)(1 << i);
|
||
|
}
|
||
|
else {
|
||
|
nib._PrintPrefix = "";
|
||
|
nib._ChildProcessorAffinityMask = IntPtr.Zero;
|
||
|
}
|
||
|
|
||
|
WorkerThread worker = new WorkerThread(nib, i);
|
||
|
Thread thread = new Thread(new ThreadStart(worker.ThreadMain));
|
||
|
#if !SINGULARITY
|
||
|
thread.IsBackground = true;
|
||
|
#endif
|
||
|
worker.Thread = thread;
|
||
|
|
||
|
workers[i] = worker;
|
||
|
}
|
||
|
|
||
|
// Console.WriteLine("nib: starting worker threads...");
|
||
|
|
||
|
for (int i = 0; i < worker_count; i++) {
|
||
|
workers[i].Thread.Start();
|
||
|
}
|
||
|
|
||
|
// Console.WriteLine("nib: waiting for all worker threads...");
|
||
|
|
||
|
for (int i = 0; i < worker_count; i++) {
|
||
|
Thread thread = workers[i].Thread;
|
||
|
thread.Join();
|
||
|
}
|
||
|
|
||
|
// Console.WriteLine("nib: all worker threads finished.");
|
||
|
Console.WriteLine();
|
||
|
|
||
|
TimeSpan elapsed = DateTime.Now - beg;
|
||
|
|
||
|
// const string times_format_uk = "nib: {0,-30} : {1,-16} total = {2,-16} user + {3,-16} kernel";
|
||
|
const string times_format = "nib: {0,-32} : {1}";
|
||
|
|
||
|
#if !SINGULARITY
|
||
|
if (_JobHandle != IntPtr.Zero) {
|
||
|
try {
|
||
|
JOBOBJECT_BASIC_ACCOUNTING_INFORMATION job_info;
|
||
|
job_info = Kernel32.QueryJobObjectBasicAccountingInformation(_JobHandle);
|
||
|
|
||
|
TimeSpan total_job_time = job_info.TotalUserTime + job_info.TotalKernelTime;
|
||
|
|
||
|
Console.WriteLine(
|
||
|
times_format,
|
||
|
"CPU time consumed by jobs",
|
||
|
total_job_time);
|
||
|
|
||
|
Console.WriteLine(
|
||
|
times_format,
|
||
|
" in user mode",
|
||
|
job_info.TotalUserTime);
|
||
|
|
||
|
Console.WriteLine(
|
||
|
times_format,
|
||
|
" in kernel mode",
|
||
|
job_info.TotalKernelTime);
|
||
|
|
||
|
Console.WriteLine(
|
||
|
times_format,
|
||
|
"Elapsed time (wall time)",
|
||
|
elapsed);
|
||
|
|
||
|
Console.WriteLine("nib:");
|
||
|
if (elapsed.TotalMilliseconds > 0) {
|
||
|
double elapsed_ms = elapsed.TotalMilliseconds;
|
||
|
double total_job_ms = total_job_time.TotalMilliseconds;
|
||
|
double throughput_ratio = total_job_ms / elapsed_ms;
|
||
|
double throughput_percent = Math.Round(throughput_ratio * 100.0);
|
||
|
Console.WriteLine("nib: Parallel throughput ratio: {0}%", throughput_percent);
|
||
|
}
|
||
|
else {
|
||
|
Console.WriteLine("nib: Too little time elapsed to compute ratio.");
|
||
|
}
|
||
|
}
|
||
|
catch (Exception ex)
|
||
|
{
|
||
|
Console.WriteLine("FAILED to query job info!");
|
||
|
ShowException(ex);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
Console.WriteLine("nib: Child process times not available.");
|
||
|
}
|
||
|
#else
|
||
|
Console.WriteLine("nib: Child process times not available.");
|
||
|
Console.WriteLine(
|
||
|
times_format,
|
||
|
"Elapsed time (wall time)",
|
||
|
elapsed);
|
||
|
#endif
|
||
|
int succeeded = 0;
|
||
|
int failed = 0;
|
||
|
foreach (QueueJob job in _FinishedJobList) {
|
||
|
if (job.Result)
|
||
|
succeeded++;
|
||
|
else
|
||
|
failed++;
|
||
|
}
|
||
|
|
||
|
if (_FinishedJobList.Count != infiles.Count) {
|
||
|
// This is defensive check. Previously worker
|
||
|
// threads could raise and exception and leave nib
|
||
|
// running to completion without placing job on
|
||
|
// _FinishedJobList.
|
||
|
failed += infiles.Count - _FinishedJobList.Count;
|
||
|
}
|
||
|
#if !SINGULARITY
|
||
|
if (_JobHandle != IntPtr.Zero)
|
||
|
Kernel32.CloseHandle(_JobHandle);
|
||
|
#endif
|
||
|
Console.WriteLine("nib: {0} job(s) succeeded, {1} job(s) failed", succeeded, failed);
|
||
|
|
||
|
return (failed != 0) ? 4 : 0;
|
||
|
}
|
||
|
}
|
||
|
}
|