Imported Upstream version 4.6.0.125

Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-08-03 10:59:49 +00:00
parent a569aebcfd
commit e79aa3c0ed
17047 changed files with 3137615 additions and 392334 deletions

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;
namespace Xamarin.ApiDiff
{
public class ApiChange
{
public string Header;
public StringBuilder Member = new StringBuilder ();
public bool Breaking;
public bool AnyChange;
public bool HasIgnoredChanges;
public ApiChange Append (string text)
{
Member.Append (text);
return this;
}
public ApiChange AppendAdded (string text, bool breaking = false)
{
Member.Append ("<span class='added ").Append (breaking ? "added-breaking-inline" : string.Empty).Append ("'>");
Member.Append (text);
Member.Append ("</span>");
Breaking |= breaking;
AnyChange = true;
return this;
}
public ApiChange AppendRemoved (string text, bool breaking = true)
{
Member.Append ("<span class='removed removed-inline ").Append (breaking ? "removed-breaking-inline" : string.Empty).Append ("'>");
Member.Append (text);
Member.Append ("</span>");
Breaking |= breaking;
AnyChange = true;
return this;
}
public ApiChange AppendModified (string old, string @new, bool breaking = true)
{
if (old.Length > 0)
AppendRemoved (old, breaking);
if (old.Length > 0 && @new.Length > 0)
Append (" ");
if (@new.Length > 0)
AppendAdded (@new);
Breaking |= breaking;
AnyChange = true;
return this;
}
}
public class ApiChanges : Dictionary<string, List<ApiChange>> {
public void Add (XElement source, XElement target, ApiChange change)
{
if (!change.AnyChange) {
// This is most likely because the rendering doesn't take into account something that's different (solution: fix rendering).
if (!change.HasIgnoredChanges) {
var isField = source.Name.LocalName == "field";
if (isField) {
Console.WriteLine ("Comparison resulting in no changes (src: {2} dst: {3}) :\n{0}\n{1}\n\n", source.ToString (), target.ToString (), source.GetFieldAttributes (), target.GetFieldAttributes ());
} else {
Console.WriteLine ("Comparison resulting in no changes (src: {2} dst: {3}) :\n{0}\n{1}\n\n", source.ToString (), target.ToString (), source.GetMethodAttributes (), target.GetMethodAttributes ());
}
}
return;
}
List<ApiChange> list;
if (!TryGetValue (change.Header, out list)) {
list = new List<ApiChange> ();
base.Add (change.Header, list);
}
list.Add (change);
}
}
}

View File

