**The Scenario**: `mono-api-info` and `mono-api-html`, together, are a
useful way to track API changes over time, specifically to check for
accidental API breakage:
# Have a "known good" assembly version;
# keep reference/assembly.xml in version control
$ mono-api-info assembly.dll > reference/assembly.xml
# ...then on every new build, check for breakage:
$ mono-api-info assembly.dll > new-assembly.xml
$ mono-api-html reference/assembly.xml new-assembly.xml > assembly-diff.html
$ if grep '<p>Removed' assembly-diff.html > /dev/null ; then \
echo "ABI BREAK IN assembly.dll" ; \
cat assembly-diff.html ; \
fi
This is a very nice workflow, and has been used in Xamarin.Android to
track accidental API breakage for some time.
**The problem** with the above workflow are *implicit* references:
`mono-api-info` will happily look for dependencies in the same
directory as `assembly`, but it will *also* look for assembly
references in various system-specific directories, which is fine...
...until it *isn't* fine, e.g. if you have your own version of
mscorlib.dll that you need to use, *not* the system one, as your own
version may have API differences from the system mscorlib.dll which
are "percolated" into `mono-api-info` output. For example, on
"desktop" profiles, System.Attribute implements
System.Runtime.InteropServices._Attribute, and thus *all* custom
attribute types will have _Attribute listed as an implemented
interface. The Xamarin.iOS and Xamarin.Android mscorlib.dll,
meanwhile, does *not* have System.Attribute implementing _Attribute.
Consequently, changing the mscorlib.dll which is used while processing
an assembly will change `mono-api-info` output, resulting in "API
breakage" which doens't actually exist, because the wrong mscorlib.dll
was used to generate the original API description in the first place.
We need a way to control which mscorlib.dll/etc. are used. This can be
done by allowing specification of the assembly search directories, so
that we can "override" the default system-specific directory location.
**The solution**: Add a `mono-api-info -L PATH` option which adds PATH
to the list of directories that `mono-api-info` will search when
resolving assembly references. This allows "overriding" the system
directory location, as PATH will be searched for mscorlib.dll before
the system directory location.
Additionally, add a `mono-api-info -r ASSEMBLY` option, which will
pre-process ASSEMBLY (and all dependencies) and add the directory
which contains ASSEMBLY to the assembly search paths.
These two options are conceptually similar to `mcs -L` and `mcs -r`.
Finally, while adding support for these options, add support for
`mono-api-info --help`, so that we don't need to use only the man page
to get option information.
-mono-api-info.exe: $(APIINFO_SOURCES)
+mono-api-info.exe: $(APIINFO_SOURCES) ../../class/Mono.Options/Mono.Options/Options.cs
$(CSCOMPILE) -r:Mono.Cecil.dll -out:$@ $^
$(CSCOMPILE) -r:Mono.Cecil.dll -out:$@ $^
var acoll = new AssemblyCollection ();
var options = new Mono.Options.OptionSet {
var acoll = new AssemblyCollection ();
var options = new Mono.Options.OptionSet {
- { "h|help", "Show this help", v => showHelp = true },
- { "abi", _ => AbiMode = true},
- { "f|follow-forwarders", _ => FollowForwarders = true },
- { "d|search-directory=", v => TypeHelper.Resolver.AddSearchDirectory (v) },
+ "usage: mono-api-info [OPTIONS+] ASSEMBLY+",
+ "",
+ "Expose IL structure of CLR assemblies as XML.",
+ "",
+ "Available Options:",
+ { "abi",
+ "Generate ABI, not API; contains only classes with instance fields which are not [NonSerialized].",
+ v => AbiMode = v != null },
+ { "f|follow-forwarders",
+ "Follow type forwarders.",
+ v => FollowForwarders = v != null },
+ { "d|L|lib|search-directory=",
+ "Check for assembly references in {DIRECTORY}.",
+ v => TypeHelper.Resolver.AddSearchDirectory (v) },
+ { "r=",
+ "Read and register the file {ASSEMBLY}, and add the directory containing ASSEMBLY to the search path.",
+ v => TypeHelper.Resolver.ResolveFile (v) },
+ { "h|?|help",
+ "Show this message and exit.",
+ v => showHelp = v != null },
};
var asms = options.Parse (args);
if (showHelp || asms.Count == 0) {
};
var asms = options.Parse (args);
if (showHelp || asms.Count == 0) {
- Console.WriteLine (@"Usage: mono-api-info [options] <assemblies>");
- Console.WriteLine ();
- Console.WriteLine ("Available options:");
options.WriteOptionDescriptions (Console.Out);
Console.WriteLine ();
return showHelp? 0 :1;
options.WriteOptionDescriptions (Console.Out);
Console.WriteLine ();
return showHelp? 0 :1;