`api-merge` is a tool to support emitting *forward compatible*
binding assemblies.
Way back in the dark days before Mono for Android 4.2, a binding
assembly was only backward compatible: if Mono.Android.dll was
targeting API-8, the resulting app could run on API-8 *and later*.
The opposite was *not* true: you *might not* be able to use
Mono.Android.dll for API-10 and run it on an API-8 device.
This is *forward compatibility*, the ability for "a system" to accept
"input" intended for a later version [0].
*Java* apps were forward compatible. Mono for Android prior to 4.2
did *not* generate forward compatible apps.
The reason why was that a binding assembly directly bound what was in
the specified API level [1], so if a member "moved" in a
*backward compatible* manner -- moving
android.view.MotionEvent.getDeviceId() to the newly introduced
android.view.InputEvent base class -- it would not be
*forward compatible* for the binding assemblies we emitted, as the
e.g. API-10 binding assembly would attempt to resolve the type
android.view.InputEvent, which didn't exist in API-8, and would result
in an exception.
`api-merge` was the fix: instead of a Mono.Android.dll targeting a
*specific* API level, it would instead take the API descriptions for
the target API level *and all prior API levels*. It would thus have
enough information to track member "movements", and with a bit of
Java.Interop/tools/generator "magic" forward compatibility was
provided starting in Mono for Android 4.2 [2].
[0]: https://en.wikipedia.org/wiki/Forward_compatibility
[1]: http://lists.ximian.com/pipermail/monodroid/2011-November/007350.html
[2]: https://developer.xamarin.com/releases/android/mono_for_android_4/mono_for_android_4.2/#Improved_API_level_support
(*Now* things are starting to come together...)
One of the important infrastructural pieces of Xamarin.Android is
Mono.Android.dll, a "binding" for the Android SDK `android.jar` file.
Bindings are generated by using the `generator` tool from
Java.Interop, along with tools/jnienv-gen, and various other programs.
This in turn requires adding a git submodule for Java.Interop.
Stitch all these pieces together so that we can take an
*API description* (stored in Profiles\api-*.xml.in) and generate a
binding for that API description.
The generated files are located in
obj\$(Configuration)\android-$(ApiLevel), and the resulting
Mono.Android.dll is copied to
$(topdir)\bin\$(Configuration)\xbuild-frameworks\MonoAndroid\$(XAFrameworkVersion).
One SNAFU: currently, Mono.Android.csproj conditinally <Import/>s a
Mono.Android.projitems file generated by Java.Interop's generator
tool, which contains a list of all the generated files.
When the project is first loaded, Mono.Android.projitems will not
exist, so on that initial build, source code will be generated but
xbuild won't re-read Mono.Android.projitems (once it exists). This
will result in a failing build.
Simply rebuild the project to get a valid build, or use the `make all`
Makefile target.
- fix Android SDK Tools location.
- add SDK/NDK items for Linux.
- xbuild condition did not work and downloaded all the items. It was because
condition by metadata didn't work with the complicated expression.
So, split filtering and transformation into different tasks.
- xbuild aborts when the specified metadata %(HostOS) does not exist, so
specify dummy HostOS metadata (which is ugly but not too annoying).
(Linux build verification is still ongoing, not assuring that this fixes
all the possible existing issues.)
Add a new build-tools/mono-runtimes project to build the required Mono
runtimes for Xamarin.Android app execution on Android devices.
Xamarin.Android 6.0 ("cycle 6") includes Mono runtimes compiled for
five architectures:
* armeabi (armv5)
* armeabi-v7a (armv7)
* arm64-v8a (aarch64)
* x86
* x86_64
An additional "host" architecture is also built to build the
"monodroid" profile assemblies such as mscorlib.dll.
In the interest of expediency and not requiring that *six* different
runtimes be built -- which can greatly increase build times and
storage requirements -- the initial xamarin-android open-source build
will only build the armeabi-v7a (armv7) and "host" architectures.
Additionally, the <UnzipDirectoryChildren/> task needed to be altered
to use unzip(1) on OS X instead of using
System.IO.Compression.ZipFile, because the ZipFile implementation
included with Mono 4.4 doesn't respect file attributes such as the +x,
causing all files to be extracted as 644 (rw-r--r--), resulting
in an NDK toolchain which wouldn't work. To resolve this issue,
execution on OS X and Linux now uses unzip(1).
~~ Adding new architectures ~~
To build Mono for a new architecture, edit
build-tools/mono-runtimes/mono-runtimes.projitems and add a new
@(_MonoRuntime) entry for the desired architecture.
TODO: Figure out if there's a reasonable way to "opt-in" or "opt-out"
of CPU architectures so that it isn't an "all or nothing" prospect.
This might be a suspect idea, but lets see if we can make this work.
[The Joel Test: 12 Steps to Better Code][0] outlines 12 steps
to better code. The first two steps are:
1. Do you use source control?
2. Can you make a build in one step?
github is being used for source control, so (1) is handled, but how
simple can we make (2)? How easy can we make it to build
Xamarin.Android upon a fresh checkout?
The ideal to strive for is simple:
Load Xamarin.Android.sln into your IDE and Build the project.
I *know* we're not going to be able to do this, if only because we're
going to be using git submodules, which will require a separate
`git submodule init` invocation [1].
Knowing we can't reach that level of simplicitly doesn't mean we
shouldn't *try* to reach it for all other parts of the build system.
Which brings us to the Android NDK and SDK. The Android NDK will be
required in order to build native code, such as libmonodroid.so, while
the Android SDK will be required in order to compile
Java Callable Wrappers (née Android Callable Wrappers [2]) and
eventual samples and unit tests.
There are three ways we can deal with the Android NDK and SDK:
1. Complicate the "build" process by requiring that developers go to
the Android SDK Download Page [3], download and install
"somewhere" the required bits, and then configure the
Xamarin.Android build to use these bits.
2. Complicate the "build" process by requiring that developers run
the Xamarin Unified Installer [4], let it install everything
required, then configure the Xamarin.Android build to use those
bits.
3. Painstakingly determine which files are actually required, then
automatically download and extract those files into a "well-known"
location known by the Xamarin.Android build process.
(1) and (2) can be infuriating. Let's give (3) a try. :-)
Add a Xamarin.Android.Tools.BootstrapTasks project which contains
MSBuild tasks to facilitate downloading the Android SDK and NDK files.
Add an android-toolchain project which uses
Xamarin.Android.Tools.BootstrapTasks to download a painstakingly
determined set of files and install them "somewhere".
Unfortunately [5] the "somewhere" to download and install these files
needs to be in a known absolute path, so I've arbitrary decided to
download the files into $(HOME)\android-archives and install them into
$(HOME)\android-toolchain. On windows, this is
%HOMEDRIVE%%HOMEPATH%\android-archives and
%HOMEDRIVE%%HOMEPATH%\android-toolchain.
These locations may be modified by creating a
Configuration.Override.props file; see README.md for details.
TL;DR: This setup is able to magically download the Android NDK and
SDK files and install them for later use in a reasonably overridable
location, all within MSBuild.
[0]: http://www.joelonsoftware.com/articles/fog0000000043.html
[1]: Though maybe there's some MSBuild-fu we can use to address that.
[2]: https://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/android_callable_wrappers/
[3]: http://developer.android.com/sdk/index.html
[4]: https://www.xamarin.com/download
[5]: Because I couldn't find a reliable way to use $(SolutionDir) when
only building a project, and relative paths would require an
in-tree installation location, which might not work.