@@ -0,0 +1,277 @@
//
// The main differences with mono-api-diff are:
// * this tool directly produce HTML similar to gdiff.sh used for Xamarin.iOS
// * this tool reports changes in an "evolutionary" way, not in a breaking way,
// i.e. it does not assume the source assembly is right (but simply older)
// * the diff .xml output was not easy to convert back into the HTML format
// that gdiff.sh produced
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013-2014 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Mono.Options;
namespace Xamarin.ApiDiff {
public static class State {
static TextWriter output;
public static TextWriter Output {
get {
if (output == null)
output = Console.Out;
return output;
}
set { output = value; }
}
public static string Assembly { get; set; }
public static string Namespace { get; set; }
public static string Type { get; set; }
public static string BaseType { get; set; }
public static int Indent { get; set; }
static List<Regex> ignoreAdded = new List<Regex> ();
public static List<Regex> IgnoreAdded {
get { return ignoreAdded; }
}
static List<Regex> ignoreNew = new List<Regex> ();
public static List<Regex> IgnoreNew {
get { return ignoreNew; }
}
static List<Regex> ignoreRemoved = new List<Regex> ();
public static List<Regex> IgnoreRemoved {
get { return ignoreRemoved; }
}
public static bool IgnoreParameterNameChanges { get; set; }
public static bool IgnoreVirtualChanges { get; set; }
public static bool IgnoreAddedPropertySetters { get; set; }
public static bool Lax;
public static bool Colorize = true;
}
class Program {
public static int Main (string[] args)
{
var showHelp = false;
string diff = null;
List<string> extra = null;
var options = new OptionSet {
{ "h|help", "Show this help", v => showHelp = true },
{ "d|diff=", "HTML diff file out output (omit for stdout)", v => diff = v },
{ "i|ignore=", "Ignore new, added, and removed members whose description matches a given C# regular expression (see below).",
v => {
var r = new Regex (v);
State.IgnoreAdded.Add (r);
State.IgnoreRemoved.Add (r);
State.IgnoreNew.Add (r);
}
},
{ "a|ignore-added=", "Ignore added members whose description matches a given C# regular expression (see below).",
v => State.IgnoreAdded.Add (new Regex (v))
},
{ "r|ignore-removed=", "Ignore removed members whose description matches a given C# regular expression (see below).",
v => State.IgnoreRemoved.Add (new Regex (v))
},
{ "n|ignore-new=", "Ignore new namespaces and types whose description matches a given C# regular expression (see below).",
v => State.IgnoreNew.Add (new Regex (v))
},
{ "ignore-changes-parameter-names", "Ignore changes to parameter names for identically prototyped methods.",
v => State.IgnoreParameterNameChanges = v != null
},
{ "ignore-changes-property-setters", "Ignore adding setters to properties.",
v => State.IgnoreAddedPropertySetters = v != null
},
{ "ignore-changes-virtual", "Ignore changing non-`virtual` to `virtual` or adding `override`.",
v => State.IgnoreVirtualChanges = v != null
},
{ "c|colorize:", "Colorize HTML output", v => State.Colorize = string.IsNullOrEmpty (v) ? true : bool.Parse (v) },
{ "x|lax", "Ignore duplicate XML entries", v => State.Lax = true }
};
try {
extra = options.Parse (args);
} catch (OptionException e) {
Console.WriteLine ("Option error: {0}", e.Message);
showHelp = true;
}
if (showHelp || extra == null || extra.Count < 2 || extra.Count > 3) {
Console.WriteLine (@"Usage: mono-api-html [options] <reference.xml> <assembly.xml> [diff.html]");
Console.WriteLine ();
Console.WriteLine ("Available options:");
options.WriteOptionDescriptions (Console.Out);
Console.WriteLine ();
Console.WriteLine ("Ignoring Members:");
Console.WriteLine ();
Console.WriteLine (" Members that were added can be filtered out of the diff by using the");
Console.WriteLine (" -i, --ignore-added option. The option takes a C# regular expression");
Console.WriteLine (" to match against member descriptions. For example, to ignore the");
Console.WriteLine (" introduction of the interfaces 'INSCopying' and 'INSCoding' on types");
Console.WriteLine (" pass the following to mono-api-html:");
Console.WriteLine ();
Console.WriteLine (" mono-api-html ... -i 'INSCopying$' -i 'INSCoding$'");
Console.WriteLine ();
Console.WriteLine (" The regular expressions will match any member description ending with");
Console.WriteLine (" 'INSCopying' or 'INSCoding'.");
Console.WriteLine ();
return 1;
}
var input = extra [0];
var output = extra [1];
if (extra.Count == 3 && diff == null)
diff = extra [2];
try {
var ac = new AssemblyComparer (input, output);
if (diff != null) {
string diffHtml = String.Empty;
using (var writer = new StringWriter ()) {
State.Output = writer;
ac.Compare ();
diffHtml = State.Output.ToString ();
}
if (diffHtml.Length > 0) {
using (var file = new StreamWriter (diff)) {
file.WriteLine ("<div>");
if (State.Colorize) {
file.WriteLine ("<style scoped>");
file.WriteLine ("\t.obsolete { color: gray; }");
file.WriteLine ("\t.added { color: green; }");
file.WriteLine ("\t.removed-inline { text-decoration: line-through; }");
file.WriteLine ("\t.removed-breaking-inline { color: red;}");
file.WriteLine ("\t.added-breaking-inline { text-decoration: underline; }");
file.WriteLine ("\t.nonbreaking { color: black; }");
file.WriteLine ("\t.breaking { color: red; }");
file.WriteLine ("</style>");
}
file.WriteLine (
@"<script type=""text/javascript"">
// Only some elements have 'data-is-[non-]breaking' attributes. Here we
// iterate over all descendents elements, and set 'data-is-[non-]breaking'
// depending on whether there are any descendents with that attribute.
function propagateDataAttribute (element)
{
if (element.hasAttribute ('data-is-propagated'))
return;
var i;
var any_breaking = element.hasAttribute ('data-is-breaking');
var any_non_breaking = element.hasAttribute ('data-is-non-breaking');
for (i = 0; i < element.children.length; i++) {
var el = element.children [i];
propagateDataAttribute (el);
any_breaking |= el.hasAttribute ('data-is-breaking');
any_non_breaking |= el.hasAttribute ('data-is-non-breaking');
}
if (any_breaking)
element.setAttribute ('data-is-breaking', null);
else if (any_non_breaking)
element.setAttribute ('data-is-non-breaking', null);
element.setAttribute ('data-is-propagated', null);
}
function hideNonBreakingChanges ()
{
var topNodes = document.querySelectorAll ('[data-is-topmost]');
var n;
var i;
for (n = 0; n < topNodes.length; n++) {
propagateDataAttribute (topNodes [n]);
var elements = topNodes [n].querySelectorAll ('[data-is-non-breaking]');
for (i = 0; i < elements.length; i++) {
var el = elements [i];
if (!el.hasAttribute ('data-original-display'))
el.setAttribute ('data-original-display', el.style.display);
el.style.display = 'none';
}
}
var links = document.getElementsByClassName ('hide-nonbreaking');
for (i = 0; i < links.length; i++)
links [i].style.display = 'none';
links = document.getElementsByClassName ('restore-nonbreaking');
for (i = 0; i < links.length; i++)
links [i].style.display = '';
}
function showNonBreakingChanges ()
{
var elements = document.querySelectorAll ('[data-original-display]');
var i;
for (i = 0; i < elements.length; i++) {
var el = elements [i];
el.style.display = el.getAttribute ('data-original-display');
}
var links = document.getElementsByClassName ('hide-nonbreaking');
for (i = 0; i < links.length; i++)
links [i].style.display = '';
links = document.getElementsByClassName ('restore-nonbreaking');
for (i = 0; i < links.length; i++)
links [i].style.display = 'none';
}
</script>");
if (ac.SourceAssembly == ac.TargetAssembly) {
file.WriteLine ("<h1>{0}.dll</h1>", ac.SourceAssembly);
} else {
file.WriteLine ("<h1>{0}.dll vs {1}.dll</h1>", ac.SourceAssembly, ac.TargetAssembly);
}
file.WriteLine ("<a href='javascript: hideNonBreakingChanges (); ' class='hide-nonbreaking'>Hide non-breaking changes</a>");
file.WriteLine ("<a href='javascript: showNonBreakingChanges (); ' class='restore-nonbreaking' style='display: none;'>Show non-breaking changes</a>");
file.WriteLine ("<br/>");
file.WriteLine ("<div data-is-topmost>");
file.Write (diffHtml);
file.WriteLine ("</div> <!-- end topmost div -->");
file.WriteLine ("</div>");
}
}
} else {
State.Output = Console.Out;
ac.Compare ();
}
}
catch (Exception e) {
Console.WriteLine (e);
return 1;
}
return 0;
}
}
}

View File

@@ -0,0 +1,79 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class AssemblyComparer : Comparer {
XDocument source;
XDocument target;
NamespaceComparer comparer;
public AssemblyComparer (string sourceFile, string targetFile)
{
source = XDocument.Load (sourceFile);
target = XDocument.Load (targetFile);
comparer = new NamespaceComparer ();
}
public string SourceAssembly { get; private set; }
public string TargetAssembly { get; private set; }
public void Compare ()
{
Compare (source.Element ("assemblies").Elements ("assembly"),
target.Element ("assemblies").Elements ("assembly"));
}
public override void SetContext (XElement current)
{
State.Assembly = current.GetAttribute ("name");
}
public override void Added (XElement target, bool wasParentAdded)
{
// one assembly per xml file
}
public override void Modified (XElement source, XElement target, ApiChanges diff)
{
SourceAssembly = source.GetAttribute ("name");
TargetAssembly = target.GetAttribute ("name");
// TODO: version
// ? custom attributes ?
comparer.Compare (source, target);
}
public override void Removed (XElement source)
{
// one assembly per xml file
}
}
}

