2601 lines
101 KiB
C#
2601 lines
101 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 System.Text.RegularExpressions;
|
|
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";
|
|
public static string LinkExecutablePath = "link.exe";
|
|
#else
|
|
public static string Bartok = "bartok";
|
|
public static string LinkExecutablePath = "link";
|
|
#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;
|
|
public static DateTime Began;
|
|
|
|
|
|
//
|
|
// Fields in the native image building class
|
|
//
|
|
private Options options;
|
|
// private string currentFile; // For warning and error messages.
|
|
private static string architecture;
|
|
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
|
|
if (appManifest == null) {
|
|
throw new Exception("appManifest is Null!");
|
|
}
|
|
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
|
|
if (parent == null) {
|
|
throw new Exception("Null parent");
|
|
}
|
|
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
|
|
if (node == null) {
|
|
throw new Exception("Null node");
|
|
}
|
|
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
|
|
if (node == null) {
|
|
throw new Exception("Null node");
|
|
}
|
|
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
|
|
if (node == null) {
|
|
throw new Exception("Null node");
|
|
}
|
|
string val = node.GetAttribute(attrib,null);
|
|
return val != null ? Int32.Parse(val) : value;
|
|
#endif
|
|
}
|
|
|
|
|
|
#if SINGULARITY
|
|
|
|
public bool RunProgram(string pass, 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();
|
|
double elapsed_ms = (DateTime.Now - Began).TotalMilliseconds;
|
|
WriteLine(" {0,8:f3} [{1,-30}] {2}",
|
|
elapsed_ms / 1000,
|
|
appname,
|
|
pass);
|
|
int exit = -1;
|
|
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) {
|
|
delete childStdInImp;
|
|
delete childStdOutExp;
|
|
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 childStdInImp;
|
|
delete childStdOutExp;
|
|
}
|
|
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();
|
|
exit = p.ExitCode;
|
|
}
|
|
}
|
|
finally {
|
|
NibHelper.ReleaseDirectoryServiceContract(ds);
|
|
}
|
|
#if TODO
|
|
if (log != null) {
|
|
log.WriteLine("# peak memory: paged={0}KB, virtual={1}KB, working={2}KB",
|
|
peakPagedMemorySize / 1024,
|
|
peakWorkingSet / 1024,
|
|
peakVirtualMemorySize / 1024);
|
|
}
|
|
#endif
|
|
if (exit != 0) {
|
|
if (true) {
|
|
WriteLine("nib: error [{0}] {1} failed with exit code: {2}", appname, program, exit);
|
|
}
|
|
return false;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
#else
|
|
public bool RunProgram(string pass, string appname, string program, string arguments, StreamWriter log)
|
|
{
|
|
Process p = new Process();
|
|
p.StartInfo.UseShellExecute = false;
|
|
p.StartInfo.RedirectStandardOutput = (log != null);
|
|
p.StartInfo.RedirectStandardError = (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);
|
|
}
|
|
}
|
|
|
|
#if false
|
|
double elapsed_ms = (DateTime.Now - Began).TotalMilliseconds;
|
|
WriteLine(" {0,8:f3} [{1,-30}] {2}",
|
|
elapsed_ms / 1000, appname, pass);
|
|
#endif
|
|
|
|
if (Verbose) {
|
|
WriteLine(" {0}", arguments);
|
|
}
|
|
if (log != null) {
|
|
Thread stderrCopier = new Thread(new ThreadStart(
|
|
delegate { CopyStreamToLog(p.StandardError, log); }));
|
|
|
|
stderrCopier.Start();
|
|
CopyStreamToLog(p.StandardOutput, log);
|
|
stderrCopier.Join();
|
|
}
|
|
p.WaitForExit();
|
|
exit = p.ExitCode;
|
|
try {
|
|
peakPagedMemorySize
|
|
= Math.Max(p.PeakPagedMemorySize64, peakPagedMemorySize);
|
|
peakVirtualMemorySize
|
|
= Math.Max(p.PeakVirtualMemorySize64, peakVirtualMemorySize);
|
|
peakWorkingSet
|
|
= Math.Max(p.PeakWorkingSet64, peakWorkingSet);
|
|
}
|
|
catch (Exception) {
|
|
}
|
|
}
|
|
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);
|
|
uint facility = ((uint) exit) >> 16;
|
|
uint failureType = ((uint) exit) & 0xFFFF;
|
|
if (facility == 0x8013) {
|
|
WriteLine("This is a CLR-related failure with code 0x{0:X}.", failureType);
|
|
WriteLine("See CorError.h in the .NET or Platform SDK for CLR error codes");
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void CopyStreamToLog(StreamReader reader, StreamWriter log) {
|
|
for (;;) {
|
|
string line = reader.ReadLine();
|
|
if (line == null) {
|
|
break;
|
|
}
|
|
if (ErrorRegex.IsMatch(line)) {
|
|
Console.Error.WriteLine(line);
|
|
}
|
|
lock (log) {
|
|
log.WriteLine(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
static readonly Regex ErrorRegex = new Regex(
|
|
@"^ \s* (internal \s+ compiler \s+ )? error \s* : ",
|
|
RegexOptions.IgnoreCase |
|
|
RegexOptions.IgnorePatternWhitespace);
|
|
#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++) {
|
|
string paramValue = GetAttribute(parameters[p], "value");
|
|
if (paramValue == null) {
|
|
WriteLine("nib: error <parameter> tag missing 'value' attribute.");
|
|
return null;
|
|
}
|
|
String condition =
|
|
GetAttribute(parameters[p], "Condition");
|
|
if (condition != null &&
|
|
EvalCondition(condition) == false) {
|
|
pars[p] = "";
|
|
} else {
|
|
pars[p] = paramValue;
|
|
}
|
|
}
|
|
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++) {
|
|
String paramValue = GetAttribute(parameters[p], "value");
|
|
if (paramValue == null) {
|
|
WriteLine("nib: error <parameter> tag missing 'value' attribute.");
|
|
return null;
|
|
}
|
|
String condition =
|
|
GetAttribute(parameters[p], "Condition");
|
|
if (condition != null &&
|
|
EvalCondition(condition) == false) {
|
|
pars[p] = "";
|
|
} else {
|
|
pars[p] = paramValue;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
private bool EvalCondition(String expression)
|
|
{
|
|
ArrayList tokens = new ArrayList();
|
|
Tokenize(expression, tokens);
|
|
int lastToken;
|
|
bool result = EvalAnd(tokens, 0, out lastToken);
|
|
if (lastToken != tokens.Count) {
|
|
WriteLine("nib: Condition evaluation error");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private bool EvalAnd(ArrayList tokens,
|
|
int startIndex,
|
|
out int endIndex)
|
|
{
|
|
if (startIndex == tokens.Count) {
|
|
WriteLine("nib: evaluation error: incomplete expr");
|
|
endIndex = startIndex;
|
|
return false;
|
|
}
|
|
bool leftResult = EvalOr(tokens, startIndex, out endIndex);
|
|
if (endIndex < tokens.Count) {
|
|
if ("and".Equals(tokens[endIndex])) {
|
|
bool rightResult =
|
|
EvalAnd(tokens, endIndex + 1, out endIndex);
|
|
return leftResult && rightResult;
|
|
} else {
|
|
WriteLine("nib: unexpected token: {0}", tokens[endIndex]);
|
|
return false;
|
|
}
|
|
} else {
|
|
return leftResult;
|
|
}
|
|
}
|
|
|
|
private bool EvalOr(ArrayList tokens,
|
|
int startIndex,
|
|
out int endIndex)
|
|
{
|
|
if (startIndex == tokens.Count) {
|
|
WriteLine("nib: evaluation error: incomplete expr");
|
|
endIndex = startIndex;
|
|
return false;
|
|
}
|
|
bool leftResult = EvalEquals(tokens, startIndex, out endIndex);
|
|
if (endIndex < tokens.Count) {
|
|
if ("and".Equals(tokens[endIndex])) {
|
|
bool rightResult =
|
|
EvalOr(tokens, endIndex + 1, out endIndex);
|
|
return leftResult || rightResult;
|
|
} else {
|
|
WriteLine("nib: unexpected token: {0}", tokens[endIndex]);
|
|
return false;
|
|
}
|
|
} else {
|
|
return leftResult;
|
|
}
|
|
}
|
|
|
|
private bool EvalEquals(ArrayList tokens,
|
|
int startIndex,
|
|
out int endIndex)
|
|
{
|
|
if (startIndex == tokens.Count) {
|
|
WriteLine("nib: evaluation error: incomplete expr");
|
|
endIndex = startIndex;
|
|
return false;
|
|
}
|
|
String leftResult = EvalString(tokens, startIndex, out endIndex);
|
|
if (endIndex < tokens.Count) {
|
|
if ("==".Equals(tokens[endIndex])) {
|
|
String rightResult =
|
|
EvalString(tokens, endIndex + 1, out endIndex);
|
|
return leftResult.Equals(rightResult);
|
|
} else if ("!=".Equals(tokens[endIndex])) {
|
|
String rightResult =
|
|
EvalString(tokens, endIndex + 1, out endIndex);
|
|
return !leftResult.Equals(rightResult);
|
|
} else {
|
|
WriteLine("nib: unexpected token: {0}", tokens[endIndex]);
|
|
return false;
|
|
}
|
|
} else if (leftResult.Equals("true")) {
|
|
return true;
|
|
} else if (leftResult.Equals("false")) {
|
|
return false;
|
|
} else {
|
|
WriteLine("nib: unexpected boolean expression");
|
|
return false;;
|
|
}
|
|
}
|
|
|
|
private String EvalString(ArrayList tokens,
|
|
int startIndex,
|
|
out int endIndex)
|
|
{
|
|
if (startIndex == tokens.Count) {
|
|
WriteLine("nib: evaluation error: incomplete expr");
|
|
endIndex = startIndex;
|
|
return "''";
|
|
}
|
|
String token = (String) tokens[startIndex];
|
|
if (token[0] == '\'') {
|
|
endIndex = startIndex + 1;
|
|
return token;
|
|
} else {
|
|
WriteLine("nib: Expected constant: {0}", tokens[startIndex]);
|
|
endIndex = startIndex;
|
|
return "";
|
|
}
|
|
}
|
|
|
|
private void Tokenize(String expression, ArrayList tokens)
|
|
{
|
|
int index = 0;
|
|
while (index < expression.Length) {
|
|
if (expression[index] == ' ') {
|
|
index++;
|
|
} else if (expression[index] == '\'') {
|
|
int endIndex = expression.IndexOf('\'', index+1);
|
|
if (endIndex < 0) {
|
|
endIndex = expression.Length - 1;
|
|
}
|
|
String quotedString =
|
|
expression.Substring(index, endIndex - index + 1);
|
|
String token = SubstituteEnvVars(quotedString);
|
|
tokens.Add(token);
|
|
index = endIndex + 1;
|
|
} else if (LookingAt(expression, index, "==") ||
|
|
LookingAt(expression, index, "!=")) {
|
|
tokens.Add(expression.Substring(index, 2));
|
|
index += 2;
|
|
} else if (LookingAtWord(expression, index, "and")) {
|
|
tokens.Add(expression.Substring(index, 3));
|
|
index += 3;
|
|
} else if (LookingAtWord(expression, index, "or")) {
|
|
tokens.Add(expression.Substring(index, 2));
|
|
index += 2;
|
|
} else if (expression[index] >= '0' &&
|
|
expression[index] <= '9') {
|
|
int endIndex = index + 1;
|
|
while (endIndex < expression.Length &&
|
|
expression[endIndex] >= '0' &&
|
|
expression[endIndex] <= '9') {
|
|
endIndex++;
|
|
}
|
|
tokens.Add(expression.Substring(index, endIndex - index));
|
|
index = endIndex;
|
|
} else if ((expression[index] >= 'a' &&
|
|
expression[index] <= 'z') ||
|
|
(expression[index] >= 'A' &&
|
|
expression[index] <= 'Z')) {
|
|
int endIndex = index + 1;
|
|
while (endIndex < expression.Length &&
|
|
((expression[endIndex] >= 'a' &&
|
|
expression[endIndex] <= 'a') ||
|
|
(expression[endIndex] >= 'A' &&
|
|
expression[endIndex] <= 'Z') ||
|
|
(expression[endIndex] >= '0' &&
|
|
expression[endIndex] <= '9'))) {
|
|
endIndex++;
|
|
}
|
|
tokens.Add(expression.Substring(index, endIndex - index));
|
|
index = endIndex;
|
|
} else {
|
|
WriteLine("nib: Cannot tokenize conditional expression;\n\t{0}", expression);
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool LookingAt(String bigString,
|
|
int startIndex,
|
|
String matchString)
|
|
{
|
|
int matchLength = matchString.Length;
|
|
for (int i = 0; i < matchLength; i++) {
|
|
if (startIndex + i >= bigString.Length ||
|
|
bigString[startIndex + i] != matchString[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool LookingAtWord(String bigString,
|
|
int startIndex,
|
|
String matchString)
|
|
{
|
|
int matchLength = matchString.Length;
|
|
for (int i = 0; i < matchLength; i++) {
|
|
if (startIndex + i >= bigString.Length ||
|
|
bigString[startIndex + i] != matchString[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
if (bigString.Length == startIndex + matchLength) {
|
|
return true;
|
|
}
|
|
char nextChar = bigString[startIndex + matchLength];
|
|
return !((nextChar >= 'a' && nextChar <= 'z') ||
|
|
(nextChar >= 'A' && nextChar <= 'Z'));
|
|
}
|
|
|
|
private String SubstituteEnvVars(String expression)
|
|
{
|
|
#if SINGULARITY
|
|
return expression;
|
|
#else
|
|
int dollarIndex = expression.IndexOf('$');
|
|
if (dollarIndex >= 0) {
|
|
if (expression.Length == dollarIndex + 1 ||
|
|
expression[dollarIndex+1] != '(') {
|
|
WriteLine("nib: condition with $ not followed by (");
|
|
return expression;
|
|
}
|
|
int endIndex = expression.IndexOf(')', dollarIndex+1);
|
|
int length = endIndex - dollarIndex - 2;
|
|
String variableName =
|
|
expression.Substring(dollarIndex + 2, length);
|
|
String prefix =
|
|
expression.Substring(0, dollarIndex);
|
|
String varString =
|
|
Environment.GetEnvironmentVariable(variableName);
|
|
String suffix =
|
|
SubstituteEnvVars(expression.Substring(endIndex + 1));
|
|
return prefix + varString + suffix;
|
|
} else {
|
|
return expression;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
class Application
|
|
{
|
|
public bool executable;
|
|
public readonly string Name;
|
|
public readonly string Runtime;
|
|
public OAssembly[] assemblies = new OAssembly[0];
|
|
// products:
|
|
public string obj;
|
|
public string info;
|
|
public string super;
|
|
public string mname; // Machine executable name.
|
|
public string mpath; // Machine executable local path.
|
|
public string mpdb; // Machine executable pdb.
|
|
public string mmani; // Output manifest
|
|
public string map;
|
|
public string log;
|
|
|
|
public OCodegen codegen;
|
|
public OLinker linker;
|
|
|
|
public Application(string name, string runtime)
|
|
{
|
|
this.Name = name;
|
|
this.Runtime = runtime;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////// 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;
|
|
}
|
|
string runtime = GetAttribute(application, "runtime");
|
|
if (runtime == null) {
|
|
WriteLine("nib: error <application> tag missing 'runtime' attribute.");
|
|
return null;
|
|
}
|
|
XmlNode process = GetChild(application, "process");
|
|
if (process == null) {
|
|
WriteLine("nib: error <application> tag missing <process> child tag.");
|
|
return null;
|
|
}
|
|
#if !SINGULARITY
|
|
Application app = new Application(name, runtime);
|
|
#else
|
|
string strongName = GetAttribute(process, "strongname");
|
|
if (strongName == null) {
|
|
WriteLine("nib: error <application> tag missing 'strongname' attribute.");
|
|
return null;
|
|
}
|
|
Application app = new Application(strongName, runtime);
|
|
#endif
|
|
|
|
app.executable = GetAttribute(process, "executable") == "true";
|
|
XmlNode assemblyHolder = GetChild(process, "assemblies");
|
|
if (assemblyHolder != 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(Application app)
|
|
{
|
|
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());
|
|
if (app.executable) {
|
|
AddAttribute(process, "path", app.mname);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
private static void AppendArg(StringBuilder sb, string arg)
|
|
{
|
|
bool containsSpaces = false;
|
|
for (int i=0; i < arg.Length; i++)
|
|
{
|
|
if (arg[i] == ' ')
|
|
{
|
|
containsSpaces = true;
|
|
break;
|
|
}
|
|
}
|
|
if (containsSpaces)
|
|
{
|
|
sb.Append(" \"");
|
|
sb.Append(arg.Replace("\"", "\"\""));
|
|
sb.Append("\"");
|
|
}
|
|
else
|
|
{
|
|
sb.Append(" ");
|
|
sb.Append(arg.Replace("\"", "\"\""));
|
|
}
|
|
}
|
|
|
|
// 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.executable) {
|
|
if (String.Compare(app.Name, "kernel", true) != 0) {
|
|
WriteLine(
|
|
"nib: Warning: Not executable process in manifest: {0}.",
|
|
applicationFile);
|
|
}
|
|
doCodegen = false;
|
|
doLinker = false;
|
|
doForceCodegen = false;
|
|
doForceLinker = false;
|
|
}
|
|
|
|
// WriteLine(" {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";
|
|
app.mname = app.Name + "." + architecture;
|
|
app.mpath = nativeDirectory + app.mname;
|
|
#if !SINGULARITY
|
|
app.mmani = nativeDirectory + app.Name + ".manifest";
|
|
#else
|
|
app.mmani = nativeDirectory + app.Name + ".resolved.manifest";
|
|
#endif
|
|
app.mpdb = nativeDirectory + app.Name + "." + architecture + ".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("obj = [{0}]", app.obj);
|
|
WriteLine("info = [{0}]", app.info);
|
|
WriteLine("super= [{0}]", app.super);
|
|
WriteLine("mpdb = [{0}]", app.mpdb);
|
|
WriteLine("mname= [{0}]", app.mname);
|
|
WriteLine("mpath= [{0}]", app.mpath);
|
|
WriteLine("mmani= [{0}]", app.mmani);
|
|
WriteLine("map = [{0}]", app.map);
|
|
WriteLine("log = [{0}]", app.log);
|
|
}
|
|
|
|
if (DoClean) {
|
|
DeleteIfExists(app.obj);
|
|
DeleteIfExists(app.info);
|
|
DeleteIfExists(app.super);
|
|
DeleteIfExists(app.mpdb);
|
|
DeleteIfExists(app.mpath);
|
|
DeleteIfExists(app.mmani);
|
|
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 = null;
|
|
if (cacheDirectory == null) {
|
|
finalAssemblyPath = assemblyCachePath;
|
|
}
|
|
else {
|
|
// The paths in the manifest can contain leading slashes.
|
|
// Don't let that confuse Path.Combine -- strip them first.
|
|
int skip = 0;
|
|
while (skip < assemblyCachePath.Length && (
|
|
assemblyCachePath[skip] == '/' || assemblyCachePath[skip] == '\\')) {
|
|
skip++;
|
|
}
|
|
if (skip > 0) {
|
|
assemblyCachePath = assemblyCachePath.Substring(skip);
|
|
}
|
|
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 find 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>
|
|
|
|
log = new StreamWriter(app.log);
|
|
|
|
StringBuilder sb = new StringBuilder(4096);
|
|
ArrayList bartokOptions = new ArrayList();
|
|
if (architecture == "x64") {
|
|
sb.Append(" /x64 ");
|
|
}
|
|
if (options != null) {
|
|
foreach (string arg in options.codegen.parameters) {
|
|
bartokOptions.Add(arg);
|
|
}
|
|
}
|
|
if (app.codegen != null) {
|
|
foreach (string arg in app.codegen.parameters) {
|
|
bartokOptions.Add(arg);
|
|
}
|
|
}
|
|
bartokOptions.Add("/out:" + app.obj);
|
|
bartokOptions.Add("/outdir:" + tempDirectory.Trim('\\'));
|
|
|
|
foreach (string bartokOption in bartokOptions) {
|
|
AppendArg(sb, bartokOption);
|
|
}
|
|
|
|
for (int a = 0; a < assemblies.Length; a++) {
|
|
AppendArg(sb, assemblies[a].Final);
|
|
}
|
|
|
|
log.WriteLine("# Name = [{0}]", app.Name);
|
|
log.WriteLine("# obj = [{0}]", app.obj);
|
|
log.WriteLine("# info = [{0}]", app.info);
|
|
log.WriteLine("# super= [{0}]", app.super);
|
|
log.WriteLine("# mpdb = [{0}]", app.mpdb);
|
|
log.WriteLine("# mname= [{0}]", app.mname);
|
|
log.WriteLine("# mpath= [{0}]", app.mpath);
|
|
log.WriteLine("# map = [{0}]", app.map);
|
|
log.WriteLine("# log = [{0}]", app.log);
|
|
log.WriteLine("# target = {0}", target);
|
|
log.WriteLine("# newest = {0}", newest);
|
|
log.WriteLine("# bartok = {0}", Bartok);
|
|
|
|
//log.WriteLine("#{0} {1}", Bartok, sb.ToString());
|
|
|
|
log.WriteLine("# Bartok Options:");
|
|
foreach (string option in bartokOptions) {
|
|
log.WriteLine("# " + option);
|
|
}
|
|
|
|
log.WriteLine("# Assemblies:");
|
|
foreach (OAssembly asm in assemblies) {
|
|
log.WriteLine("# " + asm.Name);
|
|
}
|
|
|
|
if (Verbose) {
|
|
WriteLine(sb.ToString());
|
|
}
|
|
|
|
Console.WriteLine("NIB: Generating Native Image - {0}", Path.GetFileNameWithoutExtension(app.Name));
|
|
#if !SINGULARITY
|
|
string bartokCommandLine = sb.ToString();
|
|
|
|
string emitBartokCommandFileText = Environment.GetEnvironmentVariable("EmitBartokCommandFile");
|
|
bool emitBartokCommandFile = String.Equals(emitBartokCommandFileText, "true", StringComparison.OrdinalIgnoreCase);
|
|
if (emitBartokCommandFile) {
|
|
string bartokCommandFilePath = Path.Combine(tempDirectory, app.Name + ".bartok.cmd");
|
|
try {
|
|
string bartokCommandFileContents = Bartok + " " + bartokCommandLine + "\r\n";
|
|
File.WriteAllText(bartokCommandFilePath, bartokCommandFileContents, Encoding.ASCII);
|
|
Console.Error.WriteLine("info : Saved Bartok invocation to file: {0}", bartokCommandFilePath);
|
|
}
|
|
catch (Exception ex) {
|
|
Console.Error.WriteLine("warning : Failed to write file '{0}'.", bartokCommandFilePath);
|
|
Console.Error.WriteLine("warning : {0}: {1}", ex.GetType().Name, ex.Message);
|
|
}
|
|
}
|
|
|
|
|
|
if (!RunProgram("bartok", app.Name, Bartok, bartokCommandLine, log)) {
|
|
Console.WriteLine("{0}(0): log file", app.log);
|
|
return false;
|
|
}
|
|
#else
|
|
if (!RunProgram("bartok", app.Name, Bartok, sb.ToString(), log, outputMux)) {
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
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 name = libraries[l].Name;
|
|
string cache = libraries[l].Cache;
|
|
|
|
name = name.Replace("$(RUNTIME)", app.Runtime);
|
|
if (cache != null) {
|
|
cache = cache.Replace("$(RUNTIME)", app.Runtime);
|
|
}
|
|
|
|
string test = libCacheDirectory + name;
|
|
//WriteLine(" looking for final code @ {0}", test);
|
|
if (File.Exists(test)) {
|
|
libraries[l].Final = test;
|
|
if (cache == null) {
|
|
libraries[l].Cache = libraries[l].Name;
|
|
cache = name;
|
|
}
|
|
}
|
|
else {
|
|
if (cache == null) {
|
|
WriteLine("nib: error Couldn't find library: {0}",
|
|
libraries[l]);
|
|
return false;
|
|
}
|
|
|
|
libraries[l].Final = Path.Combine(cacheDirectory,
|
|
cache.Replace('/', '\\'));
|
|
}
|
|
|
|
if (Verbose) {
|
|
WriteLine(" {0}", 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.mpath)) {
|
|
target = File.GetLastWriteTime(app.mpath);
|
|
}
|
|
|
|
|
|
if (newest >= target || doForceLinker) {
|
|
// Linking is necessary
|
|
|
|
// <option>
|
|
// /out:..\obj\Prototype.MarkSweep\Shell.$(machine)
|
|
// /pdb:..\obj\Prototype.MarkSweep\Shell.$(machine).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) {
|
|
AppendArg(sb, arg);
|
|
}
|
|
}
|
|
if (app.linker != null) {
|
|
foreach (string arg in app.linker.parameters) {
|
|
AppendArg(sb, arg);
|
|
}
|
|
}
|
|
sb.Append(" /machine:");
|
|
if (architecture =="arm") {
|
|
sb.Append("arm");
|
|
}
|
|
else {
|
|
sb.Append(architecture);
|
|
}
|
|
AppendArg(sb, "/out:" + app.mpath);
|
|
AppendArg(sb, "/pdb:" + app.mpdb);
|
|
AppendArg(sb, "/map:" + app.map);
|
|
AppendArg(sb, app.obj);
|
|
AppendArg(sb, app.super);
|
|
for (int l = 0; l < libraries.Length; l++) {
|
|
AppendArg(sb, libraries[l].Final);
|
|
}
|
|
String args = sb.ToString();
|
|
|
|
#if !SINGULARITY
|
|
if (log != null) {
|
|
log.WriteLine("#{0} {1}", LinkExecutablePath, args);
|
|
}
|
|
Console.WriteLine("NIB: Linking - {0}", Path.GetFileNameWithoutExtension(app.Name));
|
|
if (!RunProgram("link", app.Name, LinkExecutablePath, args, null)) {
|
|
return false;
|
|
}
|
|
#else
|
|
if (log != null) {
|
|
log.WriteLine("#{0} {1}", "link", args);
|
|
}
|
|
if (!RunProgram("link", app.Name, "link", args, null, outputMux)) {
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if CALL_MKNAME
|
|
args = "/u " + app.mpath;
|
|
if (log != null) {
|
|
log.WriteLine("#{0} {1}", "mkname.exe", args);
|
|
}
|
|
#if !SINGULARITY
|
|
if (!RunProgram("mkname", app.Name, "mkname.exe", args, null)) {
|
|
return false;
|
|
}
|
|
#else
|
|
if (!RunProgram("mkname", app.Name, "mkname.exe", args, null, PipeMultiplexer! outputMux)) {
|
|
return false;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
else {
|
|
if (Verbose) {
|
|
WriteLine("nib: Linker unneeded: {0}", app.mpath);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (doManifest) {
|
|
// Find the newest library.
|
|
newest = File.GetLastWriteTime(applicationFile);
|
|
if (newest < update) {
|
|
newest = update;
|
|
}
|
|
DateTime target = DateTime.MinValue;
|
|
if (File.Exists(app.mmani)) {
|
|
target = File.GetLastWriteTime(app.mmani);
|
|
}
|
|
|
|
if (newest >= target || doForceManifest) {
|
|
//////////////////////////////////////////////////////////
|
|
// Update the installed manifest and write it out.
|
|
//
|
|
if (!InstallApplicationManifest(app)) {
|
|
return false;
|
|
}
|
|
#if SINGULARITY
|
|
Console.WriteLine(" manifest ->{0}", app.mmani);
|
|
FileStream fs = new FileStream(app.mmani, 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.mmani,
|
|
System.Text.Encoding.UTF8);
|
|
writer.Formatting = Formatting.Indented;
|
|
appManifest.Save(writer);
|
|
writer.Close();
|
|
#endif
|
|
}
|
|
else {
|
|
if (Verbose) {
|
|
WriteLine("nib: Manifest generation unneeded: {0}", app.mmani);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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
|
|
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 {
|
|
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 bool IsQuote(char c)
|
|
{
|
|
return (c == '"' || c == '\'');
|
|
}
|
|
|
|
static string RemoveQuotes(string text)
|
|
{
|
|
int start = 0;
|
|
int length = text.Length;
|
|
|
|
if (length > 0 && IsQuote(text[start + length - 1])) {
|
|
length--;
|
|
}
|
|
if (length > 0 && IsQuote(text[start])) {
|
|
start++;
|
|
length--;
|
|
}
|
|
|
|
if (start != 0 || length != text.Length) {
|
|
return text.Substring(start, length);
|
|
}
|
|
else {
|
|
return text;
|
|
}
|
|
}
|
|
|
|
static bool StringIsNullOrEmpty(string str)
|
|
{
|
|
return (str == null || str.Length == 0);
|
|
}
|
|
|
|
static int Main(string[] args)
|
|
{
|
|
ArrayList infiles = new ArrayList();
|
|
string optionsFile = null;
|
|
string cacheDirectory = null;
|
|
string libCacheDirectory = null;
|
|
string tempDirectory = null;
|
|
string nativeDirectory = null;
|
|
|
|
Began = DateTime.Now;
|
|
|
|
_BuildingParallel = false;
|
|
|
|
#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];
|
|
arg = RemoveQuotes(arg);
|
|
|
|
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();
|
|
}
|
|
if (value != null) {
|
|
value = value.Trim();
|
|
value = RemoveQuotes(value);
|
|
value = value.Trim();
|
|
}
|
|
|
|
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 (!StringIsNullOrEmpty(value)) {
|
|
Bartok = value;
|
|
if (!File.Exists(Bartok)) {
|
|
Console.WriteLine("Bartok not found: {0}", Bartok);
|
|
badArg = true;
|
|
}
|
|
}
|
|
else {
|
|
Console.WriteLine("The '/bartok' argument requires a value, but none was provided.");
|
|
badArg = true;
|
|
}
|
|
break;
|
|
|
|
case "linker":
|
|
if (!StringIsNullOrEmpty(value)) {
|
|
LinkExecutablePath = value;
|
|
if (!File.Exists(LinkExecutablePath)) {
|
|
Console.WriteLine("Linker not found: " + LinkExecutablePath);
|
|
badArg = true;
|
|
}
|
|
}
|
|
else {
|
|
Console.WriteLine("The '/link' argument requires a value, but none was provided.");
|
|
badArg = true;
|
|
break;
|
|
}
|
|
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 + "/";
|
|
//Console.WriteLine("Setting libCache to ({0})",libCacheDirectory);
|
|
#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 (!StringIsNullOrEmpty(value)) {
|
|
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 == 1) {
|
|
numWorkerThreads = 1;
|
|
}
|
|
else {
|
|
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;
|
|
numWorkerThreads = count;
|
|
}
|
|
else {
|
|
numWorkerThreads = count;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Parallel builds disabled.
|
|
numWorkerThreads = 1;
|
|
}
|
|
break;
|
|
|
|
case "machine":
|
|
case "arch":
|
|
case "architecture":
|
|
if (value != null) {
|
|
architecture = 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);
|
|
}
|
|
}
|
|
|
|
_BuildingParallel = numWorkerThreads > 1;
|
|
|
|
if (architecture == null) {
|
|
Console.WriteLine("No processor architecture specified. Terminating.");
|
|
Usage();
|
|
return -1;
|
|
}
|
|
|
|
if (infiles.Count == 0 ||
|
|
#if !SINGULARITY
|
|
cacheDirectory == null ||
|
|
#endif
|
|
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(nativeDirectory)) {
|
|
Console.WriteLine("nib: error Native directory '{0}' not found.",
|
|
nativeDirectory);
|
|
return 2;
|
|
}
|
|
#if !SINGULARITY
|
|
if (!Directory.Exists(cacheDirectory)) {
|
|
Console.WriteLine("nib: error Cache directory '{0}' not found.",
|
|
cacheDirectory);
|
|
return 2;
|
|
}
|
|
#endif
|
|
if (!Directory.Exists(tempDirectory)) {
|
|
Console.WriteLine("nib: error Temp directory '{0}' not found.",
|
|
tempDirectory);
|
|
return 2;
|
|
}
|
|
|
|
if (!Directory.Exists(libCacheDirectory)) {
|
|
Console.WriteLine("nib: error libCache directory '{0}' not found.",
|
|
libCacheDirectory);
|
|
return 2;
|
|
}
|
|
|
|
foreach (string filename in infiles) {
|
|
QueueJob job = new QueueJob();
|
|
job.Filename = filename;
|
|
_WaitingJobQueue.Enqueue(job);
|
|
}
|
|
|
|
#if !SINGULARITY
|
|
if (_BuildingParallel) {
|
|
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);
|
|
}
|
|
else {
|
|
// Console.WriteLine("nib: Using 1 processor.");
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
TimeSpan elapsed = DateTime.Now - Began;
|
|
|
|
// 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 (_BuildingParallel) {
|
|
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
|
|
if (infiles.Count > 1) {
|
|
Console.WriteLine("nib: {0} job(s) succeeded, {1} job(s) failed", succeeded, failed);
|
|
}
|
|
|
|
return (failed != 0) ? 4 : 0;
|
|
}
|
|
}
|
|
}
|