singrdk/base/Windows/BuildToolsLibrary/ManifestBuilder.cs

942 lines
39 KiB
C#

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: MetaDataParser.cs
//
// TODO: fix comments in this file
//
// Note: This is a best-approximation of statically determinable metadata.
// We can create a few general properties of an application, and we
// can read the ConsoleCategory, Category, and DriverCategory attributes
//
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using Bartok.MSIL;
public class ManifestBuilder
{
//
// Note - This is a rather crude recursive descent parser
//
// This parser only deals in relatively flat xml, and as such we can use
// a very straightforward parse methodology:
// for a class decorated with a tag that is in the "triggers" list
// - start a new xml tag
// - check every decoration on that class according to the classAttributes
// rules, and for each match add a child
// - check every field in the class according to the fieldAttributes
// rules, and for each match add a child
private class ParseRules
{
public TriggerDefinition[] triggers;
public ClassDefinition[] classAttributes;
public FieldDefinition[] fieldAttributes;
public ParseRules(ParseRules inheritFrom,
TriggerDefinition[] triggers,
ClassDefinition[] classAttributes,
FieldDefinition[] fieldAttributes)
{
this.triggers = triggers;
if (inheritFrom != null) {
this.classAttributes = Inherit(inheritFrom.classAttributes,
classAttributes);
this.fieldAttributes = Inherit(inheritFrom.fieldAttributes,
fieldAttributes);
}
else {
this.classAttributes = classAttributes;
this.fieldAttributes = fieldAttributes;
}
}
private static ClassDefinition[] Inherit(ClassDefinition[] root,
ClassDefinition[] grow)
{
ClassDefinition[] target = new ClassDefinition[root.Length + grow.Length];
int j = 0;
for (int i = 0; i < root.Length; i++, j++) {
target[j] = root[i];
}
for (int i = 0; i < grow.Length; i++, j++) {
target[j] = grow[i];
}
return target;
}
private static FieldDefinition[] Inherit(FieldDefinition[] root,
FieldDefinition[] grow)
{
FieldDefinition[] target = new FieldDefinition[root.Length + grow.Length];
int j = 0;
for (int i = 0; i < root.Length; i++, j++) {
target[j] = root[i];
}
for (int i = 0; i < grow.Length; i++, j++) {
target[j] = grow[i];
}
return target;
}
}
// Construct the parsing tables.
private static readonly ParseRules[] rules;
static ManifestBuilder()
{
ParseRules categoryRules = new ParseRules(
null,
// "triggers"
new TriggerDefinition[] {
new TriggerDefinition("category",
null,
"Microsoft.Singularity.Configuration.CategoryAttribute",
"Microsoft.Singularity.Configuration.CategoryDeclaration",
new string [] {"name"}),
},
// "class" attributes
new ClassDefinition[] {},
// "field" attributes
new FieldDefinition[] {
// NB: the constructor fields on these are handled specially, unlike
// everything else in this tool. This is indicated by the "true"
// value for the last parameter to the constructor
// TODO: is this correct? Should Endpoint declarations in
// ConsoleCategories and Configuration be given names so that they are more
// human-configurable? Will an app ever declare multiple Endpoints of
// the same type and require the kernel to user to patch those identical
// endpoints to different (and conflicting) apps? Could this problem be
// addressed via stronger typing?
new EndpointDefinition("endpoints",
"extension",
"Microsoft.Singularity.Configuration.ExtensionEndpointAttribute",
"Microsoft.Singularity.Channels.TRef_2"
+ "<Microsoft.Singularity.Extending."
+ "ExtensionContract+Exp,Microsoft.Singularity"
+ ".Extending.ExtensionContract+Start>"),
new ServiceEndpointDefinition("endpoints",
"serviceProvider",
"Microsoft.Singularity.Configuration.ServiceEndpointAttribute",
"Microsoft.Singularity.Channels.TRef_2"
+ "<Microsoft.Singularity.Directory."
+ "ServiceProviderContract+Exp,Microsoft."
+ "Singularity.Directory.ServiceProviderContract"
+ "+Start>"),
// TODO: - the inheritance on this is not correct
new EndpointDefinition("endpoints",
"endpoint",
"Microsoft.Singularity.Configuration.EndpointAttribute",
null),
new EndpointDefinition("endpoints",
"customEndpoint",
"Microsoft.Singularity.Configuration.CustomEndpointAttribute",
null),
new EndpointDefinition("endpoints",
"inputPipe",
"Microsoft.Singularity.Configuration.InputEndpointAttribute",
null,
new string [] {"Kind"}),
new EndpointDefinition("endpoints",
"outputPipe",
"Microsoft.Singularity.Configuration.OutputEndpointAttribute",
null,
new string [] {"Kind"}),
new ParameterDefinition("StringParameters",
"StringParameter",
"Microsoft.Singularity.Configuration.StringParameterAttribute",
"STRING",
new string [] {"Name"}),
new ParameterDefinition("LongParameters",
"LongParameter",
"Microsoft.Singularity.Configuration.LongParameterAttribute",
"I8",
new string [] {"Name"}),
new ParameterDefinition("BoolParameters",
"BoolParameter",
"Microsoft.Singularity.Configuration.BoolParameterAttribute",
"BOOLEAN",
new string [] {"Name"}),
new ParameterDefinition("StringArrayParameters",
"StringArrayParameter",
"Microsoft.Singularity.Configuration.StringArrayParameterAttribute",
"SZARRAY",
new string [] {"Name"}),
}
);
ParseRules driverCategoryRules = new ParseRules(
categoryRules, // inherit "class" and "field" rules from category.
// "triggers"
new TriggerDefinition[] {
new TriggerDefinition("category",
"driver",
"Microsoft.Singularity.Io.DriverCategoryAttribute",
"Microsoft.Singularity.Io.DriverCategoryDeclaration"),
},
// "class" attributes
new ClassDefinition[] {
new ClassDefinition("device",
"Microsoft.Singularity.Io.SignatureAttribute",
new string [] {"signature"}),
new ClassDefinition("enumerates",
"Microsoft.Singularity.Io.EnumeratesDeviceAttribute",
new string [] {"signature"}),
},
// "field" attributes
new FieldDefinition[] {
new FieldDefinition("dynamicHardware",
"ioPortRange",
"Microsoft.Singularity.Io.IoPortRangeAttribute",
"Microsoft.Singularity.Io.IoPortRange",
new string [] {"id"},
new string [] {"Default","baseAddress",
"Length","rangeLength",
"Shared", "shared"}),
new FieldDefinition("fixedHardware",
"ioPortRange",
"Microsoft.Singularity.Io.IoFixedPortRangeAttribute",
"Microsoft.Singularity.Io.IoPortRange",
new string [] {"id"},
new string [] {"Base","baseAddress",
"Length","rangeLength",
"Shared", "shared"},
new string [] {"fixed", "true"}),
new FieldDefinition("dynamicHardware",
"ioIrqRange",
"Microsoft.Singularity.Io.IoIrqRangeAttribute",
"Microsoft.Singularity.Io.IoIrqRange",
new string [] {"id"},
new string [] {"Default","baseAddress",
"Length","rangeLength",
"Shared", "shared"},
new string [] {"rangeLength", "1"}),
new FieldDefinition("fixedHardware",
"ioIrqRange",
"Microsoft.Singularity.Io.IoFixedIrqRangeAttribute",
"Microsoft.Singularity.Io.IoIrqRange",
new string [] {"id"},
new string [] {"Base","baseAddress",
"Length","rangeLength",
"Shared", "shared"},
new string [] {"fixed", "true",
"rangeLength", "1"}),
new FieldDefinition("dynamicHardware",
"ioDmaRange",
"Microsoft.Singularity.Io.IoDmaRangeAttribute",
"Microsoft.Singularity.Io.IoDmaRange",
new string [] {"id"},
new string [] {"Default","baseAddress",
"Length","rangeLength",
"Shared", "shared"},
new string [] {"rangeLength", "1"}),
new FieldDefinition("dynamicHardware",
"ioMemoryRange",
"Microsoft.Singularity.Io.IoMemoryRangeAttribute",
"Microsoft.Singularity.Io.IoMemoryRange",
new string [] {"id"},
new string [] {"Default","baseAddress",
"Length","rangeLength",
"Shared", "shared"}),
new FieldDefinition("fixedHardware",
"ioMemoryRange",
"Microsoft.Singularity.Io.IoFixedMemoryRangeAttribute",
"Microsoft.Singularity.Io.IoMemoryRange",
new string [] {"id"},
new string [] {"Base","baseAddress",
"Length","rangeLength",
"Shared", "shared",
"AddressBits", "addressBits",
"Alignment", "alignment"},
new string [] {"fixed", "true"}),
}
);
ParseRules consoleCategoryRules = new ParseRules(
categoryRules, // inherit "class" and "field" rules from category.
// "triggers"
new TriggerDefinition[] {
new TriggerDefinition("category",
"console",
"Microsoft.Singularity.Configuration.ConsoleCategoryAttribute",
"Microsoft.Singularity.Configuration.ConsoleCategoryDeclaration"),
},
// "class" attributes
new ClassDefinition[] {
},
// "field" attributes
new FieldDefinition[] {
}
);
rules = new ParseRules[3] {
driverCategoryRules,
categoryRules,
consoleCategoryRules,
};
}
//////////////////////////////////////////////////////////////////////////
//
// the base path of the file cache
private String cache;
// the list of assembly files
private ArrayList assemblies;
// The metadata resolver
private MetaDataResolver resolver;
// the output file
private XmlDocument manifest;
// the process node
private XmlNode process;
// the Bartok/code generation parameters.
private XmlNode codegen;
// the linker parameters.
private XmlNode linker;
private int warningCount;
private int errorCount;
public ManifestBuilder(String cache, ArrayList assemblies)
{
this.cache = cache.ToLower();
this.assemblies = assemblies;
// create a blank manifest without an <?xml> header:
manifest = new XmlDocument();
}
// create a manifest for an app, without looking at any assemblies
// this creates the stub manifest into which each assembly's info will
// go
public bool CreateNewManifest(string appname, string x86filename)
{
warningCount = 0;
errorCount = 0;
// Create this.resolver using this.assemblyfilename
InitializeResolver();
// Create the basic XML tree.
XmlNode application = AddElement(manifest, "application");
AddAttribute(application, "name", appname);
process = AddElement(application, "process");
AddAttribute(process, "id", "0");
AddAttribute(process, "main", "true");
AddAttribute(process, "path", StripPathFromPath(x86filename));
AddAttribute(process, "cache", StripCacheFromPath(x86filename));
bool seenPublisher = false;
XmlNode privileges = null;
// here we assume that the first assembly is the app assembly
// extract the publisher name from there
MetaData md0 = (MetaData)resolver.MetaDataList[0];
MetaDataAssembly mda0 = (MetaDataAssembly)md0.Assemblies[0];
if (mda0.CustomAttributes != null) {
foreach (MetaDataCustomAttribute ca in mda0.CustomAttributes) {
if (ca.Name == "Microsoft.Singularity.Security.ApplicationPublisherAttribute") {
if (!seenPublisher && ca.FixedArgs != null && ca.FixedArgs[0] != null) {
AddAttribute(application, "publisher", (string)ca.FixedArgs[0]);
seenPublisher = true;
}
}
else if (ca.Name == "Microsoft.Singularity.Security.AssertPrivilegeAttribute") {
if (ca.FixedArgs != null && ca.FixedArgs[0] != null) {
if (privileges == null)
privileges = AddElement(application, "privileges");
XmlNode privilege = AddElement(privileges, "privilege");
AddAttribute(privilege, "name", (string)ca.FixedArgs[0]);
}
}
}
}
// Create the list of assemblies.
XmlNode assemblies = AddElement(process, "assemblies");
foreach (MetaData md in resolver.MetaDataList) {
MetaDataAssembly mda = (MetaDataAssembly)md.Assemblies[0];
string file = md.Name;
string assemblyname = StripPathFromPath(file);
XmlNode assembly = AddElement(assemblies, "assembly");
AddAttribute(assembly, "name", assemblyname);
if (mda.MajorVersion != 0 || mda.MinorVersion != 0 ||
mda.BuildNumber != 0 || mda.RevisionNumber != 0) {
AddAttribute(assembly, "version", String.Format("{0}.{1}.{2}.{3}",
mda.MajorVersion,
mda.MinorVersion,
mda.BuildNumber,
mda.RevisionNumber));
}
if (mda.PublicKey != null & mda.PublicKey.Length > 0) {
AddAttribute(assembly, "publickey",
String.Format("{0}", KeyToString(mda.PublicKey)));
}
if (mda.Locale != null && mda.Locale != "") {
AddAttribute(assembly, "locale", mda.Locale);
}
AddAttribute(assembly, "cache", StripCacheFromPath(file));
}
// Create a placeholder for
// now go through every class in the assembly to locate ConsoleCategory,
// Category, and DriverCategory attributes
int categoryIndex = 0;
XmlNode categories = null;
foreach (MetaData md in resolver.MetaDataList) {
// NB: md.TypeDefs is a flat list of all classes in this assembly,
// regardless of nesting
foreach (MetaDataTypeDefinition type in md.TypeDefs) {
for (int i = 0; i < rules.Length; i++) {
if (ProcessType(type, rules[i], process, ref categoryIndex, ref categories)) {
break;
}
}
}
}
if (errorCount != 0) {
return false;
}
return true;
}
public bool AddCodegenParameter(string param)
{
if (codegen == null) {
codegen = AddElement(process, "codegen");
}
XmlNode node = AddElement(codegen, "parameter");
AddAttribute(node, "value", param);
return true;
}
public bool AddLinkerParameter(string param)
{
if (linker == null) {
linker = AddElement(process, "linker");
}
XmlNode node = AddElement(linker, "parameter");
AddAttribute(node, "value", param);
return true;
}
public void Save(XmlTextWriter writer)
{
manifest.Save(writer);
}
///////////////////////////////////////////// Methods to help with errors.
//
private static string LastName(string value)
{
if (value != null) {
int period = value.LastIndexOf('.');
if (period > 0 && period < value.Length) {
return value.Substring(period + 1);
}
}
return value;
}
public void Error(string message, params object[] args)
{
Console.Write("mkmani: Error: ");
Console.WriteLine(message, args);
errorCount++;
}
private void Warning(string message, params object[] args)
{
Console.Write("mkmani: Warning: ");
Console.WriteLine(message, args);
warningCount++;
}
//////////////////////////////////////////////// Methods to help with XML.
//
private XmlNode AddElement(XmlNode parent, string name)
{
XmlNode element = manifest.CreateNode(XmlNodeType.Element, name, "");
if (parent != null) {
parent.AppendChild(element);
}
return element;
}
public void AddAttribute(XmlNode node, string name, string value)
{
XmlAttribute attr = manifest.CreateAttribute(name);
attr.Value = value;
node.Attributes.Append(attr);
}
// this method creates a MetaData resolver and loads all the assemblies into
// it
private void InitializeResolver()
{
resolver = new MetaDataResolver(assemblies, new ArrayList(), new DateTime(),
false, false);
MetaDataResolver.ResolveCustomAttributes(
new MetaDataResolver[] {resolver});
}
// parse utility function to ensure that a type's inheritance matches the
// inheritance specified in the TokenDefinition
public bool MatchInheritance(string derivesFromClass,
MetaDataTypeDefinition type)
{
//Console.WriteLine("derives=({0}), type = {1}",
// derivesFromClass,
// type.Extends == null ? null: ((MetaDataTypeReference)type.Extends).FullName);
//
// if (derivesFromClass != null) {
// if (type.Extends == null ||
// ((MetaDataTypeReference)type.Extends).FullName !=
// derivesFromClass) {
// return false;
// }
// }
//
return true;
}
// parse utility function to ensure that the class of a field matches the
// class name specified in a TokenDefinition
public bool MatchFieldType(FieldDefinition rule,
MetaDataField field)
{
if (rule.matchClassType != null) {
MetaDataObject classobj = field.Signature.FieldType.ClassObject;
string classtype = "";
// Since we can pass in different objects as field, we need to be
// careful here about how we get the class type
if (classobj is MetaDataTypeDefinition) {
classtype = ((MetaDataTypeDefinition) classobj).FullName;
}
else if (classobj is MetaDataTypeReference) {
classtype = ((MetaDataTypeReference) classobj).FullName;
}
else if (classobj == null) {
classtype = field.Signature.FieldType.ElementType.ToString();
}
if (classtype != rule.matchClassType) {
Error("{0} can't be applied to {1}",
LastName(rule.attribute), field.FullName);
Error("{0}does not match {1}",
classtype, rule.matchClassType);
return false;
}
}
return true;
}
// This is the only way we create XML tags (except for Endpoints)
public XmlNode CreateNode(MetaDataCustomAttribute data,
TokenDefinition rule)
{
XmlNode node = AddElement(null, rule.xmlTagName);
// make an attribute for each constructor argument
if (data.FixedArgs.Length != 0) {
for (int i = 0; i < data.FixedArgs.Length; i++) {
string name = rule.constructorFields[i];
if (data.FixedArgs[i] == null) {
AddAttribute(node, name, "");
}
else {
string value = data.FixedArgs[i].ToString();
AddAttribute(node, name, value);
}
}
}
// make an attribute for each constructor-time property
for (int i = 0; i < data.NamedArgs.Length; i++) {
string name = rule.FindPropertyReplacement(data.NamedArgs[i].Name);
object arg = data.NamedArgs[i].Value;
string value;
if (arg == null) {
// REVIEW: do we want "" or null here?
value = null;
}
else {
value = data.NamedArgs[i].Value.ToString();
if (arg is System.UInt32) {
// We output unsigned types as 0x because they are generally
// hardware-related numbers which are documented in hexadecimal.
value = String.Format("0x{0:x}", arg);
}
}
AddAttribute(node, name, value);
}
// make an attribute for each default field
for (int i = 0; i < rule.defaultFields.Length; i += 2) {
string name = rule.defaultFields[i];
AddAttribute(node, name, rule.defaultFields[i+1]);
}
return node;
}
private XmlNode GetEndpointHierarchy(string nodeName, string endpointType)
{
XmlNode node = AddElement(null, nodeName);
MetaDataTypeDefinition epType = resolver.ResolveName(endpointType);
XmlNode oldChild = null;
XmlNode newChild = null;
while (epType.FullName != "Microsoft.Singularity.Channels.Endpoint") {
newChild = manifest.CreateNode(XmlNodeType.Element, "inherit", "");
AddAttribute(newChild, "name", epType.FullName);
if (oldChild == null) {
node.AppendChild(newChild);
}
else {
node.InsertBefore(newChild, oldChild);
}
oldChild = newChild;
string nextName;
if (epType.Extends is MetaDataTypeReference) {
nextName = ((MetaDataTypeReference) epType.Extends).FullName;
if (nextName == "Exp" || nextName == "Imp") {
MetaDataTypeReference mdtr =
(MetaDataTypeReference) epType.Extends;
nextName =
((MetaDataTypeReference)mdtr.ResolutionScope).FullName
+ "." + nextName;
}
}
else if (epType.Extends is MetaDataTypeDefinition) {
nextName = ((MetaDataTypeDefinition) epType.Extends).FullName;
}
else {
return node;
}
epType = resolver.ResolveName(nextName);
}
newChild = manifest.CreateNode(XmlNodeType.Element, "inherit", "");
AddAttribute(newChild, "name", epType.FullName);
if (oldChild == null) {
node.AppendChild(newChild);
}
else {
node.InsertBefore(newChild, oldChild);
oldChild = newChild;
}
return node;
}
public XmlNode CreateNodeIndexed(MetaDataCustomAttribute data,
TokenDefinition rule,
int index
)
{
XmlNode node = CreateNode(data, rule);
AddAttribute(node, "id", index.ToString());
return node;
}
// Endpoints are special in a lot of ways, and require a special method
public XmlNode CreateEndpointNode(MetaDataCustomAttribute data,
EndpointDefinition rule,
int index)
{
// assume that the constructor to an endpoint always takes one argument,
// and that the argument looks like this:
// "<discard*> contractname+Exp*,AssemblyName, Version=foo,
// Culture=bar, PublicKeyToken=fbar"
// we'll parse this to get all the attributes of the top-level tag, and
// then parse field that is being decorated to get the rest of the
// information we need.
// get the type of the field that is decorated:
MetaDataObject t = (MetaDataObject)
((MetaDataField)data.Parent).Signature.FieldType.ClassObject;
// split the field to get the parts we need
string typeName = t.FullName;
typeName = typeName.Replace("<", ",");
typeName = typeName.Replace(">", "");
typeName = typeName.Replace("+", ",");
string [] nameParts = typeName.Split(',');
string contractName = nameParts[1];
string impName = contractName + ".Imp";
string expName = contractName + ".Exp";
string stateName = contractName + "." + nameParts[4];
XmlNode impNode = GetEndpointHierarchy("imp", impName);
XmlNode expNode = GetEndpointHierarchy("exp", expName);
MetaDataTypeDefinition r1 = resolver.ResolveName(impName);
MetaDataTypeDefinition r2 = resolver.ResolveName(expName);
MetaDataTypeDefinition r3 = resolver.ResolveName(stateName);
string startState = "";
for (int i = 0; i < r3.Fields.Length; i++) {
if (r3.Fields[i].Name == "Value") {
startState = r3.Fields[0].DefaultValue.ToString();
break;
}
}
XmlNode node = manifest.CreateNode(XmlNodeType.Element,
rule.xmlTagName, "");
node.AppendChild(impNode);
node.AppendChild(expNode);
AddAttribute(node, "id", index.ToString());
if (startState != "") {
AddAttribute(node, "startStateId", startState);
}
// Contract name comes from either the attribute argument
// or the TRef type depending on the endpoint kind
rule.AddContractNameAttribute(this, node, data, contractName);
// add an attribute for each constructor argument if there is one
// This should only be true for input/ouput pipes
if (rule.constructorFields != null && rule.constructorFields.Length != 0) {
if (data.FixedArgs.Length != 0) {
for (int i = 0; i < data.FixedArgs.Length; i++) {
if (rule.constructorFields[i] != null) {
string name = rule.constructorFields[i];
if (data.FixedArgs[i] == null) {
AddAttribute(node, name, "");
}
else {
string value = data.FixedArgs[i].ToString();
AddAttribute(node, name, value);
}
}
else {
Console.WriteLine(" fixed=({0}), no matching constructor?",
data.FixedArgs[i] == null? null : data.FixedArgs[i].ToString() );
}
}
}
}
return node;
}
// this does the processing of each field-level decoration on a type,
// according to rules.fieldAttributes
private void ProcessFieldAttributes(MetaDataTypeDefinition type,
FieldDefinition[] rules,
XmlNode parent)
{
int endpointIndex = 0;
int intParameterIndex = 0;
int stringParameterIndex = 0;
int stringArrayParameterIndex = 0;
int boolParameterIndex = 0;
foreach (MetaDataField field in type.Fields) {
if (field.CustomAttributes == null) {
continue;
}
foreach (MetaDataCustomAttribute attrib in field.CustomAttributes) {
foreach (FieldDefinition rule in rules) {
if (attrib.Name == rule.attribute) {
// type check it
if (MatchFieldType(rule, field)) {
// custom step if Endpoint:
XmlNode node;
EndpointDefinition edrule = rule as EndpointDefinition;
if (edrule != null) {
node = CreateEndpointNode(attrib, edrule, endpointIndex++);
}
else if (rule is ParameterDefinition) {
int index;
if (rule.matchClassType == "I8") {
index = intParameterIndex++;
}
else if (rule.matchClassType == "STRING") {
index = stringParameterIndex++;
}
else if (rule.matchClassType == "BOOLEAN") {
index = boolParameterIndex++;
}
else if (rule.matchClassType == "SZARRAY") {
index = stringArrayParameterIndex++;
}
else {
index =999;
}
node = CreateNodeIndexed(attrib, rule, index);
}
else {
node = CreateNode(attrib, rule);
}
if (node != null) {
XmlNode group = parent[rule.xmlGroup];
if (group == null) {
group = AddElement(parent, rule.xmlGroup);
}
group.AppendChild(node);
}
}
}
}
}
}
}
// this does the processing of each class-level decoration on a type, as
// described in rules.classAttributes
private void ProcessClassAttributes(MetaDataTypeDefinition type,
ClassDefinition[] rules,
XmlNode parent)
{
if (type.CustomAttributes == null) {
return;
}
foreach (MetaDataCustomAttribute attrib in type.CustomAttributes) {
foreach (ClassDefinition rule in rules) {
if (attrib.Name == rule.attribute) {
XmlNode node = CreateNode(attrib, rule);
parent.AppendChild(node);
}
}
}
}
// this kicks off the processing of a type; see if it is decorated by
// rules.triggers, and if so, then build an xml node and process the rest
// of the rules object for this type
private bool ProcessType(MetaDataTypeDefinition type,
ParseRules rules,
XmlNode parent,
ref int categoryIndex,
ref XmlNode categories)
{
if (type.CustomAttributes == null) {
return false;
}
foreach (MetaDataCustomAttribute attrib in type.CustomAttributes) {
foreach (TriggerDefinition rule in rules.triggers) {
if (attrib.Name == rule.attribute) {
// only process this entry if the ancestry is valid
if (MatchInheritance(rule.derivesFromClass, type)) {
if (categoryIndex == 0) {
categories = AddElement(parent,"categories");
}
// create an xml node from (attrib, rule)
XmlNode node = CreateNode(attrib, rule);
AddAttribute(node, "id", (categoryIndex++).ToString());
if (rule.categoryName != null) {
AddAttribute(node, "name", rule.categoryName);
}
AddAttribute(node, "class", type.FullName);
// then do the rules.classDefinitions work
ProcessClassAttributes(type, rules.classAttributes, node);
// then do the rules.fieldDefinitions work
ProcessFieldAttributes(type, rules.fieldAttributes, node);
// then append this xml node to the manifest root doc
if (categories == null) {
Console.WriteLine(" categories is null at index {0}",categoryIndex);
}
else {
categories.AppendChild(node);
}
// once we match once, we stop operating on this type
return true;
}
}
}
}
return false;
}
private String KeyToString(byte[] key)
{
StringBuilder sb = new StringBuilder("");
int count = key.Length;
if (count > 0) {
for (int i = 0; i < count; i++) {
sb.Append(key[i].ToString("x2"));
}
}
return sb.ToString();
}
private String StripPathFromPath(string path)
{
int index = Math.Max(path.LastIndexOf(':'),
Math.Max(path.LastIndexOf('\\'),
path.LastIndexOf('/')));
if (index >= 0) {
return path.Substring(index + 1);
}
return path;
}
private String StripCacheFromPath(string path)
{
if (cache != null && path.ToLower().StartsWith(cache)) {
return path.Substring(cache.Length).Replace('\\', '/');
}
return path.Replace('\\', '/');
}
// augment a manifest based on the assembly given. This touches:
// <assemblies>, <ConsoleCategory>, <category>, and <drivercategory>
private void ParseAssemblies(XmlNode assemblies)
{
}
}