View File

@@ -0,0 +1,261 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class ClassComparer : Comparer {
InterfaceComparer icomparer;
ConstructorComparer ccomparer;
FieldComparer fcomparer;
PropertyComparer pcomparer;
EventComparer ecomparer;
MethodComparer mcomparer;
ClassComparer kcomparer;
public ClassComparer ()
{
icomparer = new InterfaceComparer ();
ccomparer = new ConstructorComparer ();
fcomparer = new FieldComparer ();
pcomparer = new PropertyComparer ();
ecomparer = new EventComparer ();
mcomparer = new MethodComparer ();
}
public override void SetContext (XElement current)
{
State.Type = current.GetAttribute ("name");
State.BaseType = current.GetAttribute ("base");
}
public void Compare (XElement source, XElement target)
{
var s = source.Element ("classes");
var t = target.Element ("classes");
if (XNode.DeepEquals (s, t))
return;
Compare (s.Elements ("class"), t.Elements ("class"));
}
public override void Added (XElement target, bool wasParentAdded)
{
string name = target.Attribute ("name").Value;
if (State.IgnoreNew.Any (re => re.IsMatch (name)))
return;
Output.WriteLine ("<div> <!-- start type {0} -->", name);
Output.WriteLine ("<h3>New Type {0}.{1}</h3>", State.Namespace, name);
Output.WriteLine ("<pre class='added' data-is-non-breaking>");
State.Indent = 0;
AddedInner (target);
Output.WriteLine ("</pre>");
Output.WriteLine ("</div> <!-- end type {0} -->", name);
}
public void AddedInner (XElement target)
{
SetContext (target);
if (target.IsTrue ("serializable"))
Indent ().WriteLine ("[Serializable]");
var type = target.Attribute ("type").Value;
if (type == "enum") {
// check if [Flags] is present
var cattrs = target.Element ("attributes");
if (cattrs != null) {
foreach (var ca in cattrs.Elements ("attribute")) {
if (ca.GetAttribute ("name") == "System.FlagsAttribute") {
Indent ().WriteLine ("[Flags]");
break;
}
}
}
}
Indent ().Write ("public");
if (type != "enum") {
bool seal = target.IsTrue ("sealed");
bool abst = target.IsTrue ("abstract");
if (seal && abst)
Output.Write (" static");
else if (seal && type != "struct")
Output.Write (" sealed");
else if (abst && type != "interface")
Output.Write (" abstract");
}
Output.Write (' ');
Output.Write (type);
Output.Write (' ');
Output.Write (target.GetAttribute ("name"));
var baseclass = target.GetAttribute ("base");
if ((type != "enum") && (type != "struct")) {
if (baseclass != null) {
if (baseclass == "System.Object") {
// while true we do not need to be reminded every time...
baseclass = null;
} else {
Output.Write (" : ");
Output.Write (baseclass);
}
}
}
// interfaces on enums are "standard" not user provided - so we do not want to show them
if (type != "enum") {
var i = target.Element ("interfaces");
if (i != null) {
var interfaces = new List<string> ();
foreach (var iface in i.Elements ("interface"))
interfaces.Add (icomparer.GetDescription (iface));
Output.Write ((baseclass == null) ? " : " : ", ");
Output.Write (String.Join (", ", interfaces));
}
}
Output.WriteLine (" {");
var t = target.Element ("constructors");
if (t != null) {
Indent ().WriteLine ("\t// constructors");
foreach (var ctor in t.Elements ("constructor"))
ccomparer.Added (ctor, true);
}
t = target.Element ("fields");
if (t != null) {
if (type != "enum")
Indent ().WriteLine ("\t// fields");
else
SetContext (target);
foreach (var field in t.Elements ("field"))
fcomparer.Added (field, true);
}
t = target.Element ("properties");
if (t != null) {
Indent ().WriteLine ("\t// properties");
foreach (var property in t.Elements ("property"))
pcomparer.Added (property, true);
}
t = target.Element ("events");
if (t != null) {
Indent ().WriteLine ("\t// events");
foreach (var evnt in t.Elements ("event"))
ecomparer.Added (evnt, true);
}
t = target.Element ("methods");
if (t != null) {
Indent ().WriteLine ("\t// methods");
foreach (var method in t.Elements ("method"))
mcomparer.Added (method, true);
}
t = target.Element ("classes");
if (t != null) {
Output.WriteLine ();
Indent ().WriteLine ("\t// inner types");
kcomparer = new NestedClassComparer ();
State.Indent++;
foreach (var inner in t.Elements ("class"))
kcomparer.AddedInner (inner);
State.Indent--;
}
Indent ().WriteLine ("}");
}
public override void Modified (XElement source, XElement target, ApiChanges diff)
{
// hack - there could be changes that we're not monitoring (e.g. attributes properties)
var output = Output;
State.Output = new StringWriter ();
var sb = source.GetAttribute ("base");
var tb = target.GetAttribute ("base");
if (sb != tb) {
Output.Write ("Modified base type: ");
Output.WriteLine (new ApiChange ().AppendModified (sb, tb, true).Member.ToString ());
}
ccomparer.Compare (source, target);
icomparer.Compare (source, target);
fcomparer.Compare (source, target);
pcomparer.Compare (source, target);
ecomparer.Compare (source, target);
mcomparer.Compare (source, target);
var si = source.Element ("classes");
if (si != null) {
var ti = target.Element ("classes");
kcomparer = new NestedClassComparer ();
kcomparer.Compare (si.Elements ("class"), ti == null ? null : ti.Elements ("class"));
}
var s = (Output as StringWriter).ToString ();
State.Output = output;
if (s.Length > 0) {
var tn = GetTypeName (target);
Output.WriteLine ("<!-- start type {0} --> <div>", tn);
Output.WriteLine ("<h3>Type Changed: {0}.{1}</h3>", State.Namespace, GetTypeName (target));
Output.WriteLine (s);
Output.WriteLine ("</div> <!-- end type {0} -->", tn);
}
}
public override void Removed (XElement source)
{
Output.Write ("<h3>Removed Type <span class='breaking' data-is-breaking>{0}.{1}</span></h3>", State.Namespace, GetTypeName (source));
}
public virtual string GetTypeName (XElement type)
{
return type.GetAttribute ("name");
}
}
public class NestedClassComparer : ClassComparer {
public override void SetContext (XElement current)
{
}
public override string GetTypeName (XElement type)
{
return State.Type + "." + base.GetTypeName (type);
}
}
}

