List<AssemblyDefinition> assemblies;
readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
- bool multiassembly;
+ string apistyle = string.Empty;
+ bool isClassicRun;
+
bool delete;
bool show_exceptions;
bool no_assembly_versions, ignore_missing_types;
{
return !string.IsNullOrWhiteSpace (droppedNamespace);
}
-
+
+ /// <summary>Logic flag to signify that we should list assemblies at the method level, since there are multiple
+ /// assemblies for a given type/method.</summary>
+ public bool IsMultiAssembly {
+ get {
+ return apistyle == "classic" || apistyle == "unified";
+ }
+ }
static List<string> droppedAssemblies = new List<string>();
{ "preserve",
"Do not delete members that don't exist in the assembly, but rather mark them as preserved.",
v => PreserveTag = "true" },
- { "multiassembly",
- "Allow types to be in multiple assemblies.",
- v => multiassembly = true },
+ { "api-style=",
+ "Denotes the apistyle. Currently, only `classic` and `unified` are supported. `classic` set of assemblies should be run first, immediately followed by 'unified' assemblies with the `dropns` parameter.",
+ v => { apistyle = v.ToLowerInvariant (); }},
};
var assemblies = Parse (p, args, "update",
"[OPTIONS]+ ASSEMBLIES",
return;
if (assemblies.Count == 0)
Error ("No assemblies specified.");
-
+
+ // validation for the api-style parameter
+ if (apistyle == "classic")
+ isClassicRun = true;
+ else if (apistyle == "unified") {
+ if (!droppedAssemblies.Any ())
+ Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace.");
+ } else if (!string.IsNullOrWhiteSpace (apistyle))
+ Error ("api-style '{0}' is not currently supported", apistyle);
+
+
foreach (var dir in assemblies
.Where (a => a.Contains (Path.DirectorySeparatorChar))
.Select (a => Path.GetDirectoryName (a)))
private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent)
{
XmlElement index_assembly = null;
- if (multiassembly)
+ if (IsMultiAssembly)
index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']");
if (index_assembly == null)
XmlElement index_types = WriteElement(index.DocumentElement, "Types");
XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies");
- if (!multiassembly)
+ if (!IsMultiAssembly)
index_assemblies.RemoveAll ();
saveDoc ();
var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']");
- var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']");
+ var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']");
var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']");
var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']");
bool isUnifiedRun = HasDroppedAnyNamespace ();
XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m));
if (mm == null) continue;
- if (MDocUpdater.SwitchingToMagicTypes) {
+ if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m)) {
// this is a unified style API that obviously doesn't exist in the classic API. Let's mark
// it with apistyle="unified", so that it's not displayed for classic style APIs
- mm.SetAttribute ("apistyle", "unified");
+ mm.AddApiStyle (ApiStyle.Unified);
}
members.AppendChild( mm );
bool unifiedRun = HasDroppedNamespace (type);
- var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']");
+ var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic);
var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified);
} else if (unifiedRun && nodeIsClassic) {
// this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly.
member.RemoveApiStyle (ApiStyle.Unified);
+ member.AddApiStyle (ApiStyle.Classic);
Warning ("Not removing '{0}' since it's still in the classic assembly.", signature);
} else if (unifiedRun && !nodeIsClassic) {
// unified run, and the node is not classic, which means it doesn't exist anywhere.
actuallyDelete ();
} else {
- if (!nodeIsClassic && !nodeIsUnified) { // regular codepath (ie. not classic/unified)
+ if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified)) { // regular codepath (ie. not classic/unified)
actuallyDelete ();
} else { // this is a classic run
Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature);
/// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
/// <returns>The assembly that was either added, or was already present</returns>
- static XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
+ XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type)
{
return AddAssemblyNameToNode (root, type.Module);
}
/// <summary>Adds an AssemblyInfo with AssemblyName node to an XmlElement.</summary>
/// <returns>The assembly that was either added, or was already present</returns>
- static XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
+ XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module)
{
Func<XmlElement, bool> assemblyFilter = x => {
var existingName = x.SelectSingleNode ("AssemblyName");
- return existingName != null && existingName.InnerText == module.Assembly.Name.Name;
+
+ bool apiStyleMatches = true;
+ string currentApiStyle = x.GetAttribute ("apistyle");
+ if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") ||
+ (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic"))) {
+ apiStyleMatches = false;
+ }
+ return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name));
};
return AddAssemblyXmlNode (
assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name),
() => {
XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true);
+
if (MDocUpdater.HasDroppedNamespace (module))
- ass.SetAttribute ("apistyle", "unified");
+ ass.AddApiStyle (ApiStyle.Unified);
+ if (isClassicRun)
+ ass.AddApiStyle (ApiStyle.Classic);
return ass;
}, module);
}
WriteElementText(me, "MemberType", GetMemberType(mi));
if (!no_assembly_versions) {
- if (!multiassembly)
+ if (!IsMultiAssembly)
UpdateAssemblyVersions (me, mi, true);
else {
var node = AddAssemblyNameToNode (me, mi.Module);
XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches);
if (thisAssemblyNode == null) {
thisAssemblyNode = makeNewNode ();
- setValue (thisAssemblyNode);
}
+ setValue (thisAssemblyNode);
if (isUnified) {
thisAssemblyNode.AddApiStyle (ApiStyle.Unified);
member.AppendChild (link);
AddTargets (em, info);
- extensionMethods.Add (em);
+ var sig = em.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value");
+ if (!IsMultiAssembly || (IsMultiAssembly && sig != null && !extensionMethods.Any (ex => ex.SelectSingleNode ("Member/MemberSignature[@Language='C#']/@Value").Value == sig.Value))) {
+ extensionMethods.Add (em);
+ }
}
private static void RemoveExcept (XmlNode node, string[] except)
if (val == null) value = "null";
else if (val is Enum) value = val.ToString();
else if (val is IFormattable) {
- value = ((IFormattable)val).ToString();
+ value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
if (val is string)
value = "\"" + value + "\"";
}
n.ParentNode.RemoveChild(n);
}
- private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
+ private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add)
{
TypeDefinition type = member as TypeDefinition;
if (type == null)
return assembly.Name.Version.ToString();
}
- private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
+ private bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add)
{
+ if (IsMultiAssembly)
+ return false;
+
XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions");
if (av != null) {
// AssemblyVersions is not part of the spec
e = root.OwnerDocument.CreateElement("AssemblyInfo");
if (MDocUpdater.HasDroppedNamespace (assembly)) {
- e.SetAttribute ("apistyle", "unified");
+ e.AddApiStyle (ApiStyle.Unified);
}
root.AppendChild(e);
var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter);
if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) {
// there's a classic node, we should add apistyles
- e.SetAttribute ("apistyle", "unified");
- thatNode.SetAttribute ("apistyle", "classic");
+ e.AddApiStyle (ApiStyle.Unified);
+ thatNode.AddApiStyle (ApiStyle.Classic);
}
return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add);
return method.GenericParameters.Count > 0;
}
- public static MemberReference Resolve (this MemberReference member)
- {
- FieldReference fr = member as FieldReference;
- if (fr != null)
- return fr.Resolve ();
- MethodReference mr = member as MethodReference;
- if (mr != null)
- return mr.Resolve ();
- TypeReference tr = member as TypeReference;
- if (tr != null)
- return tr.Resolve ();
- PropertyReference pr = member as PropertyReference;
- if (pr != null)
- return pr;
- EventReference er = member as EventReference;
- if (er != null)
- return er;
- throw new NotSupportedException ("Cannot find definition for " + member.ToString ());
- }
-
public static TypeReference GetUnderlyingType (this TypeDefinition type)
{
if (!type.IsEnum)
if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) {
element.SetAttribute ("apistyle", styleString);
}
+
+ // Propagate the API style up to the membernode if necessary
+ if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") {
+ var member = element.ParentNode;
+ var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
+ var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
+
+ var parentAttribute = element.ParentNode.Attributes ["apistyle"];
+ Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
+ Action propagateStyle = () => {
+ if (parentAttribute == null) {
+ // if it doesn't have the attribute, then add it
+ parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
+ parentAttribute.Value = styleString;
+ element.ParentNode.Attributes.Append (parentAttribute);
+ }
+ };
+
+ if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
+ removeStyle ();
+ else
+ propagateStyle ();
+ }
+ }
+ public static void AddApiStyle (this XmlNode node, ApiStyle style)
+ {
+ string styleString = style.ToString ().ToLowerInvariant ();
+ var existingAttribute = node.Attributes ["apistyle"];
+ if (existingAttribute == null) {
+ existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
+ node.Attributes.Append (existingAttribute);
+ }
+ existingAttribute.Value = styleString;
}
public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
{
{
HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
List<TypeReference> userInterfaces = new List<TypeReference> ();
- foreach (TypeReference iface in type.Interfaces) {
+ foreach (var ii in type.Interfaces) {
+ var iface = ii.InterfaceType;
TypeReference lookup = iface.Resolve () ?? iface;
if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
userInterfaces.Add (iface);
Action<TypeDefinition> a = null;
a = t => {
if (t == null) return;
- foreach (TypeReference r in t.Interfaces) {
- inheritedInterfaces.Add (GetQualifiedTypeName (r));
- a (r.Resolve ());
+ foreach (var r in t.Interfaces) {
+ inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
+ a (r.InterfaceType.Resolve ());
}
};
TypeReference baseRef = type.BaseType;
else
baseRef = null;
}
- foreach (TypeReference r in type.Interfaces)
- a (r.Resolve ());
+ foreach (var r in type.Interfaces)
+ a (r.InterfaceType.Resolve ());
return inheritedInterfaces;
}
}
{
return e.Name;
}
+
+ public string GetDeclaration (MemberReference mreference)
+ {
+ return GetDeclaration (mreference.Resolve ());
+ }
- public virtual string GetDeclaration (MemberReference member)
+ string GetDeclaration (IMemberDefinition member)
{
if (member == null)
throw new ArgumentNullException ("member");
buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
}
bool first = true;
- foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
- .Select (i => full.GetName (i))
+ foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.InterfaceType.Resolve ()))
+ .Select (i => full.GetName (i.InterfaceType))
.OrderBy (n => n)) {
if (first) {
buf.Append (" implements ");
.Append (val.ToString ())
.Append (')');
else if (val is IFormattable) {
- string value = ((IFormattable)val).ToString();
+ string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
buf.Append (" = ");
if (val is string)
buf.Append ("\"" + value + "\"");
else if (val is Enum)
buf.Append (" = ").Append (val.ToString ());
else if (val is IFormattable) {
- string value = ((IFormattable)val).ToString();
+ string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
if (val is string)
value = "\"" + value + "\"";
buf.Append (" = ").Append (value);
return buf;
}
- public override string GetDeclaration (MemberReference member)
- {
- TypeReference r = member as TypeReference;
- if (r != null) {
- return "T:" + GetTypeName (r);
- }
- return base.GetDeclaration (member);
- }
-
protected override string GetConstructorName (MethodReference constructor)
{
return GetMethodDefinitionName (constructor, "#ctor");