View File

@@ -0,0 +1,98 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public abstract class Comparer {
protected List<XElement> removed = new List<XElement> ();
protected ApiChanges modified = new ApiChanges ();
public TextWriter Output {
get { return State.Output; }
}
protected TextWriter Indent ()
{
for (int i = 0; i < State.Indent; i++)
State.Output.Write ("\t");
return State.Output;
}
public abstract void Added (XElement target, bool wasParentAdded);
public abstract void Modified (XElement source, XElement target, ApiChanges changes);
public abstract void Removed (XElement source);
public virtual bool Equals (XElement source, XElement target, ApiChanges changes)
{
return XNode.DeepEquals (source, target);
}
public abstract void SetContext (XElement current);
public virtual void Compare (IEnumerable<XElement> source, IEnumerable<XElement> target)
{
removed.Clear ();
modified.Clear ();
foreach (var s in source) {
SetContext (s);
string sn = s.GetAttribute ("name");
var t = target == null ? null : target.SingleOrDefault (x => x.GetAttribute ("name") == sn);
if (t == null) {
// not in target, it was removed
removed.Add (s);
} else {
t.Remove ();
// possibly modified
if (Equals (s, t, modified))
continue;
// still in target so will be part of Added
Modified (s, t, modified);
}
}
// delayed, that way we show "Modified", "Added" and then "Removed"
foreach (var item in removed) {
SetContext (item);
Removed (item);
}
// remaining == newly added in target
if (target != null) {
foreach (var item in target) {
SetContext (item);
Added (item, false);
}
}
}
}
}

View File

@@ -0,0 +1,148 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
// MethodComparer inherits from this one
public class ConstructorComparer : MemberComparer {
public override string GroupName {
get { return "constructors"; }
}
public override string ElementName {
get { return "constructor"; }
}
public override bool Find (XElement e)
{
return (e.Attribute ("name").Value == Source.Attribute ("name").Value);
}
void RenderReturnType (XElement source, XElement target, ApiChange change)
{
var srcType = source.GetTypeName ("returntype");
var tgtType = target.GetTypeName ("returntype");
if (srcType != tgtType) {
change.AppendModified (srcType, tgtType, true);
change.Append (" ");
} else if (srcType != null) {
// ctor don't have a return type
change.Append (srcType);
change.Append (" ");
}
}
public override bool Equals (XElement source, XElement target, ApiChanges changes)
{
if (base.Equals (source, target, changes))
return true;
var change = new ApiChange ();
change.Header = "Modified " + GroupName;
RenderMethodAttributes (source, target, change);
RenderReturnType (source, target, change);
RenderName (source, target, change);
RenderGenericParameters (source, target, change);
RenderParameters (source, target, change);
changes.Add (source, target, change);
return false;
}
public override string GetDescription (XElement e)
{
var sb = new StringBuilder ();
var attribs = e.Attribute ("attrib");
if (attribs != null) {
var attr = (MethodAttributes) Int32.Parse (attribs.Value);
if ((attr & MethodAttributes.Public) != MethodAttributes.Public) {
sb.Append ("protected ");
} else {
sb.Append ("public ");
}
if ((attr & MethodAttributes.Static) != 0) {
sb.Append ("static ");
} else if ((attr & MethodAttributes.Virtual) != 0) {
if ((attr & MethodAttributes.VtableLayoutMask) == 0)
sb.Append ("override ");
else
sb.Append ("virtual ");
}
}
string name = e.GetAttribute ("name");
var r = e.GetTypeName ("returntype");
if (r != null) {
// ctor dont' have a return type
sb.Append (r).Append (' ');
} else {
// show the constructor as it would be defined in C#
name = name.Replace (".ctor", State.Type);
}
// the XML file `name` does not contain parameter names, so we must process them ourselves
// which gives us the opportunity to simplify type names
sb.Append (name.Substring (0, name.IndexOf ('(')));
var genericp = e.Element ("generic-parameters");
if (genericp != null) {
var list = new List<string> ();
foreach (var p in genericp.Elements ("generic-parameter")) {
list.Add (p.GetTypeName ("name"));
}
sb.Append ("&lt;").Append (String.Join (", ", list)).Append ("&gt;");
}
sb.Append (" (");
var parameters = e.Element ("parameters");
if (parameters != null) {
var list = new List<string> ();
foreach (var p in parameters.Elements ("parameter")) {
var pTypeName = p.GetTypeName ("type");
list.Add (State.IgnoreParameterNameChanges
? pTypeName
: pTypeName + " " + p.GetAttribute ("name"));
}
sb.Append (String.Join (", ", list));
}
sb.Append (");");
return sb.ToString ();
}
}
}

View File

@@ -0,0 +1,75 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Text;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class EventComparer : MemberComparer {
public override string GroupName {
get { return "events"; }
}
public override string ElementName {
get { return "event"; }
}
public override bool Equals (XElement source, XElement target, ApiChanges changes)
{
if (base.Equals (source, target, changes))
return true;
var change = new ApiChange ();
change.Header = "Modified " + GroupName;
change.Append ("public event ");
var srcEventType = source.GetTypeName ("eventtype");
var tgtEventType = target.GetTypeName ("eventtype");
if (srcEventType != tgtEventType) {
change.AppendModified (srcEventType, tgtEventType, true);
} else {
change.Append (srcEventType);
}
change.Append (" ");
change.Append (source.GetAttribute ("name")).Append (";");
return false;
}
public override string GetDescription (XElement e)
{
StringBuilder sb = new StringBuilder ();
// TODO: attribs
sb.Append ("public event ");
sb.Append (e.GetTypeName ("eventtype")).Append (' ');
sb.Append (e.GetAttribute ("name")).Append (';');
return sb.ToString ();
}
}
}

View File

@@ -0,0 +1,211 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013-2014 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class FieldComparer : MemberComparer {
public override string GroupName {
get { return "fields"; }
}
public override string ElementName {
get { return "field"; }
}
void RenderFieldAttributes (FieldAttributes source, FieldAttributes target, ApiChange change)
{
var srcNotSerialized = (source & FieldAttributes.NotSerialized) == FieldAttributes.NotSerialized;
var tgtNotSerialized = (target & FieldAttributes.NotSerialized) == FieldAttributes.NotSerialized;
if (srcNotSerialized != tgtNotSerialized) {
// this is not a breaking change, so only render it if it changed.
if (srcNotSerialized) {
change.AppendRemoved ("[NonSerialized]\n");
} else {
change.AppendAdded ("[NonSerialized]\n");
}
}
// the visibility values are the same for MethodAttributes and FieldAttributes, so just use the same method.
RenderVisibility ((MethodAttributes) source, (MethodAttributes) target, change);
// same for the static flag
RenderStatic ((MethodAttributes) source, (MethodAttributes) target, change);
var srcLiteral = (source & FieldAttributes.Literal) != 0;
var tgtLiteral = (target & FieldAttributes.Literal) != 0;
if (srcLiteral) {
if (tgtLiteral) {
change.Append ("const ");
} else {
change.AppendRemoved ("const", true).Append (" ");
}
} else if (tgtLiteral) {
change.AppendAdded ("const", true).Append (" ");
}
var srcInitOnly = (source & FieldAttributes.InitOnly) != 0;
var tgtInitOnly = (target & FieldAttributes.InitOnly) != 0;
if (srcInitOnly) {
if (tgtInitOnly) {
change.Append ("readonly ");
} else {
change.AppendRemoved ("readonly", false).Append (" ");
}
} else if (tgtInitOnly) {
change.AppendAdded ("readonly", true).Append (" ");
}
}
public override bool Equals (XElement source, XElement target, ApiChanges changes)
{
if (base.Equals (source, target, changes))
return true;
var name = source.GetAttribute ("name");
var srcValue = source.GetAttribute ("value");
var tgtValue = target.GetAttribute ("value");
var change = new ApiChange ();
change.Header = "Modified " + GroupName;
if (State.BaseType == "System.Enum") {
change.Append (name).Append (" = ");
if (srcValue != tgtValue) {
change.AppendModified (srcValue, tgtValue, true);
} else {
change.Append (srcValue);
}
} else {
RenderFieldAttributes (source.GetFieldAttributes (), target.GetFieldAttributes (), change);
var srcType = source.GetTypeName ("fieldtype");
var tgtType = target.GetTypeName ("fieldtype");
if (srcType != tgtType) {
change.AppendModified (srcType, tgtType, true);
} else {
change.Append (srcType);
}
change.Append (" ");
change.Append (name);
if (srcType == "string" && srcValue != null)
srcValue = "\"" + srcValue + "\"";
if (tgtType == "string" && tgtValue != null)
tgtValue = "\"" + tgtValue + "\"";
if (srcValue != tgtValue) {
change.Append (" = ");
if (srcValue == null)
srcValue = "null";
if (tgtValue == null)
tgtValue = "null";
change.AppendModified (srcValue, tgtValue, true);
} else if (srcValue != null) {
change.Append (" = ");
change.Append (srcValue);
}
change.Append (";");
}
changes.Add (source, target, change);
return false;
}
public override string GetDescription (XElement e)
{
var sb = new StringBuilder ();
string name = e.GetAttribute ("name");
string value = e.GetAttribute ("value");
if (State.BaseType == "System.Enum") {
sb.Append (name).Append (" = ").Append (value).Append (',');
} else {
var attribs = e.Attribute ("attrib");
if (attribs != null) {
var attr = (FieldAttributes)Int32.Parse (attribs.Value);
if ((attr & FieldAttributes.Public) != FieldAttributes.Public) {
sb.Append ("protected ");
} else {
sb.Append ("public ");
}
if ((attr & FieldAttributes.Static) != 0)
sb.Append ("static ");
if ((attr & FieldAttributes.Literal) != 0)
sb.Append ("const ");
}
string ftype = e.GetTypeName ("fieldtype");
sb.Append (ftype).Append (' ');
sb.Append (name);
if (ftype == "string" && e.Attribute ("value") != null) {
if (value == null)
sb.Append (" = null");
else
sb.Append (" = \"").Append (value).Append ('"');
}
sb.Append (';');
}
return sb.ToString ();
}
public override void BeforeAdding (IEnumerable<XElement> list)
{
first = true;
if (State.BaseType == "System.Enum") {
Output.WriteLine ("<div>");
Output.WriteLine ("<p>Added value{0}:</p>", list.Count () > 1 ? "s" : String.Empty);
Output.WriteLine ("<pre class='added' data-is-non-breaking>");
} else {
base.BeforeAdding (list);
}
}
public override void BeforeRemoving (IEnumerable<XElement> list)
{
first = true;
if (State.BaseType == "System.Enum") {
Output.WriteLine ("<p>Removed value{0}:</p>", list.Count () > 1 ? "s" : String.Empty);
Output.WriteLine ("<pre class='removed' data-is-breaking>");
} else {
base.BeforeRemoving (list);
}
}
}
}

View File

@@ -0,0 +1,207 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013-2014 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public static class Helper {
public static bool IsTrue (this XElement self, string name)
{
return (self.GetAttribute (name) == "true");
}
public static string GetAttribute (this XElement self, string name)
{
var n = self.Attribute (name);
if (n == null)
return null;
return n.Value;
}
// null == no obsolete, String.Empty == no description
public static string GetObsoleteMessage (this XElement self)
{
var cattrs = self.Element ("attributes");
if (cattrs == null)
return null;
foreach (var ca in cattrs.Elements ("attribute")) {
if (ca.GetAttribute ("name") != "System.ObsoleteAttribute")
continue;
var props = ca.Element ("properties");
if (props == null)
return String.Empty; // no description
foreach (var p in props.Elements ("property")) {
if (p.GetAttribute ("name") != "Message")
continue;
return p.GetAttribute ("value");
}
}
return null;
}
public static IEnumerable<XElement> Descendants (this XElement self, params string[] names)
{
XElement el = self;
if (el == null)
return null;
for (int i = 0; i < names.Length - 1; i++) {
el = el.Element (names [i]);
if (el == null)
return null;
}
return el.Elements (names [names.Length - 1]);
}
public static List<XElement> DescendantList (this XElement self, params string[] names)
{
var descendants = self.Descendants (names);
if (descendants == null)
return null;
return descendants.ToList ();
}
// make it beautiful (.NET -> C#)
public static string GetTypeName (this XElement self, string name)
{
string type = self.GetAttribute (name);
if (type == null)
return null;
StringBuilder sb = null;
bool is_nullable = false;
if (type.StartsWith ("System.Nullable`1[", StringComparison.Ordinal)) {
is_nullable = true;
sb = new StringBuilder (type, 18, type.Length - 19, 1024);
} else {
sb = new StringBuilder (type);
}
bool is_ref = (sb [sb.Length - 1] == '&');
if (is_ref)
sb.Remove (sb.Length - 1, 1);
int array = 0;
while ((sb [sb.Length - 1] == ']') && (sb [sb.Length - 2] == '[')) {
sb.Remove (sb.Length - 2, 2);
array++;
}
bool is_pointer = (sb [sb.Length - 1] == '*');
if (is_pointer)
sb.Remove (sb.Length - 1, 1);
type = GetTypeName (sb.Replace ('+', '.').ToString ());
sb.Length = 0;
if (is_ref)
sb.Append (self.GetAttribute ("direction")).Append (' ');
sb.Append (type);
while (array-- > 0)
sb.Append ("[]");
if (is_nullable)
sb.Append ('?');
if (is_pointer)
sb.Append ('*');
return sb.ToString ();
}
static string GetTypeName (string type)
{
int pos = type.IndexOf ('`');
if (pos >= 0) {
int end = type.LastIndexOf (']');
string subtype = type.Substring (pos + 3, end - pos - 3);
return type.Substring (0, pos) + "&lt;" + GetTypeName (subtype) + "&gt;";
}
switch (type) {
case "System.String":
return "string";
case "System.Int32":
return "int";
case "System.UInt32":
return "uint";
case "System.Int64":
return "long";
case "System.UInt64":
return "ulong";
case "System.Void":
return "void";
case "System.Boolean":
return "bool";
case "System.Object":
return "object";
case "System.Single":
return "float";
case "System.Double":
return "double";
case "System.Byte":
return "byte";
case "System.SByte":
return "sbyte";
case "System.Int16":
return "short";
case "System.UInt16":
return "ushort";
case "System.Char":
return "char";
case "System.nint":
return "nint";
case "System.nuint":
return "uint";
case "System.nfloat":
return "nfloat";
case "System.IntPtr":
return "IntPtr";
default:
if (type.StartsWith (State.Namespace, StringComparison.Ordinal))
type = type.Substring (State.Namespace.Length + 1);
return type;
}
}
public static MethodAttributes GetMethodAttributes (this XElement element)
{
var srcAttribs = element.Attribute ("attrib");
return (MethodAttributes) (srcAttribs != null ? Int32.Parse (srcAttribs.Value) : 0);
}
public static FieldAttributes GetFieldAttributes (this XElement element)
{
var srcAttribs = element.Attribute ("attrib");
return (FieldAttributes) (srcAttribs != null ? Int32.Parse (srcAttribs.Value) : 0);
}
}
}

View File

@@ -0,0 +1,47 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class InterfaceComparer : MemberComparer {
public override string GroupName {
get { return "interfaces"; }
}
public override string ElementName {
get { return "interface"; }
}
public override string GetDescription (XElement e)
{
return e.GetTypeName ("name");
}
}
}

View File

@@ -0,0 +1,10 @@
thisdir = tools/mono-api-html
SUBDIRS =
include ../../build/rules.make
LIB_REFS = Mono.Cecil System.Xml System.Core System System.Xml.Linq
LOCAL_MCS_FLAGS =
PROGRAM = mono-api-html.exe
include ../../build/executable.make

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class MethodComparer : ConstructorComparer {
public override string GroupName {
get { return "methods"; }
}
public override string ElementName {
get { return "method"; }
}
// operators have identical names but vary by return types
public override bool Find (XElement e)
{
if (e.GetAttribute ("name") != Source.GetAttribute ("name"))
return false;
if (e.GetAttribute ("returntype") != Source.GetAttribute ("returntype"))
return false;
var eGP = e.Element ("generic-parameters");
var sGP = Source.Element ("generic-parameters");
if (eGP == null && sGP == null)
return true;
else if (eGP == null ^ sGP == null)
return false;
else {
var eGPs = eGP.Elements ("generic-parameter");
var sGPs = sGP.Elements ("generic-parameter");
return eGPs.Count () == sGPs.Count ();
}
}
protected override bool IsBreakingRemoval (XElement e)
{
// Removing virtual methods that override another method is not a breaking change.
var is_override = e.Attribute ("is-override");
if (is_override != null)
return is_override.Value != "true";
return true; // all other removals are breaking changes
}
}
}

View File

@@ -0,0 +1,104 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class NamespaceComparer : Comparer {
ClassComparer comparer;
public NamespaceComparer ()
{
comparer = new ClassComparer ();
}
public void Compare (XElement source, XElement target)
{
var s = source.Element ("namespaces");
var t = target.Element ("namespaces");
if (XNode.DeepEquals (s, t))
return;
Compare (s.Elements ("namespace"), t.Elements ("namespace"));
}
public override void SetContext (XElement current)
{
State.Namespace = current.Attribute ("name").Value;
}
public override void Added (XElement target, bool wasParentAdded)
{
string name = target.Attribute ("name").Value;
if (State.IgnoreNew.Any (re => re.IsMatch (name)))
return;
Output.WriteLine ("<!-- start namespace {0} --> <div> ", name);
Output.WriteLine ("<h2>New Namespace {0}</h2>", name);
Output.WriteLine ();
// list all new types
foreach (var addedType in target.Element ("classes").Elements ("class"))
comparer.Added (addedType, true);
Output.WriteLine ("</div> <!-- end namespace {0} -->", name);
Output.WriteLine ();
}
public override void Modified (XElement source, XElement target, ApiChanges differences)
{
var output = Output;
State.Output = new StringWriter ();
comparer.Compare (source, target);
var s = Output.ToString ();
State.Output = output;
if (s.Length > 0) {
var name = target.Attribute ("name").Value;
Output.WriteLine ("<!-- start namespace {0} --> <div> ", name);
Output.WriteLine ("<h2>Namespace {0}</h2>", name);
Output.WriteLine (s);
Output.WriteLine ("</div> <!-- end namespace {0} -->", name);
}
}
public override void Removed (XElement source)
{
var name = source.Attribute ("name").Value;
Output.WriteLine ("<!-- start namespace {0} --> <div>", name);
Output.WriteLine ("<h2>Removed Namespace {0}</h2>", name);
Output.WriteLine ();
// list all removed types
foreach (var removedType in source.Element ("classes").Elements ("class"))
comparer.Removed (removedType);
Output.WriteLine ("</div> <!-- end namespace {0} -->", name);
Output.WriteLine ();
}
}
}

View File

@@ -0,0 +1,254 @@
//
// Authors
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013 Xamarin Inc. http://www.xamarin.com
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public class PropertyComparer : MemberComparer {
public override string GroupName {
get { return "properties"; }
}
public override string ElementName {
get { return "property"; }
}
public override bool Find (XElement e)
{
if (!base.Find (e))
return false;
// the same Item (indexer) property can have different parameters
return e.GetAttribute ("params") == Source.GetAttribute ("params");
}
void GetAccessors (XElement element, out XElement getter, out XElement setter)
{
var methods = element.Element ("methods");
getter = null;
setter = null;
if (methods == null)
return;
foreach (var m in methods.Elements ("method")) {
var n = m.GetAttribute ("name");
if (n.StartsWith ("get_", StringComparison.Ordinal)) {
getter = m;
} else if (n.StartsWith ("set_", StringComparison.Ordinal)) {
setter = m;
}
}
}
MethodAttributes GetMethodAttributes (XElement getter, XElement setter)
{
if (getter == null)
return setter.GetMethodAttributes ();
else if (setter == null)
return getter.GetMethodAttributes ();
var gAttr = getter.GetMethodAttributes ();
var sAttr = setter.GetMethodAttributes ();
var g = gAttr & MethodAttributes.MemberAccessMask;
var s = sAttr & MethodAttributes.MemberAccessMask;
// Visibility is ordered numerically (higher value = more visible).
// We want the most visible.
var visibility = (MethodAttributes) Math.Max ((int) g, (int) s);
// Do a bitwise or with the rest of the flags
var g_no_visibility = gAttr & ~MethodAttributes.MemberAccessMask;
var s_no_visibility = sAttr & ~MethodAttributes.MemberAccessMask;
return g_no_visibility | s_no_visibility | visibility;
}
void RenderPropertyType (XElement source, XElement target, ApiChange change)
{
var srcType = source.GetTypeName ("ptype");
var tgtType = target.GetTypeName ("ptype");
if (srcType == tgtType) {
change.Append (tgtType);
} else {
change.AppendModified (srcType, tgtType, true);
}
change.Append (" ");
}
void RenderAccessors (XElement srcGetter, XElement tgtGetter, XElement srcSetter, XElement tgtSetter, ApiChange change)
{
// FIXME: this doesn't render changes in the accessor visibility (a protected setter can become public for instance).
change.Append (" {");
if (tgtGetter != null) {
if (srcGetter != null) {
change.Append (" ").Append ("get;");
} else {
change.Append (" ").AppendAdded ("get;");
}
} else if (srcGetter != null) {
change.Append (" ").AppendRemoved ("get;");
}
if (tgtSetter != null) {
if (srcSetter != null) {
change.Append (" ").Append ("set;");
} else {
change.Append (" ").AppendAdded ("set;");
}
} else if (srcSetter != null) {
change.Append (" ").AppendRemoved ("set;");
}
change.Append (" }");
// Ignore added property setters if asked to
if (srcSetter == null && tgtSetter != null && State.IgnoreAddedPropertySetters && !change.Breaking) {
change.AnyChange = false;
change.HasIgnoredChanges = true;
}
}
void RenderIndexers (List<XElement> srcIndexers, List<XElement> tgtIndexers, ApiChange change)
{
change.Append ("this [");
for (int i = 0; i < srcIndexers.Count; i++) {
var source = srcIndexers [i];
var target = tgtIndexers [i];
if (i > 0)
change.Append (", ");
var srcType = source.GetTypeName ("type");
var tgtType = target.GetTypeName ("type");
if (srcType == tgtType) {
change.Append (tgtType);
} else {
change.AppendModified (srcType, tgtType, true);
}
change.Append (" ");
var srcName = source.GetAttribute ("name");
var tgtName = target.GetAttribute ("name");
if (srcName == tgtName) {
change.Append (tgtName);
} else {
change.AppendModified (srcName, tgtName, true);
}
}
change.Append ("]");
}
public override bool Equals (XElement source, XElement target, ApiChanges changes)
{
if (base.Equals (source, target, changes))
return true;
XElement srcGetter, srcSetter;
XElement tgtGetter, tgtSetter;
GetAccessors (source, out srcGetter, out srcSetter);
GetAccessors (target, out tgtGetter, out tgtSetter);
List<XElement> srcIndexers = null;
List<XElement> tgtIndexers = null;
bool isIndexer = false;
if (srcGetter != null) {
srcIndexers = srcGetter.DescendantList ("parameters", "parameter");
tgtIndexers = tgtGetter.DescendantList ("parameters", "parameter");
isIndexer = srcIndexers != null && srcIndexers.Count > 0;
}
var change = new ApiChange ();
change.Header = "Modified " + GroupName;
RenderMethodAttributes (GetMethodAttributes (srcGetter, srcSetter), GetMethodAttributes (tgtGetter, tgtSetter), change);
RenderPropertyType (source, target, change);
if (isIndexer) {
RenderIndexers (srcIndexers, tgtIndexers, change);
} else {
RenderName (source, target, change);
}
RenderGenericParameters (source, target, change);
RenderAccessors (srcGetter, tgtGetter, srcSetter, tgtSetter, change);
changes.Add (source, target, change);
return false;
}
void GetProperties (XElement e, out bool @virtual, out bool @override, out bool @static, out bool getter, out bool setter, out bool family)
{
@virtual = @override = @static = getter = setter = family = false;
var methods = e.Element ("methods");
if (methods != null) {
foreach (var m in methods.Elements ("method")) {
@virtual |= m.IsTrue ("virtual");
@static |= m.IsTrue ("static");
var n = m.GetAttribute ("name");
getter |= n.StartsWith ("get_", StringComparison.Ordinal);
setter |= n.StartsWith ("set_", StringComparison.Ordinal);
var attribs = (MethodAttributes) Int32.Parse (m.GetAttribute ("attrib"));
family = ((attribs & MethodAttributes.Public) != MethodAttributes.Public);
@override |= (attribs & MethodAttributes.NewSlot) == 0;
}
}
}
public override string GetDescription (XElement e)
{
string name = e.Attribute ("name").Value;
string ptype = e.GetTypeName ("ptype");
bool virt = false;
bool over = false;
bool stat = false;
bool getter = false;
bool setter = false;
bool family = false;
GetProperties (e, out virt, out over, out stat, out getter, out setter, out family);
var sb = new StringBuilder ();
sb.Append (family ? "protected " : "public ");
if (virt && !State.IgnoreVirtualChanges)
sb.Append (over ? "override " : "virtual ");
else if (stat)
sb.Append ("static ");
sb.Append (ptype).Append (' ').Append (name).Append (" { ");
if (getter)
sb.Append ("get; ");
if (setter)
sb.Append ("set; ");
sb.Append ("}");
return sb.ToString ();
}
}
}

View File

@@ -0,0 +1,15 @@
ApiChange.cs
ApiDiff.cs
AssemblyComparer.cs
ClassComparer.cs
Comparer.cs
ConstructorComparer.cs
EventComparer.cs
FieldComparer.cs
Helpers.cs
InterfaceComparer.cs
MemberComparer.cs
MethodComparer.cs
NamespaceComparer.cs
PropertyComparer.cs
../../class/Mono.Options/Mono.Options/Options.cs